Compare commits

..

1 Commits

Author SHA1 Message Date
MickLesk 293f050f11 Technitium DNS: read the version from its GitHub releases
The version came from scraping technitium.com, which failed for some hosts ("Command substitution exited with status 1", 6 runs on 4 hosts in three days) and has no rate-limit fallback. The GitHub release carries the same version and goes through the engine's release lookup.
2026-11-03 10:30:00 +01:00
45 changed files with 258 additions and 604 deletions
+2 -18
View File
@@ -58,9 +58,9 @@ body:
Before continuing, verify that the requested project meets the minimum requirements below. 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 - type: input
id: repository-stars id: repository-stars
@@ -134,22 +134,6 @@ body:
- label: I understand that requests outside the project scope may be closed without an extensive explanation. - label: I understand that requests outside the project scope may be closed without an extensive explanation.
required: true 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 - type: markdown
attributes: attributes:
value: | value: |
+10 -10
View File
@@ -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. --> **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) ## 🛠️ Type of Change (**X** in brackets)
@@ -60,13 +70,3 @@ Guidance:
--> -->
<!-- The advisory block is only active once it is OUTSIDE this comment. --> <!-- 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
View File
@@ -1,4 +1,4 @@
name: Flag PRs Missing Template name: Close PRs Missing Template
on: on:
pull_request_target: pull_request_target:
@@ -14,12 +14,10 @@ jobs:
issues: write issues: write
contents: read contents: read
steps: 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 uses: actions/github-script@v9
with: with:
script: | script: |
const MARKER = "<!-- pr-template-check -->";
const LABELS = ["missing pr template", "stale"];
const pr = context.payload.pull_request; const pr = context.payload.pull_request;
const prNumber = pr.number; const prNumber = pr.number;
const author = pr.user.login; const author = pr.user.login;
@@ -46,11 +44,11 @@ jobs:
return; return;
} }
const prLabels = pr.labels.map((label) => label.name); const labels = pr.labels.map((label) => label.name);
const skipLabels = ["automated pr", "keep-open"]; const skipLabels = ["automated pr", "keep-open"];
if (skipLabels.some((label) => prLabels.includes(label))) { if (skipLabels.some((label) => labels.includes(label))) {
core.info(`PR #${prNumber} has a skip label (${prLabels.join(", ")}) — skipping template validation.`); core.info(`PR #${prNumber} has a skip label (${labels.join(", ")}) — skipping template validation.`);
return; return;
} }
@@ -116,52 +114,50 @@ jobs:
failures.push("At least one **Type of Change** checkbox must be checked."); 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) { if (failures.length === 0) {
core.info(`PR #${prNumber} follows the PR template.`); 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; return;
} }
core.info(`PR #${prNumber} — template validation failed, flagging instead of closing.`); core.info(`Closing PR #${prNumber} — template validation failed.`);
const templateUrl = const templateUrl =
"https://github.com/community-scripts/ProxmoxVE/blob/main/.github/pull_request_template.md"; "https://github.com/community-scripts/ProxmoxVE/blob/main/.github/pull_request_template.md";
const failureList = failures.map((item) => `- ${item}`).join("\n"); const failureList = failures.map((item) => `- ${item}`).join("\n");
const text = const comment = [
MARKER + "\n" + `👋 Hi @${author},`,
`👋 Hi @${author},\n\n` + ``,
`This pull request does not follow the [PR template](${templateUrl}) yet:\n\n` + `This pull request was closed because it does not follow the [PR template](${templateUrl}).`,
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` + `Please fix the following and open a new PR (or reopen this one after updating the description):`,
`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` + failureList,
`Thank you for contributing! 🙏`; ``,
`> 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) { await github.rest.issues.createComment({
if (mine.body !== text) { owner,
await github.rest.issues.updateComment({ owner, repo, comment_id: mine.id, body: text }); repo,
} issue_number: prNumber,
} else { body: comment,
await github.rest.issues.createComment({ owner, repo, issue_number: prNumber, body: text }); });
}
const toAdd = LABELS.filter(name => !prLabels.includes(name)); await github.rest.pulls.update({
if (toAdd.length > 0) { owner,
await github.rest.issues.addLabels({ owner, repo, issue_number: prNumber, labels: toAdd }); repo,
} pull_number: prNumber,
state: "closed",
});
await github.rest.issues.addLabels({
owner,
repo,
issue_number: prNumber,
labels: ["missing pr template"],
});
-141
View File
@@ -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 }
);
}
+73 -95
View File
@@ -22,112 +22,90 @@ jobs:
const now = new Date(); const now = new Date();
const owner = context.repo.owner; const owner = context.repo.owner;
const repo = context.repo.repo; const repo = context.repo.repo;
const TEMPLATE_LABEL = "missing pr template";
const MAINTAINERS = ["OWNER", "MEMBER", "COLLABORATOR"]; // --- When stale label is added, comment immediately ---
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.
if (context.eventName === "pull_request_target" && context.payload.action === "labeled") { if (context.eventName === "pull_request_target" && context.payload.action === "labeled") {
const pr = context.payload.pull_request; const label = context.payload.label?.name;
const labels = (pr.labels || []).map(l => l.name); if (label === "stale") {
if (context.payload.label?.name === "stale" && !labels.includes(TEMPLATE_LABEL)) { const author = context.payload.pull_request.user.login;
await github.rest.issues.createComment({ 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; 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) { for (const pr of prs) {
const labels = pr.labels.map(l => l.name); const hasStale = pr.labels.some(l => l.name === "stale");
const author = pr.user.login; if (!hasStale) continue;
const hasStale = labels.includes("stale");
// Get timeline events to find when stale label was added
if (labels.includes("keep-open")) { const { data: events } = await github.rest.issues.listEvents({
if (hasStale) { owner,
await github.rest.issues.removeLabel({ owner, repo, issue_number: pr.number, name: "stale" }); repo,
} issue_number: pr.number,
continue; per_page: 100
}
const comments = await github.paginate(github.rest.issues.listComments, {
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 // Find the most recent time the stale label was added
}); const staleLabelEvents = events
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
.filter(e => e.event === "labeled" && e.label?.name === "stale") .filter(e => e.event === "labeled" && e.label?.name === "stale")
.map(e => new Date(e.created_at)); .sort((a, b) => new Date(b.created_at) - new Date(a.created_at));
if (staleDates.length === 0) continue;
const staleLabelDate = new Date(Math.max(...staleDates)); if (staleLabelEvents.length === 0) continue;
const isTemplateIssue = labels.includes(TEMPLATE_LABEL);
const staleLabelDate = new Date(staleLabelEvents[0].created_at);
// A push ends an inactivity mark; a template mark only ends via close-invalid-pr-template.yml's re-check. const daysSinceStale = (now - staleLabelDate) / (1000 * 60 * 60 * 24);
if (!isTemplateIssue && lastCommitDate > staleLabelDate) {
await github.rest.issues.removeLabel({ owner, repo, issue_number: pr.number, name: "stale" }); // 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({ 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.` body: `@${author} Recent activity detected. Removing stale label.`
}); });
continue;
} }
// If 7 days have passed since stale label, close the PR
// A maintainer comment after the mark restarts the 7 days. else if (daysSinceStale > 7) {
const maintainerDates = humanComments await github.rest.pulls.update({
.filter(c => MAINTAINERS.includes(c.author_association) && c.user?.login !== author) owner,
.map(c => new Date(c.created_at)) repo,
.filter(d => d > staleLabelDate); pull_number: pr.number,
const clockStart = maintainerDates.length > 0 ? new Date(Math.max(...maintainerDates)) : staleLabelDate; state: "closed"
if (days(clockStart) <= 7) continue; });
await github.rest.issues.createComment({
await github.rest.issues.createComment({ owner,
owner, repo, issue_number: pr.number, repo,
body: isTemplateIssue issue_number: pr.number,
? `@${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.` body: `@${author} Closing stale PR due to inactivity (no commits for 7 days after stale label).`
: `@${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" });
} }
+1 -47
View File
@@ -577,63 +577,17 @@ Exercise vigilance regarding copycat or coat-tailing sites that seek to exploit
- #### 🐞 Bug Fixes - #### 🐞 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)) - 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 ### 💾 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: 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: 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: 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)) - 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)) - 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)) - 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)) - 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)) - 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 ## 2026-11-02
### 🚀 Updated Scripts ### 🚀 Updated Scripts
+1 -1
View File
@@ -48,7 +48,7 @@ function update_script() {
msg_ok "Built Cinephage" msg_ok "Built Cinephage"
msg_info "Updating Camoufox" 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_ok "Updated Camoufox"
msg_info "Starting Cinephage" msg_info "Starting Cinephage"
-11
View File
@@ -58,17 +58,6 @@ function update_script() {
$STD git pull origin main $STD git pull origin main
msg_ok "Updated Discourse" 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" 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_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 RUBY_VERSION="${RUBY_TARGET:-4.0.7}" RUBY_INSTALL_RAILS="false" setup_ruby
+3 -3
View File
@@ -149,13 +149,13 @@ EOF
PG_VERSION=$(ls /etc/postgresql/ 2>/dev/null | sort -V | tail -1) PG_VERSION=$(ls /etc/postgresql/ 2>/dev/null | sort -V | tail -1)
PG_VERSION=${PG_VERSION:-16} PG_VERSION=${PG_VERSION:-16}
[[ -f ~/.vchord_version ]] && mv ~/.vchord_version ~/.vectorchord [[ -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 # 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 # "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 # 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) smart_search;"
$STD sudo -u postgres psql -d immich -c "VACUUM (ANALYZE) face_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 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 vector UPDATE;"
$STD sudo -u postgres psql -d immich -c "ALTER EXTENSION vchord UPDATE;" $STD sudo -u postgres psql -d immich -c "ALTER EXTENSION vchord UPDATE;"
@@ -232,7 +232,7 @@ EOF
# plugins # plugins
cd "$SRC_DIR" cd "$SRC_DIR"
export MISE_TRUSTED_CONFIG_PATHS="$SRC_DIR"/mise.toml 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)" export MISE_LOCKFILE_MODE=merge MISE_LOCKFILE_PLATFORMS="linux-$(arch_resolve x64 arm64)"
mise_ok=0 mise_ok=0
for i in 1 2 3; do for i in 1 2 3; do
+1 -1
View File
@@ -47,7 +47,7 @@ function update_script() {
$STD apt install -y aspnetcore-runtime-10.0 $STD apt install -y aspnetcore-runtime-10.0
fi fi
RELEASE=$(curl -fsSL https://technitium.com/dns/ | grep -oP 'Version \K[\d.]+') RELEASE=$(get_latest_github_release "TechnitiumSoftware/DnsServer")
if [[ ! -f ~/.technitium || ${RELEASE} != "$(cat ~/.technitium 2>/dev/null)" ]]; then if [[ ! -f ~/.technitium || ${RELEASE} != "$(cat ~/.technitium 2>/dev/null)" ]]; then
fetch_and_deploy_from_url "https://download.technitium.com/dns/DnsServerPortable.tar.gz" /opt/technitium/dns fetch_and_deploy_from_url "https://download.technitium.com/dns/DnsServerPortable.tar.gz" /opt/technitium/dns
echo "${RELEASE}" >~/.technitium echo "${RELEASE}" >~/.technitium
-35
View File
@@ -30,41 +30,6 @@ function update_script() {
exit exit
fi 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 NODE_VERSION="26" NODE_MODULE="yarn,node-gyp" setup_nodejs
if check_for_gh_release "wikijs" "requarks/wiki-v2"; then if check_for_gh_release "wikijs" "requarks/wiki-v2"; then
+1 -1
View File
@@ -22,7 +22,7 @@ if [[ "${var_os:-}" == "alpine" ]]; then
var_disk="${var_disk:-1}" var_disk="${var_disk:-1}"
var_version="${var_version:-3.24}" var_version="${var_version:-3.24}"
else else
var_ram="${var_ram:-1024}" var_ram="${var_ram:-512}"
var_disk="${var_disk:-4}" var_disk="${var_disk:-4}"
var_version="${var_version:-13}" var_version="${var_version:-13}"
fi fi
+1 -1
View File
@@ -35,7 +35,7 @@ $STD npm prune --omit=dev
msg_ok "Built Cinephage" msg_ok "Built Cinephage"
msg_info "Installing Camoufox" 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_ok "Installed Camoufox"
msg_info "Configuring Cinephage" msg_info "Configuring Cinephage"
+1 -28
View File
@@ -25,36 +25,9 @@ $STD apt install -y \
gsfonts \ gsfonts \
brotli \ brotli \
nginx \ nginx \
redis-server \ 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
msg_ok "Installed Dependencies" 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" msg_info "Cloning Discourse"
$STD git clone --depth 1 https://github.com/discourse/discourse.git /opt/discourse $STD git clone --depth 1 https://github.com/discourse/discourse.git /opt/discourse
msg_ok "Cloned Discourse" msg_ok "Cloned Discourse"
+23 -30
View File
@@ -18,11 +18,6 @@ if is_debian && [[ "$(get_os_version_major)" -lt 13 ]]; then
exit 238 exit 238
fi 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" RELEASE="v3.3.5"
if lscpu | grep -q 'GenuineIntel'; then if lscpu | grep -q 'GenuineIntel'; then
echo "" echo ""
@@ -113,7 +108,6 @@ $STD apt install --no-install-recommends -y \
libhwy-dev \ libhwy-dev \
libwebp-dev \ libwebp-dev \
libaom-dev \ libaom-dev \
unzip \
ccache ccache
setup_deb822_repo \ setup_deb822_repo \
@@ -135,28 +129,6 @@ if [[ "$CTTYPE" == "0" && -d /dev/dri ]]; then
fi fi
msg_ok "Dependencies Installed" 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" MISE_ARCH="x64"
[[ "$(arch_resolve)" == "arm64" ]] && MISE_ARCH="arm64" [[ "$(arch_resolve)" == "arm64" ]] && MISE_ARCH="arm64"
fetch_and_deploy_gh_release "mise" "jdx/mise" "singlefile" "latest" "/usr/local/bin" "mise-v*-linux-${MISE_ARCH}" 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} ACTUAL_PG_VERSION=${ACTUAL_PG_VERSION:-16}
VCHORD_RELEASE="1.1.1" 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 sed -i "s/^#shared_preload.*/shared_preload_libraries = 'vchord.so'/" /etc/postgresql/${ACTUAL_PG_VERSION}/main/postgresql.conf
systemctl restart postgresql.service systemctl restart postgresql.service
@@ -375,6 +347,15 @@ libvips: $LIBVIPS_REVISION
EOF EOF
msg_ok "Custom Photo-processing Libraries Compiled Successfully" 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" 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)" PNPM_VERSION="$(jq -r '.packageManager | split("@")[1] | split("+")[0]' ${SRC_DIR}/package.json)"
export COREPACK_ENABLE_DOWNLOAD_PROMPT=0 export COREPACK_ENABLE_DOWNLOAD_PROMPT=0
@@ -415,7 +396,7 @@ cp -a web/build "$APP_DIR"/www
cp LICENSE "$APP_DIR" cp LICENSE "$APP_DIR"
cd "$SRC_DIR" cd "$SRC_DIR"
export MISE_TRUSTED_CONFIG_PATHS="$SRC_DIR"/mise.toml 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)" export MISE_LOCKFILE_MODE=merge MISE_LOCKFILE_PLATFORMS="linux-$(arch_resolve x64 arm64)"
mise_ok=0 mise_ok=0
for i in 1 2 3; do 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 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" "$APP_DIR"/upload
ln -s "$UPLOAD_DIR" "$ML_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" cd "$INSTALL_DIR"
ln -s "$GEO_DIR" "$APP_DIR" ln -s "$GEO_DIR" "$APP_DIR"
msg_ok "Installed GeoNames data"
# MickLesk temporary patch for HEIC thumbnail gen # MickLesk temporary patch for HEIC thumbnail gen
MEDIA_REPO_JS="/opt/immich/app/dist/repositories/media.repository.js" MEDIA_REPO_JS="/opt/immich/app/dist/repositories/media.repository.js"
+1 -1
View File
@@ -23,7 +23,7 @@ setup_deb822_repo \
$STD apt install -y aspnetcore-runtime-10.0 $STD apt install -y aspnetcore-runtime-10.0
msg_ok "Installed Dependencies" msg_ok "Installed Dependencies"
RELEASE=$(curl -fsSL https://technitium.com/dns/ | grep -oP 'Version \K[\d.]+') RELEASE=$(get_latest_github_release "TechnitiumSoftware/DnsServer")
fetch_and_deploy_from_url "https://download.technitium.com/dns/DnsServerPortable.tar.gz" /opt/technitium/dns fetch_and_deploy_from_url "https://download.technitium.com/dns/DnsServerPortable.tar.gz" /opt/technitium/dns
echo "${RELEASE}" >~/.technitium echo "${RELEASE}" >~/.technitium
+1 -5
View File
@@ -21,11 +21,7 @@ msg_info "Setup Wazuh"
curl -fsSL https://packages.wazuh.com/$RELEASE/wazuh-install.sh -o wazuh-install.sh curl -fsSL https://packages.wazuh.com/$RELEASE/wazuh-install.sh -o wazuh-install.sh
chmod +x wazuh-install.sh chmod +x wazuh-install.sh
if [ "$STD" = "silent" ]; then if [ "$STD" = "silent" ]; then
bash wazuh-install.sh -a >>~/wazuh-install.output || { 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
}
else else
bash wazuh-install.sh -a | tee -a ~/wazuh-install.output bash wazuh-install.sh -a | tee -a ~/wazuh-install.output
fi fi
+8 -32
View File
@@ -17,44 +17,26 @@ msg_info "Installing Dependencies"
$STD apt install -y git $STD apt install -y git
msg_ok "Installed Dependencies" 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_VERSION="17" setup_postgresql
PG_DB_NAME="wiki" PG_DB_USER="wikijs_user" setup_postgresql_db 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" "prebuild" "latest" "/opt/wikijs" "wiki-js.tar.gz" fetch_and_deploy_gh_release "wikijs" "requarks/wiki-v2" "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
msg_info "Configuring Wiki.js" msg_info "Configuring Wiki.js"
mkdir -p /opt/wikijs_data mv /opt/wikijs/config.sample.yml /opt/wikijs/config.yml
cp /opt/wikijs/config.sample.yml /opt/wikijs/config.yml sed -i -E 's|^( *user: ).*|\1'"$PG_DB_USER"'|' /opt/wikijs/config.yml
sed -i \ sed -i -E 's|^( *pass: ).*|\1'"$PG_DB_PASS"'|' /opt/wikijs/config.yml
-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
msg_ok "Configured Wiki.js" msg_ok "Configured Wiki.js"
msg_info "Creating Service" msg_info "Creating Service"
cat <<EOF >/etc/systemd/system/wikijs.service cat <<EOF >/etc/systemd/system/wikijs.service
[Unit] [Unit]
Description=Wiki.js Description=Wiki.js
After=network.target postgresql.service After=network.target
[Service] [Service]
Type=simple Type=simple
ExecStart=/usr/bin/node --no-experimental-webstorage backend ExecStart=/usr/bin/node server
Restart=always Restart=always
User=root User=root
Environment=NODE_ENV=production Environment=NODE_ENV=production
@@ -63,13 +45,7 @@ WorkingDirectory=/opt/wikijs
[Install] [Install]
WantedBy=multi-user.target WantedBy=multi-user.target
EOF EOF
systemctl set-environment ADMIN_PASS="${ADMIN_PASS}"
systemctl enable -q --now wikijs 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" msg_ok "Created Service"
motd_ssh motd_ssh
+5 -5
View File
@@ -36,17 +36,17 @@ cd /tmp
ZABBIX_REPO_DIR="debian" ZABBIX_REPO_DIR="debian"
[[ "$ZABBIX_VERSION" == "7.0" && "$(arch_resolve)" == "arm64" ]] && ZABBIX_REPO_DIR="debian-arm64" [[ "$ZABBIX_VERSION" == "7.0" && "$(arch_resolve)" == "arm64" ]] && ZABBIX_REPO_DIR="debian-arm64"
if [[ "$ZABBIX_VERSION" == "7.0" ]]; then 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_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}+debian$(get_os_version_major)_all.deb" ZABBIX_DEB_FILE="zabbix-release_latest_${ZABBIX_VERSION}+debian13_all.deb"
else 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_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+debian$(get_os_version_major)_all.deb" ZABBIX_DEB_FILE="zabbix-release_latest+debian13_all.deb"
fi fi
curl -fsSL "$ZABBIX_DEB_URL" -o /tmp/"$ZABBIX_DEB_FILE" curl -fsSL "$ZABBIX_DEB_URL" -o /tmp/"$ZABBIX_DEB_FILE"
$STD dpkg -i /tmp/"$ZABBIX_DEB_FILE" $STD dpkg -i /tmp/"$ZABBIX_DEB_FILE"
apt_update_safe 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 if [[ "$ZABBIX_VERSION" == "7.0" ]]; then
ZABBIX_SQL="/usr/share/zabbix-sql-scripts/postgresql/server.sql.gz" ZABBIX_SQL="/usr/share/zabbix-sql-scripts/postgresql/server.sql.gz"
-22
View File
@@ -65,28 +65,6 @@ while [[ -z "${CTID}" ]]; do
"${CTID_MENU[@]}" 3>&1 1>&2 2>&3) || exit 0 "${CTID_MENU[@]}" 3>&1 1>&2 2>&3) || exit 0
done 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" CTID_CONFIG_PATH="/etc/pve/lxc/${CTID}.conf"
# Skip if already configured # Skip if already configured
+6 -7
View File
@@ -70,10 +70,14 @@ function cleanup_ctid() {
header_info header_info
require_pve_host 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" msg_info "Updating template list"
pveam update >/dev/null 2>&1 pveam update >/dev/null 2>&1
msg_ok "Updated template list" 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=() TEMPLATE_MENU=()
MSG_MAX_LENGTH=0 MSG_MAX_LENGTH=0
HOST_ARCH=$(dpkg --print-architecture) 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 ((${#ITEM} + OFFSET > MSG_MAX_LENGTH)) && MSG_MAX_LENGTH=${#ITEM}+OFFSET
TEMPLATE_MENU+=("$ITEM" "$TAG " "OFF") TEMPLATE_MENU+=("$ITEM" "$TAG " "OFF")
done < <(pveam available) 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" ] && { [ -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 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" echo "Done"
@@ -181,11 +185,6 @@ msg_ok "Using '$TEMPLATE_STORAGE' for template storage."
CONTAINER_STORAGE=$(select_storage container) CONTAINER_STORAGE=$(select_storage container)
msg_ok "Using '$CONTAINER_STORAGE' for container storage." 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 # Download template
msg_info "Downloading LXC template (Patience)" msg_info "Downloading LXC template (Patience)"
pveam download "$TEMPLATE_STORAGE" "$TEMPLATE" >/dev/null || { pveam download "$TEMPLATE_STORAGE" "$TEMPLATE" >/dev/null || {
+3 -3
View File
@@ -42,7 +42,7 @@ require_debian_like
function uninstall() { function uninstall() {
msg_info "Uninstalling Homebrew" 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 if [[ -n "$BREW_USER" ]]; then
BREW_USER_HOME=$(getent passwd "$BREW_USER" | cut -d: -f6) BREW_USER_HOME=$(getent passwd "$BREW_USER" | cut -d: -f6)
for rc_file in "$BREW_USER_HOME/.bashrc" "$BREW_USER_HOME/.profile"; do for rc_file in "$BREW_USER_HOME/.bashrc" "$BREW_USER_HOME/.profile"; do
@@ -64,7 +64,7 @@ function uninstall() {
# ============================================================================== # ==============================================================================
function install() { function install() {
msg_info "Detecting Non-Root User" 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 if [[ -z "$BREW_USER" ]]; then
msg_warn "No non-root user found (uid >= 1000). Homebrew cannot run as root." 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 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_ok "Detected User: $BREW_USER"
msg_info "Installing Dependencies" msg_info "Installing Dependencies"
apt_update_safe $STD apt update
$STD apt install -y build-essential git file procps $STD apt install -y build-essential git file procps
msg_ok "Installed Dependencies" msg_ok "Installed Dependencies"
+2 -2
View File
@@ -80,7 +80,7 @@ install() {
msg_ok "Set up repository" msg_ok "Set up repository"
msg_info "Installing ${APP}" msg_info "Installing ${APP}"
apt_update_safe $STD apt update
$STD apt install -y netdata $STD apt install -y netdata
msg_ok "Installed ${APP}" msg_ok "Installed ${APP}"
msg_ok "Completed successfully!\n" msg_ok "Completed successfully!\n"
@@ -108,7 +108,7 @@ OPTIONS=(Install "Install ${APP} on Proxmox VE"
Uninstall "Uninstall ${APP} from Proxmox VE") Uninstall "Uninstall ${APP} from Proxmox VE")
CHOICE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "${APP}" \ 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 case $CHOICE in
"Install") install ;; "Install") install ;;
+1 -1
View File
@@ -5,7 +5,7 @@
# https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE
function header_info() { function header_info() {
clear 2>/dev/null || printf '\033[H\033[2J\033[3J' clear
cat <<"EOF" cat <<"EOF"
________ __ _ ________ ________ __ _ ________
/ ____/ /__ ____ _____ / / | |/ / ____/ / ____/ /__ ____ _____ / / | |/ / ____/
+1 -1
View File
@@ -19,7 +19,7 @@ CONF_FILE="/etc/update-lxcs.conf"
LOG_FILE="/var/log/update-lxcs-cron.log" LOG_FILE="/var/log/update-lxcs-cron.log"
CRON_ENTRY="0 0 * * 0 ${LOCAL_SCRIPT} >>${LOG_FILE} 2>&1" CRON_ENTRY="0 0 * * 0 ${LOCAL_SCRIPT} >>${LOG_FILE} 2>&1"
clear 2>/dev/null || printf '\033[H\033[2J\033[3J' clear
cat <<"EOF" cat <<"EOF"
______ __ __ __ __ __ _ ________ ______ __ __ __ __ __ _ ________
/ ____/________ ____ / / / /___ ____/ /___ _/ /____ / / | |/ / ____/____ / ____/________ ____ / / / /___ ____/ /___ _/ /____ / / | |/ / ____/____
+2 -2
View File
@@ -6,7 +6,7 @@
# https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE
function header_info() { function header_info() {
clear 2>/dev/null || printf '\033[H\033[2J\033[3J' clear
cat <<"EOF" cat <<"EOF"
______ __ __ _ ________ ______ __ __ _ ________
/ ____/ _____ _______ __/ /____ / / | |/ / ____/ / ____/ _____ _______ __/ /____ / / | |/ / ____/
@@ -67,7 +67,7 @@ for container in $(pct list | awk '{if(NR>1) print $1}'); do
else else
os=$(pct config "$container" | awk '/^ostype/ {print $2}') os=$(pct config "$container" | awk '/^ostype/ {print $2}')
if [ "$os" != "debian" ] && [ "$os" != "ubuntu" ]; then 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 continue
fi fi
+2 -3
View File
@@ -3,7 +3,7 @@
set -eEuo pipefail set -eEuo pipefail
function header_info() { function header_info() {
clear 2>/dev/null || printf '\033[H\033[2J\033[3J' clear
cat <<"EOF" cat <<"EOF"
_______ __ __ ______ _ _______ __ __ ______ _
/ ____(_) /__ _______ _______/ /____ ____ ___ /_ __/____(_)___ ___ / ____(_) /__ _______ _______/ /____ ____ ___ /_ __/____(_)___ ___
@@ -118,8 +118,7 @@ function trim_container() {
fi fi
local fstrim_output 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)
fstrim_output=$(pct fstrim "$container" 2>&1) || true
if echo "$fstrim_output" | grep -qi "not supported"; then if echo "$fstrim_output" | grep -qi "not supported"; then
echo -e "${RD}fstrim isnt supported on this storage!${CL}" echo -e "${RD}fstrim isnt supported on this storage!${CL}"
elif echo "$fstrim_output" | grep -Eq '([0-9]+(\.[0-9]+)?\s*[KMGT]?B)'; then elif echo "$fstrim_output" | grep -Eq '([0-9]+(\.[0-9]+)?\s*[KMGT]?B)'; then
+1 -1
View File
@@ -5,7 +5,7 @@
# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE
function header_info { function header_info {
clear 2>/dev/null || printf '\033[H\033[2J\033[3J' clear
cat <<"EOF" cat <<"EOF"
____ ______ __ ____ __ __ ____ ______ __ ____ __ __
/ __ \_________ _ ______ ___ ____ _ __ / ____/_ _____ _____/ /_ / __ \___ / /__ / /____ / __ \_________ _ ______ ___ ____ _ __ / ____/_ _____ _____/ /_ / __ \___ / /__ / /____
+1 -1
View File
@@ -9,7 +9,7 @@
set -e set -e
function header_info { function header_info {
clear 2>/dev/null || printf '\033[H\033[2J\033[3J' clear
cat <<"EOF" cat <<"EOF"
__ ___ __ ___ __ __ _ __ ___ __ ___ __ __ _
+2 -2
View File
@@ -6,7 +6,7 @@
# https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE
header_info() { header_info() {
clear 2>/dev/null || printf '\033[H\033[2J\033[3J' clear
cat <<"EOF" cat <<"EOF"
____ ____ __________ __ ______ __________ ___ ____ ______ ____ ____ __________ __ ______ __________ ___ ____ ______
/ __ \/ __ ) ___/__ / / / / / __ \/ ____/ __ \/ | / __ \/ ____/ / __ \/ __ ) ___/__ / / / / / __ \/ ____/ __ \/ | / __ \/ ____/
@@ -168,7 +168,7 @@ while true; do
case $yn in case $yn in
[Yy]*) break ;; [Yy]*) break ;;
[Nn]*) [Nn]*)
clear 2>/dev/null || printf '\033[H\033[2J\033[3J' clear
exit exit
;; ;;
*) echo "Please answer yes or no." ;; *) echo "Please answer yes or no." ;;
+2 -7
View File
@@ -5,7 +5,7 @@
# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE
header_info() { header_info() {
clear 2>/dev/null || printf '\033[H\033[2J\033[3J' clear
cat <<"EOF" cat <<"EOF"
____ ____ _____ __ __ __ __ __ ____ ____ _____ __ __ __ __ __
/ __ \/ __ ) ___// // / / / / /___ ____ __________ _____/ /__ / __ \/ __ ) ___// // / / / / /___ ____ __________ _____/ /__
@@ -192,18 +192,13 @@ while true; do
case $yn in case $yn in
[Yy]*) break ;; [Yy]*) break ;;
[Nn]*) [Nn]*)
clear 2>/dev/null || printf '\033[H\033[2J\033[3J' clear
exit exit
;; ;;
*) echo "Please answer yes or no." ;; *) echo "Please answer yes or no." ;;
esac esac
done 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 if [ "$(dpkg --print-architecture 2>/dev/null)" = "arm64" ]; then
header_info header_info
msg_error "This upgrade script targets the amd64 Proxmox repositories and is not supported on ARM64." msg_error "This upgrade script targets the amd64 Proxmox repositories and is not supported on ARM64."
+2 -2
View File
@@ -5,7 +5,7 @@
# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE
header_info() { header_info() {
clear 2>/dev/null || printf '\033[H\033[2J\033[3J' clear
cat <<"EOF" cat <<"EOF"
____ ____ _____ ____ __ ____ __ ____ ____ ____ _____ ____ __ ____ __ ____
/ __ \/ __ ) ___/ / __ \____ _____/ /_ / _/___ _____/ /_____ _/ / / / __ \/ __ ) ___/ / __ \____ _____/ /_ / _/___ _____/ /_____ _/ / /
@@ -89,7 +89,7 @@ main() {
case $yn in case $yn in
[Yy]*) break ;; [Yy]*) break ;;
[Nn]*) [Nn]*)
clear 2>/dev/null || printf '\033[H\033[2J\033[3J' clear
exit exit
;; ;;
*) echo "Please answer yes or no." ;; *) echo "Please answer yes or no." ;;
+2 -2
View File
@@ -5,7 +5,7 @@
# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE
header_info() { header_info() {
clear 2>/dev/null || printf '\033[H\033[2J\033[3J' clear
cat <<"EOF" cat <<"EOF"
____ ____ __ ___ ____ __ ____ __ ____ ____ ____ __ ___ ____ __ ____ __ ____
/ __ \/ __ \/ |/ / / __ \____ _____/ /_ / _/___ _____/ /_____ _/ / / / __ \/ __ \/ |/ / / __ \____ _____/ /_ / _/___ _____/ /_____ _/ / /
@@ -412,7 +412,7 @@ while true; do
case $yn in case $yn in
[Yy]*) break ;; [Yy]*) break ;;
[Nn]*) [Nn]*)
clear 2>/dev/null || printf '\033[H\033[2J\033[3J' clear
exit exit
;; ;;
*) echo "Please answer yes or no." ;; *) echo "Please answer yes or no." ;;
+2 -2
View File
@@ -5,7 +5,7 @@
# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE
header_info() { header_info() {
clear 2>/dev/null || printf '\033[H\033[2J\033[3J' clear
cat <<"EOF" cat <<"EOF"
____ __ _________ ____ __ ____ __ ____ ____ __ _________ ____ __ ____ __ ____
/ __ \/ |/ / ____/ / __ \____ _____/ /_ / _/___ _____/ /_____ _/ / / / __ \/ |/ / ____/ / __ \____ _____/ /_ / _/___ _____/ /_____ _/ / /
@@ -410,7 +410,7 @@ while true; do
case $yn in case $yn in
[Yy]*) break ;; [Yy]*) break ;;
[Nn]*) [Nn]*)
clear 2>/dev/null || printf '\033[H\033[2J\033[3J' clear
exit exit
;; ;;
*) echo "Please answer yes or no." ;; *) echo "Please answer yes or no." ;;
+5 -4
View File
@@ -6,7 +6,7 @@
# https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE
header_info() { header_info() {
clear 2>/dev/null || printf '\033[H\033[2J\033[3J' clear
cat <<"EOF" cat <<"EOF"
____ _ ________ ____ __ ____ __ ____ ____ _ ________ ____ __ ____ __ ____
/ __ \ | / / ____/ / __ \____ _____/ /_ / _/___ _____/ /_____ _/ / / / __ \ | / / ____/ / __ \____ _____/ /_ / _/___ _____/ /_____ _/ / /
@@ -81,7 +81,7 @@ main() {
case $yn in case $yn in
[Yy]*) break ;; [Yy]*) break ;;
[Nn]*) [Nn]*)
clear 2>/dev/null || printf '\033[H\033[2J\033[3J' clear
exit exit
;; ;;
*) echo "Please answer yes or no." ;; *) echo "Please answer yes or no." ;;
@@ -226,8 +226,9 @@ start_routines_9() {
[[ -n "$list_files" ]] && MSG+="$(echo "$list_files" | sed 's|^| - |')\n" [[ -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." 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?" \ whiptail --backtitle "Proxmox VE Helper Scripts" --title "Disable legacy sources?" \
--yesno "$MSG" 18 80; then --yesno "$MSG" 18 80
if [[ $? -eq 0 ]]; then
# Backup and disable sources.list # Backup and disable sources.list
if [[ -f "$listfile" ]] && grep -qE '^\s*deb ' "$listfile"; then if [[ -f "$listfile" ]] && grep -qE '^\s*deb ' "$listfile"; then
cp "$listfile" "$listfile.bak" cp "$listfile" "$listfile.bak"
+1 -1
View File
@@ -31,7 +31,7 @@ check_root() {
select_target_storage_and_container_id() { select_target_storage_and_container_id() {
echo -e "\nSelect target storage for restored container:\n" 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 for i in "${!target_storages[@]}"; do
printf "%s) %s\n" "$((i + 1))" "${target_storages[$i]}" printf "%s) %s\n" "$((i + 1))" "${target_storages[$i]}"
done done
+2 -2
View File
@@ -6,7 +6,7 @@
# https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE
header_info() { header_info() {
clear 2>/dev/null || printf '\033[H\033[2J\033[3J' clear
cat <<"EOF" cat <<"EOF"
____ _ ____________ __ ______ __________ ___ ____ ______ ____ _ ____________ __ ______ __________ ___ ____ ______
/ __ \ | / / ____( __ ) / / / / __ \/ ____/ __ \/ | / __ \/ ____/ / __ \ | / / ____( __ ) / / / / __ \/ ____/ __ \/ | / __ \/ ____/
@@ -118,7 +118,7 @@ while true; do
case $yn in case $yn in
[Yy]*) break ;; [Yy]*) break ;;
[Nn]*) [Nn]*)
clear 2>/dev/null || printf '\033[H\033[2J\033[3J' clear
exit exit
;; ;;
*) echo "Please answer yes or no." ;; *) echo "Please answer yes or no." ;;
+3 -3
View File
@@ -11,7 +11,7 @@ source <(curl -fsSL "${COMMUNITY_SCRIPTS_CORE_URL:-https://raw.githubusercontent
declare -f init_tool_telemetry &>/dev/null && init_tool_telemetry "scaling-governor" "pve" declare -f init_tool_telemetry &>/dev/null && init_tool_telemetry "scaling-governor" "pve"
header_info() { header_info() {
clear 2>/dev/null || printf '\033[H\033[2J\033[3J' clear
cat <<EOF cat <<EOF
________ __ __ _____ ________ __ __ _____
/ ___/ _ \/ / / / / ___/__ _ _____ _______ ___ _______ / ___/ _ \/ / / / / ___/__ _ _____ _______ ___ _______
@@ -26,7 +26,7 @@ GOV_BASE="/sys/devices/system/cpu/cpu0/cpufreq"
if [[ ! -r "$GOV_BASE/scaling_governor" || ! -r "$GOV_BASE/scaling_available_governors" ]]; then if [[ ! -r "$GOV_BASE/scaling_governor" || ! -r "$GOV_BASE/scaling_available_governors" ]]; then
whiptail --backtitle "Proxmox VE Helper Scripts" --title "CPU Scaling Not Available" \ whiptail --backtitle "Proxmox VE Helper Scripts" --title "CPU Scaling Not Available" \
--msgbox "CPU frequency scaling is not available on this system.\n\nThis is normal when no cpufreq driver is active (e.g. CPU power management handled by the BIOS, or certain virtualized hosts)." 12 70 --msgbox "CPU frequency scaling is not available on this system.\n\nThis is normal when no cpufreq driver is active (e.g. CPU power management handled by the BIOS, or certain virtualized hosts)." 12 70
clear 2>/dev/null || printf '\033[H\033[2J\033[3J' clear
exit 0 exit 0
fi fi
@@ -42,7 +42,7 @@ done < <(tr ' ' '\n' <"$GOV_BASE/scaling_available_governors" | sed '/^$/d' | gr
scaling_governor=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "Current CPU Scaling Governor is set to $current_governor" --radiolist "\nSelect the Scaling Governor to use:\n" 16 $((MSG_MAX_LENGTH + 58)) 6 "${GOVERNORS_MENU[@]}" 3>&1 1>&2 2>&3 | tr -d '"') scaling_governor=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "Current CPU Scaling Governor is set to $current_governor" --radiolist "\nSelect the Scaling Governor to use:\n" 16 $((MSG_MAX_LENGTH + 58)) 6 "${GOVERNORS_MENU[@]}" 3>&1 1>&2 2>&3 | tr -d '"')
[ -z "$scaling_governor" ] && { [ -z "$scaling_governor" ] && {
whiptail --backtitle "Proxmox VE Helper Scripts" --title "No CPU Scaling Governor Selected" --msgbox "It appears that no CPU Scaling Governor was selected" 10 68 whiptail --backtitle "Proxmox VE Helper Scripts" --title "No CPU Scaling Governor Selected" --msgbox "It appears that no CPU Scaling Governor was selected" 10 68
clear 2>/dev/null || printf '\033[H\033[2J\033[3J' clear
exit exit
} }
echo "${scaling_governor}" | tee /sys/devices/system/cpu/cpu*/cpufreq/scaling_governor >/dev/null echo "${scaling_governor}" | tee /sys/devices/system/cpu/cpu*/cpufreq/scaling_governor >/dev/null
+1 -1
View File
@@ -11,7 +11,7 @@ declare -f init_tool_telemetry &>/dev/null && init_tool_telemetry "storage-share
set -eEuo pipefail set -eEuo pipefail
function header_info() { function header_info() {
clear 2>/dev/null || printf '\033[H\033[2J\033[3J' clear
cat <<"EOF" cat <<"EOF"
_____ __ ___ _________ _____ __ ___ _________
/ ___// /_____ _________ _____ ____ / | / _/ __ \ / ___// /_____ _________ _____ ____ / | / _/ __ \
+13 -20
View File
@@ -271,22 +271,15 @@ function dry_run_container() {
fi 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() { function backup_container() {
msg_info "Creating backup for container $1" msg_info "Creating backup for container $1"
local output vzdump $1 --compress zstd --storage $STORAGE_CHOICE -notes-template "{{guestname}} - community-scripts backup updater" >/dev/null 2>&1
if output=$(vzdump $1 --compress zstd --storage $STORAGE_CHOICE -notes-template "{{guestname}} - community-scripts backup updater" 2>&1); then status=$?
if [ $status -eq 0 ]; then
msg_ok "Backup created" msg_ok "Backup created"
else else
log_write "Container $1: vzdump failed:"$'\n'"$output" msg_error "Backup failed for container $1"
msg_error "Backup failed for container $1: $(failure_reason "$output")"
exit 235 exit 235
fi fi
} }
@@ -344,7 +337,7 @@ function log_write() {
# Skip confirmation if var_skip_confirm is set to yes # Skip confirmation if var_skip_confirm is set to yes
if [[ "$var_skip_confirm" != "yes" ]]; then 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 fi
tags_formatted="${var_tags//|/, }" tags_formatted="${var_tags//|/, }"
@@ -467,11 +460,12 @@ if [ "$BACKUP_CHOICE" == "yes" ]; then
# Determine storage based on var_backup_storage # Determine storage based on var_backup_storage
if [[ -n "$var_backup_storage" ]]; then if [[ -n "$var_backup_storage" ]]; then
# Validate that the specified storage exists and supports backups # 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" STORAGE_CHOICE="$var_backup_storage"
msg_ok "Using backup storage: $STORAGE_CHOICE" msg_ok "Using backup storage: $STORAGE_CHOICE"
else 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 exit 119
fi fi
else else
@@ -648,7 +642,7 @@ for container in $CHOICE; do
log_result "$container" "$service" "SKIPPED" "Storage critically low on /boot (>80%)" log_result "$container" "$service" "SKIPPED" "Storage critically low on /boot (>80%)"
log_write "Container $container ($service): SKIPPED — storage critically low on /boot" log_write "Container $container ($service): SKIPPED — storage critically low on /boot"
elif [ "$BACKUP_CHOICE" == "yes" ]; then 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" log_write "Container $container ($service): FAILED (exit $exit_code) — attempting restore"
msg_info "Restoring LXC $container from backup ($STORAGE_CHOICE)" msg_info "Restoring LXC $container from backup ($STORAGE_CHOICE)"
pct stop $container pct stop $container
@@ -660,7 +654,7 @@ for container in $CHOICE; do
log_write "Container $container ($service): FAILED — no backup found for restore" log_write "Container $container ($service): FAILED — no backup found for restore"
exit 235 exit 235
fi 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=$? restorestatus=$?
if [ $restorestatus -eq 0 ]; then if [ $restorestatus -eq 0 ]; then
pct start $container 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_result "$container" "$service" "RESTORED" "Update failed (exit $exit_code) — restored from backup"
log_write "Container $container ($service): RESTORED from $BACKUP_ENTRY" log_write "Container $container ($service): RESTORED from $BACKUP_ENTRY"
else else
log_write "Container $container: pct restore failed:"$'\n'"$restore_output" msg_error "Restore failed for container $container"
msg_error "Restore failed for container $container: $(failure_reason "$restore_output")"
log_result "$container" "$service" "FAILED" "Update failed (exit $exit_code) — restore also failed" log_result "$container" "$service" "FAILED" "Update failed (exit $exit_code) — restore also failed"
log_write "Container $container ($service): FAILED — restore also failed" log_write "Container $container ($service): FAILED — restore also failed"
exit 235 exit 235
fi fi
else 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_result "$container" "$service" "FAILED" "Exit code $exit_code"
log_write "Container $container ($service): FAILED (exit $exit_code)" log_write "Container $container ($service): FAILED (exit $exit_code)"
if [[ "$var_continue_on_error" == "yes" ]]; then if [[ "$var_continue_on_error" == "yes" ]]; then
+1 -1
View File
@@ -6,7 +6,7 @@
# https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE
function header_info() { function header_info() {
clear 2>/dev/null || printf '\033[H\033[2J\033[3J' clear
cat <<"EOF" cat <<"EOF"
__ __ __ __ __ _ ________ __ __ __ __ __ _ ________
/ / / /___ ____/ /___ _/ /____ / / | |/ / ____/ / / / /___ ____/ /___ _/ /____ / / | |/ / ____/
+1 -1
View File
@@ -6,7 +6,7 @@
# https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE
function header_info { function header_info {
clear 2>/dev/null || printf '\033[H\033[2J\033[3J' clear
cat <<"EOF" cat <<"EOF"
__ __ __ __ ____ __ __ __ __ ____
/ / / /___ ____/ /___ _/ /____ / __ \___ ____ ____ / / / /___ ____/ /___ _/ /____ / __ \___ ____ ____
+22 -3
View File
@@ -24,9 +24,18 @@ THIN="discard=on,ssd=1,"
set -e set -e
trap 'error_handler $LINENO "$BASH_COMMAND"' ERR trap 'error_handler $LINENO "$BASH_COMMAND"' ERR
trap cleanup EXIT trap cleanup EXIT
trap 'post_update_to_api "failed" "130"' SIGINT trap 'post_update_to_api "failed" "INTERRUPTED"' SIGINT
trap 'post_update_to_api "failed" "143"' SIGTERM trap 'post_update_to_api "failed" "TERMINATED"' SIGTERM
trap 'post_update_to_api "failed" "129"; exit 129' SIGHUP
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) TEMP_DIR=$(mktemp -d)
pushd "$TEMP_DIR" >/dev/null pushd "$TEMP_DIR" >/dev/null
@@ -117,6 +126,16 @@ post_to_api_vm
vm_select_storage "$HN" 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" msg_info "Retrieving the URL for the ${APP} Qcow2 Disk Image"
ALMA_ARCH="$(vm_arch_resolve x86_64 aarch64)" 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" URL="https://repo.almalinux.org/almalinux/${var_version}/cloud/${ALMA_ARCH}/images/AlmaLinux-${var_version}-GenericCloud-latest.${ALMA_ARCH}.qcow2"
+7
View File
@@ -89,6 +89,13 @@ post_to_api_vm
vm_select_storage "$HN" 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" msg_info "Retrieving the URL for the Fedora Cloud Base image"
MIRROR="https://dl.fedoraproject.org/pub/fedora/linux/releases" 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) FEDORA_RELEASE=$(curl -fsSL "$MIRROR/" 2>/dev/null | grep -oP 'href="\K[0-9]+(?=/")' | sort -rn | head -1)
+1 -1
View File
@@ -259,7 +259,7 @@ post_to_api_vm
vm_select_storage "$HN" vm_select_storage "$HN"
msg_info "Getting URL for OpenWrt Disk Image" 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" 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" URL="https://downloads.openwrt.org/releases/$var_version/targets/x86/64/openwrt-$var_version-x86-64-generic-ext4-combined.img.gz"