mirror of
https://github.com/community-scripts/ProxmoxVE.git
synced 2026-11-03 17:15:22 +01:00
Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| a5e2a50cd6 |
+2
-18
@@ -58,9 +58,9 @@ body:
|
||||
|
||||
Before continuing, verify that the requested project meets the minimum requirements below.
|
||||
|
||||
**Projects with fewer than 1,000 stars, or younger than 6 months, are generally not eligible for a script request.**
|
||||
**Projects with fewer than 1,000 stars are generally not eligible for a script request.**
|
||||
|
||||
An automated check verifies the star count and project age for GitHub-hosted projects. If the project doesn't meet these requirements, fill in **Justification for an exception** below — without one, the request is closed automatically. With one, it's flagged for manual review instead. Exceptions are rare and require a clearly verifiable, significant public adoption signal.
|
||||
Exceptions are only considered where there is a clearly verifiable, significant public adoption signal.
|
||||
|
||||
- type: input
|
||||
id: repository-stars
|
||||
@@ -134,22 +134,6 @@ body:
|
||||
- label: I understand that requests outside the project scope may be closed without an extensive explanation.
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
id: justification
|
||||
attributes:
|
||||
label: Justification for an exception
|
||||
description: >-
|
||||
Only needed if the project has fewer than 1,000 stars or is younger than 6 months (e.g. it has no GitHub
|
||||
presence, so stars don't apply). Explain the significant public adoption signal. Leave blank if the project
|
||||
meets the normal requirements — requests that don't meet the requirements and have no justification here are
|
||||
closed automatically.
|
||||
placeholder: |
|
||||
Example:
|
||||
No public GitHub repository, but the project has 50k+ active installs reported by its own telemetry
|
||||
dashboard and is covered by several established self-hosting publications.
|
||||
validations:
|
||||
required: false
|
||||
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
|
||||
Generated
+10
-10
@@ -17,6 +17,16 @@ Fixes #
|
||||
**Tested on:** <!-- e.g. PVE 9.2 / Debian 13 / fresh install + update. Write "not tested" if you could not run it. -->
|
||||
|
||||
|
||||
---
|
||||
|
||||
## 🤖 AI Assistance (**X** in brackets)
|
||||
|
||||
> If you used an AI tool (GitHub Copilot, Claude, ChatGPT, etc.) to write or generate any scripts in this PR, you **must** confirm compliance below.
|
||||
> Select exactly one option.
|
||||
|
||||
- [ ] **No AI used** – Scripts were written without AI assistance.
|
||||
- [ ] **AI was used** – I confirm the scripts were built using [`AGENTS.md`](https://github.com/community-scripts/DevScripts/blob/main/AGENTS.md) and [`.github/agents/pve-script-creator.agent.md`](https://github.com/community-scripts/DevScripts/blob/main/.github/agents/pve-script-creator.agent.md) as guidance, and the output has been reviewed and corrected to match those guidelines.
|
||||
|
||||
---
|
||||
|
||||
## 🛠️ Type of Change (**X** in brackets)
|
||||
@@ -60,13 +70,3 @@ Guidance:
|
||||
-->
|
||||
|
||||
<!-- The advisory block is only active once it is OUTSIDE this comment. -->
|
||||
|
||||
---
|
||||
|
||||
## 🤖 AI Assistance (**X** in brackets)
|
||||
|
||||
> If you used an AI tool (GitHub Copilot, Claude, ChatGPT, etc.) to write or generate any scripts in this PR, you **must** confirm compliance below.
|
||||
> Select exactly one option.
|
||||
|
||||
- [ ] **No AI used** – Scripts were written without AI assistance.
|
||||
- [ ] **AI was used** – I confirm the scripts were built using [`AGENTS.md`](https://github.com/community-scripts/DevScripts/blob/main/AGENTS.md) and [`.github/agents/pve-script-creator.agent.md`](https://github.com/community-scripts/DevScripts/blob/main/.github/agents/pve-script-creator.agent.md) as guidance, and the output has been reviewed and corrected to match those guidelines.
|
||||
|
||||
+40
-44
@@ -1,4 +1,4 @@
|
||||
name: Flag PRs Missing Template
|
||||
name: Close PRs Missing Template
|
||||
|
||||
on:
|
||||
pull_request_target:
|
||||
@@ -14,12 +14,10 @@ jobs:
|
||||
issues: write
|
||||
contents: read
|
||||
steps:
|
||||
- name: Check PR against the template, comment and label instead of closing
|
||||
- name: Close PR if it does not follow the PR template
|
||||
uses: actions/github-script@v9
|
||||
with:
|
||||
script: |
|
||||
const MARKER = "<!-- pr-template-check -->";
|
||||
const LABELS = ["missing pr template", "stale"];
|
||||
const pr = context.payload.pull_request;
|
||||
const prNumber = pr.number;
|
||||
const author = pr.user.login;
|
||||
@@ -46,11 +44,11 @@ jobs:
|
||||
return;
|
||||
}
|
||||
|
||||
const prLabels = pr.labels.map((label) => label.name);
|
||||
const labels = pr.labels.map((label) => label.name);
|
||||
const skipLabels = ["automated pr", "keep-open"];
|
||||
|
||||
if (skipLabels.some((label) => prLabels.includes(label))) {
|
||||
core.info(`PR #${prNumber} has a skip label (${prLabels.join(", ")}) — skipping template validation.`);
|
||||
if (skipLabels.some((label) => labels.includes(label))) {
|
||||
core.info(`PR #${prNumber} has a skip label (${labels.join(", ")}) — skipping template validation.`);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -116,52 +114,50 @@ jobs:
|
||||
failures.push("At least one **Type of Change** checkbox must be checked.");
|
||||
}
|
||||
|
||||
const comments = await github.paginate(github.rest.issues.listComments, {
|
||||
owner, repo, issue_number: prNumber, per_page: 100
|
||||
});
|
||||
const mine = comments.find(c => c.body && c.body.includes(MARKER));
|
||||
|
||||
if (failures.length === 0) {
|
||||
core.info(`PR #${prNumber} follows the PR template.`);
|
||||
if (mine && !mine.body.startsWith(MARKER + "\n✅")) {
|
||||
await github.rest.issues.updateComment({
|
||||
owner, repo, comment_id: mine.id,
|
||||
body: MARKER + "\n✅ **Template check passed.** Thanks for fixing the description — the `stale` mark has been removed.",
|
||||
});
|
||||
}
|
||||
for (const name of LABELS) {
|
||||
if (prLabels.includes(name)) {
|
||||
await github.rest.issues.removeLabel({ owner, repo, issue_number: prNumber, name });
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
core.info(`PR #${prNumber} — template validation failed, flagging instead of closing.`);
|
||||
core.info(`Closing PR #${prNumber} — template validation failed.`);
|
||||
|
||||
const templateUrl =
|
||||
"https://github.com/community-scripts/ProxmoxVE/blob/main/.github/pull_request_template.md";
|
||||
const failureList = failures.map((item) => `- ${item}`).join("\n");
|
||||
|
||||
const text =
|
||||
MARKER + "\n" +
|
||||
`👋 Hi @${author},\n\n` +
|
||||
`This pull request does not follow the [PR template](${templateUrl}) yet:\n\n` +
|
||||
failureList + "\n\n" +
|
||||
`**Edit the PR description** to fix it — no new PR needed. This check runs again on every edit and clears itself once everything is in place.\n\n` +
|
||||
`Until then the PR is marked \`stale\` and will be closed in **7 days**.\n\n` +
|
||||
`Maintainers can add the \`keep-open\` label to exempt a PR from this check.\n\n` +
|
||||
`Thank you for contributing! 🙏`;
|
||||
const comment = [
|
||||
`👋 Hi @${author},`,
|
||||
``,
|
||||
`This pull request was closed because it does not follow the [PR template](${templateUrl}).`,
|
||||
``,
|
||||
`Please fix the following and open a new PR (or reopen this one after updating the description):`,
|
||||
``,
|
||||
failureList,
|
||||
``,
|
||||
`> Use the template sections, fill in the description, check all prerequisite boxes, and select at least one type of change.`,
|
||||
``,
|
||||
`Maintainers can add the \`keep-open\` label to exempt a PR from this check.`,
|
||||
``,
|
||||
`Thank you for contributing! 🙏`,
|
||||
].join("\n");
|
||||
|
||||
if (mine) {
|
||||
if (mine.body !== text) {
|
||||
await github.rest.issues.updateComment({ owner, repo, comment_id: mine.id, body: text });
|
||||
}
|
||||
} else {
|
||||
await github.rest.issues.createComment({ owner, repo, issue_number: prNumber, body: text });
|
||||
}
|
||||
await github.rest.issues.createComment({
|
||||
owner,
|
||||
repo,
|
||||
issue_number: prNumber,
|
||||
body: comment,
|
||||
});
|
||||
|
||||
const toAdd = LABELS.filter(name => !prLabels.includes(name));
|
||||
if (toAdd.length > 0) {
|
||||
await github.rest.issues.addLabels({ owner, repo, issue_number: prNumber, labels: toAdd });
|
||||
}
|
||||
await github.rest.pulls.update({
|
||||
owner,
|
||||
repo,
|
||||
pull_number: prNumber,
|
||||
state: "closed",
|
||||
});
|
||||
|
||||
await github.rest.issues.addLabels({
|
||||
owner,
|
||||
repo,
|
||||
issue_number: prNumber,
|
||||
labels: ["missing pr template"],
|
||||
});
|
||||
|
||||
-141
@@ -1,141 +0,0 @@
|
||||
name: Request Script Eligibility Check
|
||||
|
||||
# category.slug is the same string GitHub uses in the "new discussion" URL
|
||||
# (?category=request-script, linked from every repo's issue chooser), so this
|
||||
# guard matches the live category without needing its numeric id.
|
||||
on:
|
||||
discussion:
|
||||
types: [created, edited, reopened]
|
||||
|
||||
jobs:
|
||||
check-eligibility:
|
||||
if: github.repository == 'community-scripts/ProxmoxVE' && github.event.discussion.category.slug == 'request-script'
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
discussions: write
|
||||
issues: write
|
||||
contents: read
|
||||
steps:
|
||||
- name: Verify stars/age, flag or close
|
||||
uses: actions/github-script@v9
|
||||
with:
|
||||
script: |
|
||||
const owner = context.repo.owner;
|
||||
const repo = context.repo.repo;
|
||||
const discussion = context.payload.discussion;
|
||||
|
||||
if (!discussion || discussion.state === "closed") {
|
||||
core.info("Discussion missing or already closed — skipping.");
|
||||
return;
|
||||
}
|
||||
|
||||
const body = discussion.body || "";
|
||||
const lines = body.split("\n");
|
||||
const fields = {};
|
||||
let current = null;
|
||||
for (const line of lines) {
|
||||
const m = line.match(/^###\s+(.+?)\s*$/);
|
||||
if (m) {
|
||||
current = m[1].trim();
|
||||
fields[current] = [];
|
||||
} else if (current) {
|
||||
fields[current].push(line);
|
||||
}
|
||||
}
|
||||
function fieldText(label) {
|
||||
const raw = (fields[label] || []).join("\n").trim();
|
||||
return raw === "_No response_" ? "" : raw;
|
||||
}
|
||||
|
||||
const sourceRepo = fieldText("Source Repository");
|
||||
const justification = fieldText("Justification for an exception");
|
||||
|
||||
let eligible = false;
|
||||
let verified = false;
|
||||
const ghMatch = sourceRepo.match(/^https?:\/\/(?:www\.)?github\.com\/([^\/\s]+)\/([^\/\s#?]+?)(?:\.git)?\/?(?:[#?].*)?$/i);
|
||||
|
||||
if (ghMatch) {
|
||||
const [, repoOwner, repoName] = ghMatch;
|
||||
try {
|
||||
const { data: repoData } = await github.rest.repos.get({ owner: repoOwner, repo: repoName });
|
||||
const stars = repoData.stargazers_count;
|
||||
const createdAt = new Date(repoData.created_at);
|
||||
const now = new Date();
|
||||
const ageMonths =
|
||||
(now.getFullYear() - createdAt.getFullYear()) * 12 +
|
||||
(now.getMonth() - createdAt.getMonth()) -
|
||||
(now.getDate() < createdAt.getDate() ? 1 : 0);
|
||||
eligible = stars >= 1000 && ageMonths >= 6;
|
||||
verified = true;
|
||||
core.info(`Verified ${repoOwner}/${repoName}: ${stars} stars, ${ageMonths} months old, eligible=${eligible}`);
|
||||
} catch (err) {
|
||||
core.warning(`Could not verify ${repoOwner}/${repoName}: ${err.message} — treating as unverifiable.`);
|
||||
}
|
||||
} else {
|
||||
core.info("Source Repository is not a github.com URL — cannot auto-verify, treating as unverifiable.");
|
||||
}
|
||||
|
||||
if (eligible) {
|
||||
core.info(`Discussion #${discussion.number} meets the requirements — no action.`);
|
||||
return;
|
||||
}
|
||||
|
||||
const labels = (discussion.labels || []).map(l => l.name);
|
||||
const reasonLine = verified
|
||||
? "the source repository does not meet the 1,000-star / 6-month minimum adoption requirement"
|
||||
: "the source repository's star count and age could not be verified automatically";
|
||||
|
||||
if (justification) {
|
||||
if (labels.includes("investigation")) {
|
||||
core.info(`Discussion #${discussion.number} already flagged for investigation — skipping.`);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
await github.rest.issues.createLabel({
|
||||
owner, repo, name: "investigation",
|
||||
color: "fbca04",
|
||||
description: "Below the normal script-request threshold; a justification was given and needs maintainer review.",
|
||||
});
|
||||
} catch (err) {
|
||||
if (err.status !== 422) throw err;
|
||||
}
|
||||
|
||||
const labelQuery = await github.graphql(
|
||||
`query($owner: String!, $repo: String!) { repository(owner: $owner, name: $repo) { label(name: "investigation") { id } } }`,
|
||||
{ owner, repo }
|
||||
);
|
||||
const labelNodeId = labelQuery.repository.label.id;
|
||||
|
||||
await github.graphql(
|
||||
`mutation($labelableId: ID!, $labelIds: [ID!]!) { addLabelsToLabelable(input: { labelableId: $labelableId, labelIds: $labelIds }) { clientMutationId } }`,
|
||||
{ labelableId: discussion.node_id, labelIds: [labelNodeId] }
|
||||
);
|
||||
|
||||
await github.graphql(
|
||||
`mutation($discussionId: ID!, $body: String!) { addDiscussionComment(input: { discussionId: $discussionId, body: $body }) { comment { id } } }`,
|
||||
{
|
||||
discussionId: discussion.node_id,
|
||||
body:
|
||||
`Thanks for the justification — ${reasonLine}, so this has been flagged \`investigation\` for a maintainer ` +
|
||||
`to review manually rather than closed automatically.`,
|
||||
}
|
||||
);
|
||||
} else {
|
||||
await github.graphql(
|
||||
`mutation($discussionId: ID!, $body: String!) { addDiscussionComment(input: { discussionId: $discussionId, body: $body }) { comment { id } } }`,
|
||||
{
|
||||
discussionId: discussion.node_id,
|
||||
body:
|
||||
`This request doesn't meet the minimum requirements (1,000 stars, project at least 6 months old) — ` +
|
||||
`${reasonLine}, and no justification was given. Closing automatically.\n\n` +
|
||||
`If you believe this project qualifies for an exception, edit this discussion to fill in ` +
|
||||
`**Justification for an exception** and reopen it — it will then be flagged for manual review instead of closed again.`,
|
||||
}
|
||||
);
|
||||
|
||||
await github.graphql(
|
||||
`mutation($discussionId: ID!) { closeDiscussion(input: { discussionId: $discussionId, reason: OUTDATED }) { discussion { id } } }`,
|
||||
{ discussionId: discussion.node_id }
|
||||
);
|
||||
}
|
||||
Generated
+73
-95
@@ -22,112 +22,90 @@ jobs:
|
||||
const now = new Date();
|
||||
const owner = context.repo.owner;
|
||||
const repo = context.repo.repo;
|
||||
const TEMPLATE_LABEL = "missing pr template";
|
||||
const MAINTAINERS = ["OWNER", "MEMBER", "COLLABORATOR"];
|
||||
const days = d => (now - d) / (1000 * 60 * 60 * 24);
|
||||
const staleNotice = author =>
|
||||
`@${author} This PR has been marked as stale. It will be closed if no new commits are added in 7 days.`;
|
||||
|
||||
// Only fires for a maintainer's label: labels set with GITHUB_TOKEN raise no event.
|
||||
|
||||
// --- When stale label is added, comment immediately ---
|
||||
if (context.eventName === "pull_request_target" && context.payload.action === "labeled") {
|
||||
const pr = context.payload.pull_request;
|
||||
const labels = (pr.labels || []).map(l => l.name);
|
||||
if (context.payload.label?.name === "stale" && !labels.includes(TEMPLATE_LABEL)) {
|
||||
const label = context.payload.label?.name;
|
||||
if (label === "stale") {
|
||||
const author = context.payload.pull_request.user.login;
|
||||
await github.rest.issues.createComment({
|
||||
owner, repo, issue_number: pr.number, body: staleNotice(pr.user.login)
|
||||
owner,
|
||||
repo,
|
||||
issue_number: context.payload.pull_request.number,
|
||||
body: `@${author} This PR has been marked as stale. It will be closed if no new commits are added in 7 days.`
|
||||
});
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
const prs = await github.paginate(github.rest.pulls.list, { owner, repo, state: "open", per_page: 100 });
|
||||
|
||||
|
||||
// --- Scheduled run: check all stale PRs ---
|
||||
const { data: prs } = await github.rest.pulls.list({
|
||||
owner,
|
||||
repo,
|
||||
state: "open",
|
||||
per_page: 100
|
||||
});
|
||||
|
||||
for (const pr of prs) {
|
||||
const labels = pr.labels.map(l => l.name);
|
||||
const author = pr.user.login;
|
||||
const hasStale = labels.includes("stale");
|
||||
|
||||
if (labels.includes("keep-open")) {
|
||||
if (hasStale) {
|
||||
await github.rest.issues.removeLabel({ owner, repo, issue_number: pr.number, name: "stale" });
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
const comments = await github.paginate(github.rest.issues.listComments, {
|
||||
owner, repo, issue_number: pr.number, per_page: 100
|
||||
const hasStale = pr.labels.some(l => l.name === "stale");
|
||||
if (!hasStale) continue;
|
||||
|
||||
// Get timeline events to find when stale label was added
|
||||
const { data: events } = await github.rest.issues.listEvents({
|
||||
owner,
|
||||
repo,
|
||||
issue_number: pr.number,
|
||||
per_page: 100
|
||||
});
|
||||
const commits = await github.paginate(github.rest.pulls.listCommits, {
|
||||
owner, repo, pull_number: pr.number, per_page: 100
|
||||
});
|
||||
const lastCommitDate = commits.length > 0
|
||||
? new Date(commits[commits.length - 1].commit.author.date)
|
||||
: new Date(pr.created_at);
|
||||
const humanComments = comments.filter(c => c.user?.type !== "Bot");
|
||||
|
||||
// -------------------------------------------------------
|
||||
// Auto-label PRs with no activity in the last 14 days,
|
||||
// but ONLY if someone other than the author has engaged.
|
||||
// -------------------------------------------------------
|
||||
if (!hasStale) {
|
||||
const reviews = await github.paginate(github.rest.pulls.listReviews, {
|
||||
owner, repo, pull_number: pr.number, per_page: 100
|
||||
});
|
||||
const engaged =
|
||||
reviews.some(r => r.user?.type !== "Bot" && r.user?.login !== author) ||
|
||||
humanComments.some(c => c.user?.login !== author);
|
||||
if (!engaged) continue;
|
||||
|
||||
const lastCommentDate = humanComments.length > 0
|
||||
? new Date(humanComments[humanComments.length - 1].created_at)
|
||||
: null;
|
||||
const lastActivityDate = lastCommentDate && lastCommentDate > lastCommitDate
|
||||
? lastCommentDate
|
||||
: lastCommitDate;
|
||||
|
||||
if (days(lastActivityDate) > 14) {
|
||||
await github.rest.issues.addLabels({ owner, repo, issue_number: pr.number, labels: ["stale"] });
|
||||
await github.rest.issues.createComment({ owner, repo, issue_number: pr.number, body: staleNotice(author) });
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
// -------------------------------------------------------
|
||||
// Manage already-stale PRs
|
||||
// -------------------------------------------------------
|
||||
const events = await github.paginate(github.rest.issues.listEvents, {
|
||||
owner, repo, issue_number: pr.number, per_page: 100
|
||||
});
|
||||
const staleDates = events
|
||||
|
||||
// Find the most recent time the stale label was added
|
||||
const staleLabelEvents = events
|
||||
.filter(e => e.event === "labeled" && e.label?.name === "stale")
|
||||
.map(e => new Date(e.created_at));
|
||||
if (staleDates.length === 0) continue;
|
||||
const staleLabelDate = new Date(Math.max(...staleDates));
|
||||
const isTemplateIssue = labels.includes(TEMPLATE_LABEL);
|
||||
|
||||
// A push ends an inactivity mark; a template mark only ends via close-invalid-pr-template.yml's re-check.
|
||||
if (!isTemplateIssue && lastCommitDate > staleLabelDate) {
|
||||
await github.rest.issues.removeLabel({ owner, repo, issue_number: pr.number, name: "stale" });
|
||||
.sort((a, b) => new Date(b.created_at) - new Date(a.created_at));
|
||||
|
||||
if (staleLabelEvents.length === 0) continue;
|
||||
|
||||
const staleLabelDate = new Date(staleLabelEvents[0].created_at);
|
||||
const daysSinceStale = (now - staleLabelDate) / (1000 * 60 * 60 * 24);
|
||||
|
||||
// Check for new commits since stale label was added
|
||||
const { data: commits } = await github.rest.pulls.listCommits({
|
||||
owner,
|
||||
repo,
|
||||
pull_number: pr.number
|
||||
});
|
||||
|
||||
const lastCommitDate = new Date(commits[commits.length - 1].commit.author.date);
|
||||
const author = pr.user.login;
|
||||
|
||||
// If there are new commits after the stale label, remove it
|
||||
if (lastCommitDate > staleLabelDate) {
|
||||
await github.rest.issues.removeLabel({
|
||||
owner,
|
||||
repo,
|
||||
issue_number: pr.number,
|
||||
name: "stale"
|
||||
});
|
||||
await github.rest.issues.createComment({
|
||||
owner, repo, issue_number: pr.number,
|
||||
owner,
|
||||
repo,
|
||||
issue_number: pr.number,
|
||||
body: `@${author} Recent activity detected. Removing stale label.`
|
||||
});
|
||||
continue;
|
||||
}
|
||||
|
||||
// A maintainer comment after the mark restarts the 7 days.
|
||||
const maintainerDates = humanComments
|
||||
.filter(c => MAINTAINERS.includes(c.author_association) && c.user?.login !== author)
|
||||
.map(c => new Date(c.created_at))
|
||||
.filter(d => d > staleLabelDate);
|
||||
const clockStart = maintainerDates.length > 0 ? new Date(Math.max(...maintainerDates)) : staleLabelDate;
|
||||
if (days(clockStart) <= 7) continue;
|
||||
|
||||
await github.rest.issues.createComment({
|
||||
owner, repo, issue_number: pr.number,
|
||||
body: isTemplateIssue
|
||||
? `@${author} Closing: the PR template requirements listed above were still missing 7 days later. Once they are in place, please open a new pull request.`
|
||||
: `@${author} Closing stale PR due to inactivity (no commits for 7 days after stale label).`
|
||||
});
|
||||
await github.rest.pulls.update({ owner, repo, pull_number: pr.number, state: "closed" });
|
||||
// If 7 days have passed since stale label, close the PR
|
||||
else if (daysSinceStale > 7) {
|
||||
await github.rest.pulls.update({
|
||||
owner,
|
||||
repo,
|
||||
pull_number: pr.number,
|
||||
state: "closed"
|
||||
});
|
||||
await github.rest.issues.createComment({
|
||||
owner,
|
||||
repo,
|
||||
issue_number: pr.number,
|
||||
body: `@${author} Closing stale PR due to inactivity (no commits for 7 days after stale label).`
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
+1
-47
@@ -577,63 +577,17 @@ Exercise vigilance regarding copycat or coat-tailing sites that seek to exploit
|
||||
|
||||
- #### 🐞 Bug Fixes
|
||||
|
||||
- Fedora VM: let the engine install libguestfs-tools [@MickLesk](https://github.com/MickLesk) ([#18422](https://github.com/community-scripts/ProxmoxVE/pull/18422))
|
||||
- Cinephage: run the camoufox binary the package now ships [@MickLesk](https://github.com/MickLesk) ([#18426](https://github.com/community-scripts/ProxmoxVE/pull/18426))
|
||||
- AlmaLinux VM: report failures through the engine's error handler [@MickLesk](https://github.com/MickLesk) ([#18418](https://github.com/community-scripts/ProxmoxVE/pull/18418))
|
||||
- WireGuard: give the Debian container 1 GB for WGDashboard [@MickLesk](https://github.com/MickLesk) ([#18419](https://github.com/community-scripts/ProxmoxVE/pull/18419))
|
||||
- zabbix: use os specific apt packages [@MickLesk](https://github.com/MickLesk) ([#18417](https://github.com/community-scripts/ProxmoxVE/pull/18417))
|
||||
- Discourse: build libvips 8.18 for its image worker [@MickLesk](https://github.com/MickLesk) ([#18430](https://github.com/community-scripts/ProxmoxVE/pull/18430))
|
||||
- Wazuh: show the installer's own error when it fails [@MickLesk](https://github.com/MickLesk) ([#18428](https://github.com/community-scripts/ProxmoxVE/pull/18428))
|
||||
- Tracearr: move to Node.js 26 [@connorgallopo](https://github.com/connorgallopo) ([#18393](https://github.com/community-scripts/ProxmoxVE/pull/18393))
|
||||
|
||||
- #### ✨ New Features
|
||||
|
||||
- Wiki.js: install 3.x [@MickLesk](https://github.com/MickLesk) ([#18382](https://github.com/community-scripts/ProxmoxVE/pull/18382))
|
||||
|
||||
- #### 🔧 Refactor
|
||||
|
||||
- OpenWrt VM: read the stable release from downloads.openwrt.org [@MickLesk](https://github.com/MickLesk) ([#18411](https://github.com/community-scripts/ProxmoxVE/pull/18411))
|
||||
- Immich: fetch VectorChord from its new repo, skip Java, get GeoNames first [@MickLesk](https://github.com/MickLesk) ([#18397](https://github.com/community-scripts/ProxmoxVE/pull/18397))
|
||||
|
||||
### 💾 Core
|
||||
|
||||
- fix: put the reason for a VM failure into its trace [@MickLesk](https://github.com/MickLesk) ([core#200](https://github.com/community-scripts/core/pull/200))
|
||||
- fix: check the bridge before creating a container, and its state after start [@MickLesk](https://github.com/MickLesk) ([core#199](https://github.com/community-scripts/core/pull/199))
|
||||
- ref: offer to remove only the container this run created [@MickLesk](https://github.com/MickLesk) ([core#198](https://github.com/community-scripts/core/pull/198))
|
||||
- feat: report a cancelled tool dialog as an abort and name the failing command [@MickLesk](https://github.com/MickLesk) ([core#197](https://github.com/community-scripts/core/pull/197))
|
||||
- feat: check KVM and the host's threads before building a VM [@MickLesk](https://github.com/MickLesk) ([core#196](https://github.com/community-scripts/core/pull/196))
|
||||
- ref: give a host tool one log file for the whole run [@MickLesk](https://github.com/MickLesk) ([core#194](https://github.com/community-scripts/core/pull/194))
|
||||
- fix: keep a piped stdin when output moves to the log after a hangup [@MickLesk](https://github.com/MickLesk) ([core#193](https://github.com/community-scripts/core/pull/193))
|
||||
- fix: pve-LXC: say when the container went away during its setup [@MickLesk](https://github.com/MickLesk) ([core#192](https://github.com/community-scripts/core/pull/192))
|
||||
- fix: pve-LXC: say when the container went away during its setup [@MickLesk](https://github.com/MickLesk) ([core#192](https://github.com/community-scripts/core/pull/192))
|
||||
- fix: treat Esc in a VM wizard dialog as leaving the script [@MickLesk](https://github.com/MickLesk) ([core#190](https://github.com/community-scripts/core/pull/190))
|
||||
- ref: count a container stopped during a detached install as an abort [@MickLesk](https://github.com/MickLesk) ([core#187](https://github.com/community-scripts/core/pull/187))
|
||||
- feat: find the assets of a renamed GitHub repo without the API [@MickLesk](https://github.com/MickLesk) ([core#188](https://github.com/community-scripts/core/pull/188))
|
||||
- fix: restore npm when an interrupted update left it behind [@MickLesk](https://github.com/MickLesk) ([core#189](https://github.com/community-scripts/core/pull/189))
|
||||
- feat: add var_cmode to set the container console mode [@MickLesk](https://github.com/MickLesk) ([core#180](https://github.com/community-scripts/core/pull/180))
|
||||
|
||||
### 🧰 Tools
|
||||
|
||||
- #### 🐞 Bug Fixes
|
||||
|
||||
- all-templates: answering No or Cancel is not a failure [@MickLesk](https://github.com/MickLesk) ([#18423](https://github.com/community-scripts/ProxmoxVE/pull/18423))
|
||||
- homebrew: skip service accounts when picking the user, survive a failed apt update [@MickLesk](https://github.com/MickLesk) ([#18424](https://github.com/community-scripts/ProxmoxVE/pull/18424))
|
||||
- add-tailscale-lxc: start a stopped container and check its OS first [@MickLesk](https://github.com/MickLesk) ([#18421](https://github.com/community-scripts/ProxmoxVE/pull/18421))
|
||||
- netdata: survive a failed apt update, leave quietly on Cancel [@MickLesk](https://github.com/MickLesk) ([#18427](https://github.com/community-scripts/ProxmoxVE/pull/18427))
|
||||
- Privilege-Converter: offer only container-capable storages as the converter's restore target [@MickLesk](https://github.com/MickLesk) ([#18415](https://github.com/community-scripts/ProxmoxVE/pull/18415))
|
||||
- let post-pve-install go on when legacy sources are kept [@MickLesk](https://github.com/MickLesk) ([#18403](https://github.com/community-scripts/ProxmoxVE/pull/18403))
|
||||
|
||||
- #### 🔧 Refactor
|
||||
|
||||
- update-apps: say why an backup or restore failed [@MickLesk](https://github.com/MickLesk) ([#18405](https://github.com/community-scripts/ProxmoxVE/pull/18405))
|
||||
- Keep host tools running when clear does not know the terminal [@MickLesk](https://github.com/MickLesk) ([#18412](https://github.com/community-scripts/ProxmoxVE/pull/18412))
|
||||
- Refuse to run the PBS 4 upgrade on a Proxmox VE host [@MickLesk](https://github.com/MickLesk) ([#18416](https://github.com/community-scripts/ProxmoxVE/pull/18416))
|
||||
- fstrim: keep trimming when pct fstrim fails on one container [@MickLesk](https://github.com/MickLesk) ([#18413](https://github.com/community-scripts/ProxmoxVE/pull/18413))
|
||||
- execute-lxc: do not abort on a container that is not Debian or Ubuntu [@MickLesk](https://github.com/MickLesk) ([#18414](https://github.com/community-scripts/ProxmoxVE/pull/18414))
|
||||
|
||||
### 📚 Documentation
|
||||
|
||||
- github: flag instead of closing on first PR template miss; add script-request eligibility check [@MickLesk](https://github.com/MickLesk) ([#18384](https://github.com/community-scripts/ProxmoxVE/pull/18384))
|
||||
|
||||
## 2026-11-02
|
||||
|
||||
### 🚀 Updated Scripts
|
||||
|
||||
+1
-1
@@ -48,7 +48,7 @@ function update_script() {
|
||||
msg_ok "Built Cinephage"
|
||||
|
||||
msg_info "Updating Camoufox"
|
||||
$STD /opt/cinephage/node_modules/.bin/camoufox fetch
|
||||
$STD /opt/cinephage/node_modules/.bin/camoufox-js fetch
|
||||
msg_ok "Updated Camoufox"
|
||||
|
||||
msg_info "Starting Cinephage"
|
||||
|
||||
@@ -58,17 +58,6 @@ function update_script() {
|
||||
$STD git pull origin main
|
||||
msg_ok "Updated Discourse"
|
||||
|
||||
if check_for_gh_release "libvips" "libvips/libvips"; then
|
||||
ensure_dependencies meson ninja-build pkg-config libglib2.0-dev libexpat1-dev libjpeg-dev libpng-dev libwebp-dev libtiff-dev libheif-dev libjxl-dev librsvg2-dev libcgif-dev libexif-dev liblcms2-dev liborc-0.4-dev libimagequant-dev libpango1.0-dev
|
||||
CLEAN_INSTALL=1 fetch_and_deploy_gh_release "libvips" "libvips/libvips" "prebuild" "latest" "/opt/libvips" "vips-*.tar.xz"
|
||||
msg_info "Building libvips"
|
||||
cd /opt/libvips
|
||||
$STD meson setup build --prefix=/usr/local --libdir=lib --buildtype=release -Dmodules=disabled -Dmagick=disabled -Dexamples=false
|
||||
$STD ninja -C build install
|
||||
ldconfig
|
||||
msg_ok "Built libvips"
|
||||
fi
|
||||
|
||||
msg_info "Updating Dependencies"
|
||||
RUBY_TARGET=$(grep -A1 '^RUBY VERSION' /opt/discourse/Gemfile.lock | grep -oE '[0-9]+\.[0-9]+\.[0-9]+' | head -n1)
|
||||
RUBY_VERSION="${RUBY_TARGET:-4.0.7}" RUBY_INSTALL_RAILS="false" setup_ruby
|
||||
|
||||
+3
-3
@@ -149,13 +149,13 @@ EOF
|
||||
PG_VERSION=$(ls /etc/postgresql/ 2>/dev/null | sort -V | tail -1)
|
||||
PG_VERSION=${PG_VERSION:-16}
|
||||
[[ -f ~/.vchord_version ]] && mv ~/.vchord_version ~/.vectorchord
|
||||
if check_for_gh_release "VectorChord" "supervc-stack/VectorChord" "${VCHORD_RELEASE}" "updated together with Immich after testing"; then
|
||||
if check_for_gh_release "VectorChord" "tensorchord/VectorChord" "${VCHORD_RELEASE}" "updated together with Immich after testing"; then
|
||||
# dead tuples in smart_search/face_search make the REINDEX below fail with
|
||||
# "missing chunk ... for toast value" on VectorChord 1.0.0 (#15588); must vacuum
|
||||
# while still on the old extension version, a post-upgrade vacuum errors instead
|
||||
$STD sudo -u postgres psql -d immich -c "VACUUM (ANALYZE) smart_search;"
|
||||
$STD sudo -u postgres psql -d immich -c "VACUUM (ANALYZE) face_search;"
|
||||
fetch_and_deploy_gh_release "VectorChord" "supervc-stack/VectorChord" "binary" "${VCHORD_RELEASE}" "/tmp" "postgresql-${PG_VERSION}-vchord_*_$(arch_resolve).deb"
|
||||
fetch_and_deploy_gh_release "VectorChord" "tensorchord/VectorChord" "binary" "${VCHORD_RELEASE}" "/tmp" "postgresql-${PG_VERSION}-vchord_*_$(arch_resolve).deb"
|
||||
systemctl restart postgresql
|
||||
$STD sudo -u postgres psql -d immich -c "ALTER EXTENSION vector UPDATE;"
|
||||
$STD sudo -u postgres psql -d immich -c "ALTER EXTENSION vchord UPDATE;"
|
||||
@@ -232,7 +232,7 @@ EOF
|
||||
# plugins
|
||||
cd "$SRC_DIR"
|
||||
export MISE_TRUSTED_CONFIG_PATHS="$SRC_DIR"/mise.toml
|
||||
export MISE_DISABLE_TOOLS=github:jellyfin/jellyfin-ffmpeg,java,terragrunt,opentofu,npm:@openapitools/openapi-generator-cli
|
||||
export MISE_DISABLE_TOOLS=github:jellyfin/jellyfin-ffmpeg
|
||||
export MISE_LOCKFILE_MODE=merge MISE_LOCKFILE_PLATFORMS="linux-$(arch_resolve x64 arm64)"
|
||||
mise_ok=0
|
||||
for i in 1 2 3; do
|
||||
|
||||
@@ -30,41 +30,6 @@ function update_script() {
|
||||
exit
|
||||
fi
|
||||
|
||||
if [[ -d /opt/wikijs/backend ]]; then
|
||||
NODE_VERSION="26" setup_nodejs
|
||||
|
||||
if check_for_gh_release "wikijs" "requarks/wiki"; then
|
||||
PUPPETEER_INSTALLED=$([[ -d /opt/wikijs/backend/node_modules/puppeteer ]] && echo "true" || echo "false")
|
||||
|
||||
msg_info "Stopping Service"
|
||||
systemctl stop wikijs
|
||||
msg_ok "Stopped Service"
|
||||
|
||||
create_backup /opt/wikijs/config.yml
|
||||
CLEAN_INSTALL=1 fetch_and_deploy_gh_release "wikijs" "requarks/wiki" "prebuild" "latest" "/opt/wikijs" "wiki-js.tar.gz"
|
||||
restore_backup
|
||||
|
||||
cd /opt/wikijs/backend
|
||||
if [[ "$(arch_resolve)" == "arm64" ]]; then
|
||||
msg_info "Building Wiki.js Dependencies"
|
||||
$STD npm ci --omit=dev
|
||||
msg_ok "Built Wiki.js Dependencies"
|
||||
fi
|
||||
if [[ "$PUPPETEER_INSTALLED" == "true" ]]; then
|
||||
msg_info "Reinstalling Puppeteer"
|
||||
$STD npm install --no-save --force --include=optional --no-ignore-scripts --no-audit --no-fund "puppeteer@$(sed -n 's/^installVersion: *//p' modules/extensions/puppeteer/definition.yml)"
|
||||
msg_ok "Reinstalled Puppeteer"
|
||||
fi
|
||||
|
||||
msg_info "Starting Service"
|
||||
systemctl start wikijs
|
||||
msg_ok "Started Service"
|
||||
msg_ok "Updated successfully!"
|
||||
fi
|
||||
exit
|
||||
fi
|
||||
|
||||
msg_warn "Wiki.js 3 cannot update a 2.x install in place. Export it under Administration > Utilities > Export for Wiki.js 3.x and import it into a new Wiki.js 3 container."
|
||||
NODE_VERSION="26" NODE_MODULE="yarn,node-gyp" setup_nodejs
|
||||
|
||||
if check_for_gh_release "wikijs" "requarks/wiki-v2"; then
|
||||
|
||||
+1
-1
@@ -22,7 +22,7 @@ if [[ "${var_os:-}" == "alpine" ]]; then
|
||||
var_disk="${var_disk:-1}"
|
||||
var_version="${var_version:-3.24}"
|
||||
else
|
||||
var_ram="${var_ram:-1024}"
|
||||
var_ram="${var_ram:-512}"
|
||||
var_disk="${var_disk:-4}"
|
||||
var_version="${var_version:-13}"
|
||||
fi
|
||||
|
||||
@@ -35,7 +35,7 @@ $STD npm prune --omit=dev
|
||||
msg_ok "Built Cinephage"
|
||||
|
||||
msg_info "Installing Camoufox"
|
||||
$STD /opt/cinephage/node_modules/.bin/camoufox fetch
|
||||
$STD /opt/cinephage/node_modules/.bin/camoufox-js fetch
|
||||
msg_ok "Installed Camoufox"
|
||||
|
||||
msg_info "Configuring Cinephage"
|
||||
|
||||
@@ -25,36 +25,9 @@ $STD apt install -y \
|
||||
gsfonts \
|
||||
brotli \
|
||||
nginx \
|
||||
redis-server \
|
||||
meson \
|
||||
ninja-build \
|
||||
pkg-config \
|
||||
libglib2.0-dev \
|
||||
libexpat1-dev \
|
||||
libjpeg-dev \
|
||||
libpng-dev \
|
||||
libwebp-dev \
|
||||
libtiff-dev \
|
||||
libheif-dev \
|
||||
libjxl-dev \
|
||||
librsvg2-dev \
|
||||
libcgif-dev \
|
||||
libexif-dev \
|
||||
liblcms2-dev \
|
||||
liborc-0.4-dev \
|
||||
libimagequant-dev \
|
||||
libpango1.0-dev
|
||||
redis-server
|
||||
msg_ok "Installed Dependencies"
|
||||
|
||||
fetch_and_deploy_gh_release "libvips" "libvips/libvips" "prebuild" "latest" "/opt/libvips" "vips-*.tar.xz"
|
||||
|
||||
msg_info "Building libvips"
|
||||
cd /opt/libvips
|
||||
$STD meson setup build --prefix=/usr/local --libdir=lib --buildtype=release -Dmodules=disabled -Dmagick=disabled -Dexamples=false
|
||||
$STD ninja -C build install
|
||||
ldconfig
|
||||
msg_ok "Built libvips"
|
||||
|
||||
msg_info "Cloning Discourse"
|
||||
$STD git clone --depth 1 https://github.com/discourse/discourse.git /opt/discourse
|
||||
msg_ok "Cloned Discourse"
|
||||
|
||||
+23
-30
@@ -18,11 +18,6 @@ if is_debian && [[ "$(get_os_version_major)" -lt 13 ]]; then
|
||||
exit 238
|
||||
fi
|
||||
|
||||
if [[ "$(arch_resolve)" == "amd64" ]] && ! grep -qm1 sse4_2 /proc/cpuinfo; then
|
||||
msg_error "Immich needs a CPU with SSE4.2: its web build stops with 'Illegal instruction' on older ones."
|
||||
exit 236
|
||||
fi
|
||||
|
||||
RELEASE="v3.3.5"
|
||||
if lscpu | grep -q 'GenuineIntel'; then
|
||||
echo ""
|
||||
@@ -113,7 +108,6 @@ $STD apt install --no-install-recommends -y \
|
||||
libhwy-dev \
|
||||
libwebp-dev \
|
||||
libaom-dev \
|
||||
unzip \
|
||||
ccache
|
||||
|
||||
setup_deb822_repo \
|
||||
@@ -135,28 +129,6 @@ if [[ "$CTTYPE" == "0" && -d /dev/dri ]]; then
|
||||
fi
|
||||
msg_ok "Dependencies Installed"
|
||||
|
||||
INSTALL_DIR="/opt/${APPLICATION}"
|
||||
UPLOAD_DIR="${INSTALL_DIR}/upload"
|
||||
SRC_DIR="${INSTALL_DIR}/source"
|
||||
APP_DIR="${INSTALL_DIR}/app"
|
||||
PLUGIN_DIR="${APP_DIR}/plugins/immich-plugin-core"
|
||||
ML_DIR="${APP_DIR}/machine-learning"
|
||||
GEO_DIR="${INSTALL_DIR}/geodata"
|
||||
mkdir -p {"${APP_DIR}","${UPLOAD_DIR}","${GEO_DIR}","${INSTALL_DIR}"/cache}
|
||||
|
||||
msg_info "Installing GeoNames data"
|
||||
cd "$GEO_DIR"
|
||||
curl_with_retry "https://download.geonames.org/export/dump/admin1CodesASCII.txt" "admin1CodesASCII.txt"
|
||||
curl_with_retry "https://download.geonames.org/export/dump/admin2Codes.txt" "admin2Codes.txt"
|
||||
curl_with_retry "https://download.geonames.org/export/dump/countryInfo.txt" "countryInfo.txt"
|
||||
curl_with_retry "https://download.geonames.org/export/dump/cities500.zip" "cities500.zip"
|
||||
curl_with_retry "https://raw.githubusercontent.com/nvkelso/natural-earth-vector/v5.1.2/geojson/ne_10m_admin_0_countries.geojson" "ne_10m_admin_0_countries.geojson"
|
||||
unzip -q cities500.zip
|
||||
date --iso-8601=seconds | tr -d "\n" >geodata-date.txt
|
||||
rm cities500.zip
|
||||
cd ~
|
||||
msg_ok "Installed GeoNames data"
|
||||
|
||||
MISE_ARCH="x64"
|
||||
[[ "$(arch_resolve)" == "arm64" ]] && MISE_ARCH="arm64"
|
||||
fetch_and_deploy_gh_release "mise" "jdx/mise" "singlefile" "latest" "/usr/local/bin" "mise-v*-linux-${MISE_ARCH}"
|
||||
@@ -195,7 +167,7 @@ ACTUAL_PG_VERSION=$(ls /etc/postgresql/ 2>/dev/null | sort -V | tail -1)
|
||||
ACTUAL_PG_VERSION=${ACTUAL_PG_VERSION:-16}
|
||||
|
||||
VCHORD_RELEASE="1.1.1"
|
||||
fetch_and_deploy_gh_release "VectorChord" "supervc-stack/VectorChord" "binary" "${VCHORD_RELEASE}" "/tmp" "postgresql-${ACTUAL_PG_VERSION}-vchord_*_$(arch_resolve).deb"
|
||||
fetch_and_deploy_gh_release "VectorChord" "tensorchord/VectorChord" "binary" "${VCHORD_RELEASE}" "/tmp" "postgresql-${ACTUAL_PG_VERSION}-vchord_*_$(arch_resolve).deb"
|
||||
|
||||
sed -i "s/^#shared_preload.*/shared_preload_libraries = 'vchord.so'/" /etc/postgresql/${ACTUAL_PG_VERSION}/main/postgresql.conf
|
||||
systemctl restart postgresql.service
|
||||
@@ -375,6 +347,15 @@ libvips: $LIBVIPS_REVISION
|
||||
EOF
|
||||
msg_ok "Custom Photo-processing Libraries Compiled Successfully"
|
||||
|
||||
INSTALL_DIR="/opt/${APPLICATION}"
|
||||
UPLOAD_DIR="${INSTALL_DIR}/upload"
|
||||
SRC_DIR="${INSTALL_DIR}/source"
|
||||
APP_DIR="${INSTALL_DIR}/app"
|
||||
PLUGIN_DIR="${APP_DIR}/plugins/immich-plugin-core"
|
||||
ML_DIR="${APP_DIR}/machine-learning"
|
||||
GEO_DIR="${INSTALL_DIR}/geodata"
|
||||
mkdir -p {"${APP_DIR}","${UPLOAD_DIR}","${GEO_DIR}","${INSTALL_DIR}"/cache}
|
||||
|
||||
fetch_and_deploy_gh_release "Immich" "immich-app/immich" "tarball" "${RELEASE}" "$SRC_DIR"
|
||||
PNPM_VERSION="$(jq -r '.packageManager | split("@")[1] | split("+")[0]' ${SRC_DIR}/package.json)"
|
||||
export COREPACK_ENABLE_DOWNLOAD_PROMPT=0
|
||||
@@ -415,7 +396,7 @@ cp -a web/build "$APP_DIR"/www
|
||||
cp LICENSE "$APP_DIR"
|
||||
cd "$SRC_DIR"
|
||||
export MISE_TRUSTED_CONFIG_PATHS="$SRC_DIR"/mise.toml
|
||||
export MISE_DISABLE_TOOLS=github:jellyfin/jellyfin-ffmpeg,java,terragrunt,opentofu,npm:@openapitools/openapi-generator-cli
|
||||
export MISE_DISABLE_TOOLS=github:jellyfin/jellyfin-ffmpeg
|
||||
export MISE_LOCKFILE_MODE=merge MISE_LOCKFILE_PLATFORMS="linux-$(arch_resolve x64 arm64)"
|
||||
mise_ok=0
|
||||
for i in 1 2 3; do
|
||||
@@ -500,8 +481,20 @@ grep -rlE "'/build'" | xargs -n1 sed -i "s|'/build'|'$APP_DIR'|g"
|
||||
sed -i "s@\"/cache\"@\"$INSTALL_DIR/cache\"@g" "$ML_DIR"/immich_ml/config.py
|
||||
ln -s "$UPLOAD_DIR" "$APP_DIR"/upload
|
||||
ln -s "$UPLOAD_DIR" "$ML_DIR"/upload
|
||||
|
||||
msg_info "Installing GeoNames data"
|
||||
cd "$GEO_DIR"
|
||||
curl_with_retry "https://download.geonames.org/export/dump/admin1CodesASCII.txt" "admin1CodesASCII.txt"
|
||||
curl_with_retry "https://download.geonames.org/export/dump/admin2Codes.txt" "admin2Codes.txt"
|
||||
curl_with_retry "https://download.geonames.org/export/dump/countryInfo.txt" "countryInfo.txt"
|
||||
curl_with_retry "https://download.geonames.org/export/dump/cities500.zip" "cities500.zip"
|
||||
curl_with_retry "https://raw.githubusercontent.com/nvkelso/natural-earth-vector/v5.1.2/geojson/ne_10m_admin_0_countries.geojson" "ne_10m_admin_0_countries.geojson"
|
||||
unzip -q cities500.zip
|
||||
date --iso-8601=seconds | tr -d "\n" >geodata-date.txt
|
||||
rm cities500.zip
|
||||
cd "$INSTALL_DIR"
|
||||
ln -s "$GEO_DIR" "$APP_DIR"
|
||||
msg_ok "Installed GeoNames data"
|
||||
|
||||
# MickLesk temporary patch for HEIC thumbnail gen
|
||||
MEDIA_REPO_JS="/opt/immich/app/dist/repositories/media.repository.js"
|
||||
|
||||
@@ -21,11 +21,7 @@ msg_info "Setup Wazuh"
|
||||
curl -fsSL https://packages.wazuh.com/$RELEASE/wazuh-install.sh -o wazuh-install.sh
|
||||
chmod +x wazuh-install.sh
|
||||
if [ "$STD" = "silent" ]; then
|
||||
bash wazuh-install.sh -a >>~/wazuh-install.output || {
|
||||
$STD tail -n 25 ~/wazuh-install.output
|
||||
msg_error "The Wazuh installer failed, see its output above or in ~/wazuh-install.output"
|
||||
exit 1
|
||||
}
|
||||
bash wazuh-install.sh -a >>~/wazuh-install.output
|
||||
else
|
||||
bash wazuh-install.sh -a | tee -a ~/wazuh-install.output
|
||||
fi
|
||||
|
||||
@@ -17,44 +17,26 @@ msg_info "Installing Dependencies"
|
||||
$STD apt install -y git
|
||||
msg_ok "Installed Dependencies"
|
||||
|
||||
NODE_VERSION="26" setup_nodejs
|
||||
NODE_VERSION="26" NODE_MODULE="yarn,node-gyp" setup_nodejs
|
||||
PG_VERSION="17" setup_postgresql
|
||||
PG_DB_NAME="wiki" PG_DB_USER="wikijs_user" setup_postgresql_db
|
||||
fetch_and_deploy_gh_release "wikijs" "requarks/wiki" "prebuild" "latest" "/opt/wikijs" "wiki-js.tar.gz"
|
||||
|
||||
if [[ "$(arch_resolve)" == "arm64" ]]; then
|
||||
msg_info "Building Wiki.js Dependencies"
|
||||
cd /opt/wikijs/backend
|
||||
$STD npm ci --omit=dev
|
||||
msg_ok "Built Wiki.js Dependencies"
|
||||
fi
|
||||
PG_DB_NAME="wiki" PG_DB_USER="wikijs_user" PG_DB_EXTENSIONS="pg_trgm" setup_postgresql_db
|
||||
fetch_and_deploy_gh_release "wikijs" "requarks/wiki-v2" "prebuild" "latest" "/opt/wikijs" "wiki-js.tar.gz"
|
||||
|
||||
msg_info "Configuring Wiki.js"
|
||||
mkdir -p /opt/wikijs_data
|
||||
cp /opt/wikijs/config.sample.yml /opt/wikijs/config.yml
|
||||
sed -i \
|
||||
-e "s|^ user: .*| user: wikijs_user|" \
|
||||
-e "s|^ pass: .*| pass: ${PG_DB_PASS}|" \
|
||||
-e "s|^ db: .*| db: wiki|" \
|
||||
-e "s|^dataPath: .*|dataPath: /opt/wikijs_data|" \
|
||||
/opt/wikijs/config.yml
|
||||
ADMIN_PASS=$(openssl rand -base64 18 | tr -dc 'a-zA-Z0-9' | cut -c1-16)
|
||||
cat <<EOF >~/wikijs.creds
|
||||
Wiki.js Admin
|
||||
Email: admin@example.com
|
||||
Password: ${ADMIN_PASS}
|
||||
EOF
|
||||
mv /opt/wikijs/config.sample.yml /opt/wikijs/config.yml
|
||||
sed -i -E 's|^( *user: ).*|\1'"$PG_DB_USER"'|' /opt/wikijs/config.yml
|
||||
sed -i -E 's|^( *pass: ).*|\1'"$PG_DB_PASS"'|' /opt/wikijs/config.yml
|
||||
msg_ok "Configured Wiki.js"
|
||||
|
||||
msg_info "Creating Service"
|
||||
cat <<EOF >/etc/systemd/system/wikijs.service
|
||||
[Unit]
|
||||
Description=Wiki.js
|
||||
After=network.target postgresql.service
|
||||
After=network.target
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
ExecStart=/usr/bin/node --no-experimental-webstorage backend
|
||||
ExecStart=/usr/bin/node server
|
||||
Restart=always
|
||||
User=root
|
||||
Environment=NODE_ENV=production
|
||||
@@ -63,13 +45,7 @@ WorkingDirectory=/opt/wikijs
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
EOF
|
||||
systemctl set-environment ADMIN_PASS="${ADMIN_PASS}"
|
||||
systemctl enable -q --now wikijs
|
||||
for _ in $(seq 60); do
|
||||
curl -fs -o /dev/null http://127.0.0.1:3000 && break
|
||||
sleep 2
|
||||
done
|
||||
systemctl unset-environment ADMIN_PASS
|
||||
msg_ok "Created Service"
|
||||
|
||||
motd_ssh
|
||||
|
||||
@@ -36,17 +36,17 @@ cd /tmp
|
||||
ZABBIX_REPO_DIR="debian"
|
||||
[[ "$ZABBIX_VERSION" == "7.0" && "$(arch_resolve)" == "arm64" ]] && ZABBIX_REPO_DIR="debian-arm64"
|
||||
if [[ "$ZABBIX_VERSION" == "7.0" ]]; then
|
||||
ZABBIX_DEB_URL="https://repo.zabbix.com/zabbix/${ZABBIX_VERSION}/${ZABBIX_REPO_DIR}/pool/main/z/zabbix-release/zabbix-release_latest_${ZABBIX_VERSION}+debian$(get_os_version_major)_all.deb"
|
||||
ZABBIX_DEB_FILE="zabbix-release_latest_${ZABBIX_VERSION}+debian$(get_os_version_major)_all.deb"
|
||||
ZABBIX_DEB_URL="https://repo.zabbix.com/zabbix/${ZABBIX_VERSION}/${ZABBIX_REPO_DIR}/pool/main/z/zabbix-release/zabbix-release_latest_${ZABBIX_VERSION}+debian13_all.deb"
|
||||
ZABBIX_DEB_FILE="zabbix-release_latest_${ZABBIX_VERSION}+debian13_all.deb"
|
||||
else
|
||||
ZABBIX_DEB_URL="https://repo.zabbix.com/zabbix/${ZABBIX_VERSION}/release/debian/pool/main/z/zabbix-release/zabbix-release_latest+debian$(get_os_version_major)_all.deb"
|
||||
ZABBIX_DEB_FILE="zabbix-release_latest+debian$(get_os_version_major)_all.deb"
|
||||
ZABBIX_DEB_URL="https://repo.zabbix.com/zabbix/${ZABBIX_VERSION}/release/debian/pool/main/z/zabbix-release/zabbix-release_latest+debian13_all.deb"
|
||||
ZABBIX_DEB_FILE="zabbix-release_latest+debian13_all.deb"
|
||||
fi
|
||||
|
||||
curl -fsSL "$ZABBIX_DEB_URL" -o /tmp/"$ZABBIX_DEB_FILE"
|
||||
$STD dpkg -i /tmp/"$ZABBIX_DEB_FILE"
|
||||
apt_update_safe
|
||||
$STD apt install -y zabbix-server-pgsql zabbix-frontend-php php-pgsql zabbix-apache-conf zabbix-sql-scripts
|
||||
$STD apt install -y zabbix-server-pgsql zabbix-frontend-php php8.4-pgsql zabbix-apache-conf zabbix-sql-scripts
|
||||
|
||||
if [[ "$ZABBIX_VERSION" == "7.0" ]]; then
|
||||
ZABBIX_SQL="/usr/share/zabbix-sql-scripts/postgresql/server.sql.gz"
|
||||
|
||||
@@ -65,28 +65,6 @@ while [[ -z "${CTID}" ]]; do
|
||||
"${CTID_MENU[@]}" 3>&1 1>&2 2>&3) || exit 0
|
||||
done
|
||||
|
||||
if [[ "$(pct status "$CTID" | awk '{print $2}')" != "running" ]]; then
|
||||
msg_info "Starting CT $CTID"
|
||||
pct start "$CTID"
|
||||
for _ in {1..30}; do
|
||||
pct exec "$CTID" -- sh -c 'ip route 2>/dev/null | grep -q "^default"' && break
|
||||
sleep 1
|
||||
done
|
||||
msg_ok "Started CT $CTID"
|
||||
fi
|
||||
|
||||
CT_OS=$(pct exec "$CTID" -- sh -c '. /etc/os-release 2>/dev/null; echo "${ID:-}"')
|
||||
case "$CT_OS" in
|
||||
debian | ubuntu) ;;
|
||||
alpine)
|
||||
pct exec "$CTID" -- sh -c 'command -v bash >/dev/null 2>&1 || apk add --no-cache bash >/dev/null'
|
||||
;;
|
||||
*)
|
||||
msg_error "CT $CTID runs ${CT_OS:-an unknown OS}. This add-on supports Debian, Ubuntu and Alpine containers."
|
||||
exit 238
|
||||
;;
|
||||
esac
|
||||
|
||||
CTID_CONFIG_PATH="/etc/pve/lxc/${CTID}.conf"
|
||||
|
||||
# Skip if already configured
|
||||
|
||||
@@ -70,10 +70,14 @@ function cleanup_ctid() {
|
||||
header_info
|
||||
require_pve_host
|
||||
|
||||
# Stop Proxmox VE Monitor-All if running
|
||||
if systemctl is-active -q ping-instances.service; then
|
||||
systemctl stop ping-instances.service
|
||||
fi
|
||||
msg_info "Updating template list"
|
||||
pveam update >/dev/null 2>&1
|
||||
msg_ok "Updated template list"
|
||||
whiptail --backtitle "Proxmox VE Helper Scripts" --title "All Templates" --yesno "This will allow for the creation of one of the many Template LXC Containers. Proceed?" 10 68 || exit 0
|
||||
whiptail --backtitle "Proxmox VE Helper Scripts" --title "All Templates" --yesno "This will allow for the creation of one of the many Template LXC Containers. Proceed?" 10 68
|
||||
TEMPLATE_MENU=()
|
||||
MSG_MAX_LENGTH=0
|
||||
HOST_ARCH=$(dpkg --print-architecture)
|
||||
@@ -83,7 +87,7 @@ while read -r TAG ITEM ARCH; do
|
||||
((${#ITEM} + OFFSET > MSG_MAX_LENGTH)) && MSG_MAX_LENGTH=${#ITEM}+OFFSET
|
||||
TEMPLATE_MENU+=("$ITEM" "$TAG " "OFF")
|
||||
done < <(pveam available)
|
||||
TEMPLATE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "All Template LXCs" --radiolist "\nSelect a Template LXC to create:\n" 16 $((MSG_MAX_LENGTH + 58)) 10 "${TEMPLATE_MENU[@]}" 3>&1 1>&2 2>&3 | tr -d '"') || exit 0
|
||||
TEMPLATE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "All Template LXCs" --radiolist "\nSelect a Template LXC to create:\n" 16 $((MSG_MAX_LENGTH + 58)) 10 "${TEMPLATE_MENU[@]}" 3>&1 1>&2 2>&3 | tr -d '"')
|
||||
[ -z "$TEMPLATE" ] && {
|
||||
whiptail --backtitle "Proxmox VE Helper Scripts" --title "No Template LXC Selected" --msgbox "It appears that no Template LXC container was selected" 10 68
|
||||
echo "Done"
|
||||
@@ -181,11 +185,6 @@ msg_ok "Using '$TEMPLATE_STORAGE' for template storage."
|
||||
CONTAINER_STORAGE=$(select_storage container)
|
||||
msg_ok "Using '$CONTAINER_STORAGE' for container storage."
|
||||
|
||||
# Stop Proxmox VE Monitor-All if running
|
||||
if systemctl is-active -q ping-instances.service; then
|
||||
systemctl stop ping-instances.service
|
||||
fi
|
||||
|
||||
# Download template
|
||||
msg_info "Downloading LXC template (Patience)"
|
||||
pveam download "$TEMPLATE_STORAGE" "$TEMPLATE" >/dev/null || {
|
||||
|
||||
@@ -42,7 +42,7 @@ require_debian_like
|
||||
function uninstall() {
|
||||
msg_info "Uninstalling Homebrew"
|
||||
|
||||
BREW_USER=$(awk -F: '$3 >= 1000 && $3 < 65534 && $7 !~ /(nologin|false)$/ { print $1; exit }' /etc/passwd)
|
||||
BREW_USER=$(awk -F: '$3 >= 1000 && $3 < 65534 { print $1; exit }' /etc/passwd)
|
||||
if [[ -n "$BREW_USER" ]]; then
|
||||
BREW_USER_HOME=$(getent passwd "$BREW_USER" | cut -d: -f6)
|
||||
for rc_file in "$BREW_USER_HOME/.bashrc" "$BREW_USER_HOME/.profile"; do
|
||||
@@ -64,7 +64,7 @@ function uninstall() {
|
||||
# ==============================================================================
|
||||
function install() {
|
||||
msg_info "Detecting Non-Root User"
|
||||
BREW_USER=$(awk -F: '$3 >= 1000 && $3 < 65534 && $7 !~ /(nologin|false)$/ { print $1; exit }' /etc/passwd)
|
||||
BREW_USER=$(awk -F: '$3 >= 1000 && $3 < 65534 { print $1; exit }' /etc/passwd)
|
||||
if [[ -z "$BREW_USER" ]]; then
|
||||
msg_warn "No non-root user found (uid >= 1000). Homebrew cannot run as root."
|
||||
read -r -p "${TAB}Create a 'brew' user automatically? (y/N): " create_user_prompt
|
||||
@@ -81,7 +81,7 @@ function install() {
|
||||
msg_ok "Detected User: $BREW_USER"
|
||||
|
||||
msg_info "Installing Dependencies"
|
||||
apt_update_safe
|
||||
$STD apt update
|
||||
$STD apt install -y build-essential git file procps
|
||||
msg_ok "Installed Dependencies"
|
||||
|
||||
|
||||
@@ -80,7 +80,7 @@ install() {
|
||||
msg_ok "Set up repository"
|
||||
|
||||
msg_info "Installing ${APP}"
|
||||
apt_update_safe
|
||||
$STD apt update
|
||||
$STD apt install -y netdata
|
||||
msg_ok "Installed ${APP}"
|
||||
msg_ok "Completed successfully!\n"
|
||||
@@ -108,7 +108,7 @@ OPTIONS=(Install "Install ${APP} on Proxmox VE"
|
||||
Uninstall "Uninstall ${APP} from Proxmox VE")
|
||||
|
||||
CHOICE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "${APP}" \
|
||||
--menu "Select an option:" 10 58 2 "${OPTIONS[@]}" 3>&1 1>&2 2>&3) || exit 0
|
||||
--menu "Select an option:" 10 58 2 "${OPTIONS[@]}" 3>&1 1>&2 2>&3)
|
||||
|
||||
case $CHOICE in
|
||||
"Install") install ;;
|
||||
|
||||
@@ -67,7 +67,7 @@ for container in $(pct list | awk '{if(NR>1) print $1}'); do
|
||||
else
|
||||
os=$(pct config "$container" | awk '/^ostype/ {print $2}')
|
||||
if [ "$os" != "debian" ] && [ "$os" != "ubuntu" ]; then
|
||||
echo -e "${BL}[Info]${GN} Skipping ${RD}$container is not Debian or Ubuntu ${CL}"
|
||||
echo -e "${BL}[Info]${GN} Skipping ${name} ${RD}$container is not Debian or Ubuntu ${CL}"
|
||||
continue
|
||||
fi
|
||||
|
||||
|
||||
+1
-2
@@ -118,8 +118,7 @@ function trim_container() {
|
||||
fi
|
||||
|
||||
local fstrim_output
|
||||
# pct fstrim fails on a locked CT (e.g. a running backup); report it and go on.
|
||||
fstrim_output=$(pct fstrim "$container" 2>&1) || true
|
||||
fstrim_output=$(pct fstrim "$container" 2>&1)
|
||||
if echo "$fstrim_output" | grep -qi "not supported"; then
|
||||
echo -e "${RD}fstrim isnt supported on this storage!${CL}"
|
||||
elif echo "$fstrim_output" | grep -Eq '([0-9]+(\.[0-9]+)?\s*[KMGT]?B)'; then
|
||||
|
||||
@@ -199,11 +199,6 @@ while true; do
|
||||
esac
|
||||
done
|
||||
|
||||
if command -v pveversion >/dev/null 2>&1; then
|
||||
echo -e "\n🛑 PVE Detected, Wrong Script!\n"
|
||||
exit 232
|
||||
fi
|
||||
|
||||
if [ "$(dpkg --print-architecture 2>/dev/null)" = "arm64" ]; then
|
||||
header_info
|
||||
msg_error "This upgrade script targets the amd64 Proxmox repositories and is not supported on ARM64."
|
||||
|
||||
@@ -226,8 +226,9 @@ start_routines_9() {
|
||||
[[ -n "$list_files" ]] && MSG+="$(echo "$list_files" | sed 's|^| - |')\n"
|
||||
MSG+="\nDo you want to disable (comment out/rename) all legacy sources and use ONLY deb822 .sources format?\n\nRecommended for Proxmox VE 9."
|
||||
|
||||
if whiptail --backtitle "Proxmox VE Helper Scripts" --title "Disable legacy sources?" \
|
||||
--yesno "$MSG" 18 80; then
|
||||
whiptail --backtitle "Proxmox VE Helper Scripts" --title "Disable legacy sources?" \
|
||||
--yesno "$MSG" 18 80
|
||||
if [[ $? -eq 0 ]]; then
|
||||
# Backup and disable sources.list
|
||||
if [[ -f "$listfile" ]] && grep -qE '^\s*deb ' "$listfile"; then
|
||||
cp "$listfile" "$listfile.bak"
|
||||
|
||||
@@ -31,7 +31,7 @@ check_root() {
|
||||
|
||||
select_target_storage_and_container_id() {
|
||||
echo -e "\nSelect target storage for restored container:\n"
|
||||
mapfile -t target_storages < <(pvesm status --content rootdir | awk 'NR > 1 {print $1}')
|
||||
mapfile -t target_storages < <(pvesm status --content images | awk 'NR > 1 {print $1}')
|
||||
for i in "${!target_storages[@]}"; do
|
||||
printf "%s) %s\n" "$((i + 1))" "${target_storages[$i]}"
|
||||
done
|
||||
|
||||
+13
-20
@@ -271,22 +271,15 @@ function dry_run_container() {
|
||||
fi
|
||||
}
|
||||
|
||||
# The line of vzdump or pct restore output that says why it failed.
|
||||
function failure_reason() {
|
||||
local reason
|
||||
reason=$(grep -m1 '^ERROR:' <<<"$1" | sed 's/^ERROR: //')
|
||||
[[ -n "$reason" ]] || reason=$(grep -v '^[[:space:]]*$' <<<"$1" | tail -n1)
|
||||
printf '%s' "$reason"
|
||||
}
|
||||
|
||||
function backup_container() {
|
||||
msg_info "Creating backup for container $1"
|
||||
local output
|
||||
if output=$(vzdump $1 --compress zstd --storage $STORAGE_CHOICE -notes-template "{{guestname}} - community-scripts backup updater" 2>&1); then
|
||||
vzdump $1 --compress zstd --storage $STORAGE_CHOICE -notes-template "{{guestname}} - community-scripts backup updater" >/dev/null 2>&1
|
||||
status=$?
|
||||
|
||||
if [ $status -eq 0 ]; then
|
||||
msg_ok "Backup created"
|
||||
else
|
||||
log_write "Container $1: vzdump failed:"$'\n'"$output"
|
||||
msg_error "Backup failed for container $1: $(failure_reason "$output")"
|
||||
msg_error "Backup failed for container $1"
|
||||
exit 235
|
||||
fi
|
||||
}
|
||||
@@ -344,7 +337,7 @@ function log_write() {
|
||||
|
||||
# Skip confirmation if var_skip_confirm is set to yes
|
||||
if [[ "$var_skip_confirm" != "yes" ]]; then
|
||||
whiptail --backtitle "Proxmox VE Helper Scripts" --title "LXC App Update" --yesno "This will update apps in LXCs installed by Helper-Scripts. Proceed?" 10 58 || exit 0
|
||||
whiptail --backtitle "Proxmox VE Helper Scripts" --title "LXC App Update" --yesno "This will update apps in LXCs installed by Helper-Scripts. Proceed?" 10 58 || exit
|
||||
fi
|
||||
|
||||
tags_formatted="${var_tags//|/, }"
|
||||
@@ -467,11 +460,12 @@ if [ "$BACKUP_CHOICE" == "yes" ]; then
|
||||
# Determine storage based on var_backup_storage
|
||||
if [[ -n "$var_backup_storage" ]]; then
|
||||
# Validate that the specified storage exists and supports backups
|
||||
if grep -qxF -- "$var_backup_storage" <<<"$STORAGES"; then
|
||||
if echo "$STORAGES" | grep -qw "$var_backup_storage"; then
|
||||
STORAGE_CHOICE="$var_backup_storage"
|
||||
msg_ok "Using backup storage: $STORAGE_CHOICE"
|
||||
else
|
||||
msg_error "Specified backup storage '$var_backup_storage' not found or doesn't support backups! Available: $(echo $STORAGES)"
|
||||
msg_error "Specified backup storage '$var_backup_storage' not found or doesn't support backups!"
|
||||
msg_info "Available storages: $(echo $STORAGES | tr '\n' ' ')"
|
||||
exit 119
|
||||
fi
|
||||
else
|
||||
@@ -648,7 +642,7 @@ for container in $CHOICE; do
|
||||
log_result "$container" "$service" "SKIPPED" "Storage critically low on /boot (>80%)"
|
||||
log_write "Container $container ($service): SKIPPED — storage critically low on /boot"
|
||||
elif [ "$BACKUP_CHOICE" == "yes" ]; then
|
||||
msg_error "Update failed for container $container ($service, exit code: $exit_code) — attempting restore"
|
||||
msg_error "Update failed for container $container (exit code: $exit_code) — attempting restore"
|
||||
log_write "Container $container ($service): FAILED (exit $exit_code) — attempting restore"
|
||||
msg_info "Restoring LXC $container from backup ($STORAGE_CHOICE)"
|
||||
pct stop $container
|
||||
@@ -660,7 +654,7 @@ for container in $CHOICE; do
|
||||
log_write "Container $container ($service): FAILED — no backup found for restore"
|
||||
exit 235
|
||||
fi
|
||||
restore_output=$(pct restore $container "$BACKUP_ENTRY" --storage $LXC_STORAGE --force 2>&1)
|
||||
pct restore $container "$BACKUP_ENTRY" --storage $LXC_STORAGE --force >/dev/null 2>&1
|
||||
restorestatus=$?
|
||||
if [ $restorestatus -eq 0 ]; then
|
||||
pct start $container
|
||||
@@ -668,14 +662,13 @@ for container in $CHOICE; do
|
||||
log_result "$container" "$service" "RESTORED" "Update failed (exit $exit_code) — restored from backup"
|
||||
log_write "Container $container ($service): RESTORED from $BACKUP_ENTRY"
|
||||
else
|
||||
log_write "Container $container: pct restore failed:"$'\n'"$restore_output"
|
||||
msg_error "Restore failed for container $container: $(failure_reason "$restore_output")"
|
||||
msg_error "Restore failed for container $container"
|
||||
log_result "$container" "$service" "FAILED" "Update failed (exit $exit_code) — restore also failed"
|
||||
log_write "Container $container ($service): FAILED — restore also failed"
|
||||
exit 235
|
||||
fi
|
||||
else
|
||||
msg_error "Update failed for container $container ($service, exit code: $exit_code)"
|
||||
msg_error "Update failed for container $container (exit code: $exit_code)"
|
||||
log_result "$container" "$service" "FAILED" "Exit code $exit_code"
|
||||
log_write "Container $container ($service): FAILED (exit $exit_code)"
|
||||
if [[ "$var_continue_on_error" == "yes" ]]; then
|
||||
|
||||
+22
-3
@@ -24,9 +24,18 @@ THIN="discard=on,ssd=1,"
|
||||
set -e
|
||||
trap 'error_handler $LINENO "$BASH_COMMAND"' ERR
|
||||
trap cleanup EXIT
|
||||
trap 'post_update_to_api "failed" "130"' SIGINT
|
||||
trap 'post_update_to_api "failed" "143"' SIGTERM
|
||||
trap 'post_update_to_api "failed" "129"; exit 129' SIGHUP
|
||||
trap 'post_update_to_api "failed" "INTERRUPTED"' SIGINT
|
||||
trap 'post_update_to_api "failed" "TERMINATED"' SIGTERM
|
||||
|
||||
function error_handler() {
|
||||
local exit_code="$?"
|
||||
local line_number="$1"
|
||||
local command="$2"
|
||||
local error_message="${RD}[ERROR]${CL} in line ${RD}$line_number${CL}: exit code ${RD}$exit_code${CL}: while executing command ${YW}$command${CL}"
|
||||
post_update_to_api "failed" "${command}"
|
||||
echo -e "\n$error_message\n"
|
||||
cleanup_vmid
|
||||
}
|
||||
|
||||
TEMP_DIR=$(mktemp -d)
|
||||
pushd "$TEMP_DIR" >/dev/null
|
||||
@@ -117,6 +126,16 @@ post_to_api_vm
|
||||
|
||||
vm_select_storage "$HN"
|
||||
|
||||
# ==============================================================================
|
||||
# PREREQUISITES
|
||||
# ==============================================================================
|
||||
if ! command -v virt-customize &>/dev/null; then
|
||||
msg_info "Installing libguestfs-tools"
|
||||
$STD apt-get update
|
||||
$STD apt-get install -y libguestfs-tools
|
||||
msg_ok "Installed libguestfs-tools"
|
||||
fi
|
||||
|
||||
msg_info "Retrieving the URL for the ${APP} Qcow2 Disk Image"
|
||||
ALMA_ARCH="$(vm_arch_resolve x86_64 aarch64)"
|
||||
URL="https://repo.almalinux.org/almalinux/${var_version}/cloud/${ALMA_ARCH}/images/AlmaLinux-${var_version}-GenericCloud-latest.${ALMA_ARCH}.qcow2"
|
||||
|
||||
@@ -89,6 +89,13 @@ post_to_api_vm
|
||||
|
||||
vm_select_storage "$HN"
|
||||
|
||||
if ! command -v virt-customize &>/dev/null; then
|
||||
msg_info "Installing libguestfs-tools"
|
||||
$STD apt-get update
|
||||
$STD apt-get install -y libguestfs-tools
|
||||
msg_ok "Installed libguestfs-tools"
|
||||
fi
|
||||
|
||||
msg_info "Retrieving the URL for the Fedora Cloud Base image"
|
||||
MIRROR="https://dl.fedoraproject.org/pub/fedora/linux/releases"
|
||||
FEDORA_RELEASE=$(curl -fsSL "$MIRROR/" 2>/dev/null | grep -oP 'href="\K[0-9]+(?=/")' | sort -rn | head -1)
|
||||
|
||||
+1
-1
@@ -259,7 +259,7 @@ post_to_api_vm
|
||||
vm_select_storage "$HN"
|
||||
msg_info "Getting URL for OpenWrt Disk Image"
|
||||
|
||||
vm_latest_from_index "https://downloads.openwrt.org/.versions.json" '"stable_version": *"\K[0-9]+\.[0-9]+\.[0-9]+' || exit 115
|
||||
vm_latest_from_index "https://openwrt.org" 'Current stable release - OpenWrt \K[0-9]+\.[0-9]+\.[0-9]+' || exit 115
|
||||
var_version="$VM_INDEX_LATEST"
|
||||
URL="https://downloads.openwrt.org/releases/$var_version/targets/x86/64/openwrt-$var_version-x86-64-generic-ext4-combined.img.gz"
|
||||
|
||||
|
||||
Reference in New Issue
Block a user