Compare commits

..

1 Commits

Author SHA1 Message Date
Michel Roegl-Brunner
cea197fc3e fix(workflow): only flag node drift when local is behind upstream
Update the node version drift check to count drift only when our script version is lower than upstream, so newer local versions no longer create false-positive drift issues.
2026-06-02 09:00:53 +02:00
17 changed files with 80 additions and 145 deletions

View File

@@ -3,7 +3,7 @@ name: Close Unauthorized New Script PRs
on:
pull_request_target:
branches: ["main"]
types: [opened, labeled, reopened, synchronize]
types: [opened, labeled]
jobs:
check-new-script:
@@ -24,6 +24,13 @@ jobs:
const owner = context.repo.owner;
const repo = context.repo.repo;
// --- Only act on PRs with the "new script" label ---
const labels = pr.labels.map(l => l.name);
if (!labels.includes("new script")) {
core.info(`PR #${prNumber} does not have "new script" label — skipping.`);
return;
}
// --- Allow our bots ---
const allowedBots = [
"push-app-to-main[bot]",
@@ -35,40 +42,38 @@ jobs:
return;
}
// --- Exempt contributors via author_association ---
// OWNER/MEMBER/COLLABORATOR are trusted; CONTRIBUTOR ("has merged before")
// and NONE are not — their new-script PRs are still closed.
const association = pr.author_association;
const exempt = ["OWNER", "MEMBER", "COLLABORATOR"];
if (exempt.includes(association)) {
core.info(`PR #${prNumber} by ${association} "${author}" — skipping.`);
return;
}
// --- Check if author is a member of the contributor team ---
const teamSlug = "contributor";
let isMember = false;
// --- Detect a new-script PR: "new script" label OR a newly-added
// script file under ct/ install/ turnkey/ vm/ (mirrors
// autolabeler-config.json). Removes the label-timing dependency. ---
const labels = pr.labels.map(l => l.name);
const hasNewScriptLabel = labels.includes("new script");
const scriptPrefixes = ["ct/", "install/", "turnkey/", "vm/"];
let hasAddedScriptFile = false;
try {
const files = await github.paginate(github.rest.pulls.listFiles, {
owner,
repo,
pull_number: prNumber,
per_page: 100,
const { status } = await github.rest.teams.getMembershipForUserInOrg({
org: owner,
team_slug: teamSlug,
username: author,
});
hasAddedScriptFile = files.some(
f => f.status === "added" && scriptPrefixes.some(p => f.filename.startsWith(p))
);
// status 200 means the user is a member (active or pending)
isMember = true;
} catch (error) {
core.warning(`Could not list files for PR #${prNumber}: ${error.message}`);
if (error.status === 404) {
isMember = false;
} else {
core.warning(`Could not check team membership for ${author}: ${error.message}`);
// Fallback: check org membership
try {
await github.rest.orgs.checkMembershipForUser({
org: owner,
username: author,
});
isMember = true;
} catch {
isMember = false;
}
}
}
if (!hasNewScriptLabel && !hasAddedScriptFile) {
core.info(`PR #${prNumber} is not a new-script submission (no label, no added script file) — skipping.`);
if (isMember) {
core.info(`PR #${prNumber} by contributor "${author}" — skipping.`);
return;
}

View File

@@ -56,57 +56,46 @@ jobs:
echo "$slugs" > pocketbase_slugs.txt
echo "count=$(echo $slugs | wc -w)" >> "$GITHUB_OUTPUT"
- name: Find matching issues in ProxmoxVED by slug
- name: Search for Issues with Similar Titles
id: find_issue
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
if [[ ! -s pocketbase_slugs.txt ]]; then
echo "No slugs derived from PR — nothing to match."
echo "issue_numbers=" >> "$GITHUB_OUTPUT"
exit 0
fi
slugs=$(cat pocketbase_slugs.txt)
issues=$(gh issue list --repo community-scripts/ProxmoxVED --json number,title --jq '.[] | {number, title}')
# Normalize: lowercase, strip spaces and hyphens (same shape as the slug derivation)
norm() { echo "$1" | tr '[:upper:]' '[:lower:]' | sed 's/[[:space:]]//g; s/-//g'; }
best_match_score=0
best_match_number=0
issues=$(gh issue list --repo community-scripts/ProxmoxVED --state open --limit 1000 --json number,title,body)
for issue in $(echo "$issues" | jq -r '. | @base64'); do
_jq() {
echo ${issue} | base64 --decode | jq -r ${1}
}
matched=""
for slug in $slugs; do
nslug=$(norm "$slug")
[[ -z "$nslug" ]] && continue
while IFS= read -r row; do
num=$(echo "$row" | jq -r '.number')
ntitle=$(norm "$(echo "$row" | jq -r '.title')")
body=$(echo "$row" | jq -r '.body // ""' | tr '[:upper:]' '[:lower:]')
# Match when the issue title contains the slug, or the body mentions it verbatim
if [[ "$ntitle" == *"$nslug"* ]] || [[ "$body" == *"$slug"* ]]; then
matched="$matched $num"
fi
done < <(echo "$issues" | jq -c '.[]')
issue_title=$(_jq '.title' | tr '[:upper:]' '[:lower:]' | sed 's/ //g' | sed 's/-//g')
issue_number=$(_jq '.number')
match_score=$(echo "$title" | grep -o "$issue_title" | wc -l)
if [ "$match_score" -gt "$best_match_score" ]; then
best_match_score=$match_score
best_match_number=$issue_number
fi
done
matched=$(echo $matched | xargs -n1 2>/dev/null | sort -un | tr '\n' ' ')
if [[ -z "$matched" ]]; then
echo "No matching ProxmoxVED issues found for slugs: $slugs"
echo "issue_numbers=" >> "$GITHUB_OUTPUT"
if [ "$best_match_number" != "0" ]; then
echo "issue_number=$best_match_number" >> $GITHUB_ENV
else
echo "No matching issue found."
exit 0
fi
echo "Matched ProxmoxVED issues: $matched"
echo "issue_numbers=$matched" >> "$GITHUB_OUTPUT"
- name: Comment on and close matching ProxmoxVED issues
if: steps.find_issue.outputs.issue_numbers != ''
- name: Comment on the Best-Matching Issue and Close It
if: env.issue_number != ''
env:
GH_TOKEN: ${{ secrets.PAT_MICHEL }}
run: |
for issue_number in ${{ steps.find_issue.outputs.issue_numbers }}; do
echo "Closing ProxmoxVED issue #$issue_number"
gh issue comment "$issue_number" --repo community-scripts/ProxmoxVED --body "Merged with #${{ github.event.pull_request.number }} in ProxmoxVE"
gh issue close "$issue_number" --repo community-scripts/ProxmoxVED
done
gh issue comment $issue_number --repo community-scripts/ProxmoxVED --body "Merged with #${{ github.event.pull_request.number }} in ProxmoxVE"
gh issue close $issue_number --repo community-scripts/ProxmoxVED
- name: Set is_dev to false in PocketBase
if: steps.get_slugs.outputs.count != '0'

4
.github/workflows/lock-issue.yaml generated vendored
View File

@@ -17,7 +17,7 @@ jobs:
uses: actions/github-script@v7
with:
script: |
const daysBeforeLock = 7;
const daysBeforeLock = 3;
const lockDate = new Date();
lockDate.setDate(lockDate.getDate() - daysBeforeLock);
@@ -28,7 +28,7 @@ jobs:
/dependabot/i
];
// Search for closed, unlocked issues older than 7 days (paginated, oldest first)
// Search for closed, unlocked issues older than 3 days (paginated, oldest first)
let page = 1;
let totalLocked = 0;

24
.github/workflows/pocketbase-bot.yml generated vendored
View File

@@ -13,8 +13,8 @@ jobs:
pocketbase-bot:
runs-on: self-hosted
# Act on comments that contain a /pocketbase command line (precise line check happens in-script)
if: contains(github.event.comment.body, '/pocketbase')
# Only act on /pocketbase commands
if: startsWith(github.event.comment.body, '/pocketbase')
steps:
- name: Execute PocketBase bot command
@@ -257,22 +257,6 @@ jobs:
if (!res.ok) console.warn('Could not post comment:', res.body);
}
// ── Locate the command line ────────────────────────────────────────
// Accept /pocketbase at the start of ANY line (leading whitespace ok),
// so the command works even when preceded by other text. Mid-sentence
// mentions and blockquoted ("> ...") examples are ignored.
const commentBody = process.env.COMMENT_BODY || '';
const cmdLine = commentBody
.split('\n')
.map(l => l.trim())
.find(l => l.startsWith('/pocketbase'));
if (!cmdLine) {
console.log('No /pocketbase command line found — ignoring comment.');
process.exit(0);
}
const withoutCmd = cmdLine.replace(/^\/pocketbase\s*/, '').trim();
// ── Permission check ───────────────────────────────────────────────
const association = process.env.ACTOR_ASSOCIATION;
if (association !== 'OWNER' && association !== 'MEMBER') {
@@ -288,6 +272,10 @@ jobs:
await addReaction('eyes');
// ── Parse command ──────────────────────────────────────────────────
const commentBody = process.env.COMMENT_BODY || '';
const lines = commentBody.trim().split('\n');
const firstLine = lines[0].trim();
const withoutCmd = firstLine.replace(/^\/pocketbase\s+/, '').trim();
function extractCodeBlock(body) {
const m = body.match(/```[^\n]*\n([\s\S]*?)```/);

View File

@@ -470,32 +470,6 @@ Exercise vigilance regarding copycat or coat-tailing sites that seek to exploit
</details>
## 2026-06-02
### 🚀 Updated Scripts
- #### 🐞 Bug Fixes
- infisical: fix update abort due to creds field mismatch (#14868) [@michelroegl-brunner](https://github.com/michelroegl-brunner) ([#14870](https://github.com/community-scripts/ProxmoxVE/pull/14870))
- #### ✨ New Features
- feat(degoog): enable default valkey cache integration [@michelroegl-brunner](https://github.com/michelroegl-brunner) ([#14871](https://github.com/community-scripts/ProxmoxVE/pull/14871))
- #### 🔧 Refactor
- chore: bump Node version in selected scripts [@michelroegl-brunner](https://github.com/michelroegl-brunner) ([#14873](https://github.com/community-scripts/ProxmoxVE/pull/14873))
### 💾 Core
- #### ✨ New Features
- tools.func: add support for Rust installation profile in setup_rust [@MickLesk](https://github.com/MickLesk) ([#14872](https://github.com/community-scripts/ProxmoxVE/pull/14872))
### 📂 Github
- fix(workflow): only flag node drift when local is behind upstream [@michelroegl-brunner](https://github.com/michelroegl-brunner) ([#14874](https://github.com/community-scripts/ProxmoxVE/pull/14874))
## 2026-06-01
### 🚀 Updated Scripts

View File

@@ -25,7 +25,7 @@ function update_script() {
check_container_storage
check_container_resources
NODE_VERSION="26" setup_nodejs
NODE_VERSION="24" setup_nodejs
ensure_dependencies build-essential
if command -v cross-seed &>/dev/null; then

View File

@@ -49,21 +49,11 @@ function update_script() {
msg_ok "Installed Bun"
fi
msg_info "Updating Valkey"
ensure_dependencies valkey
msg_ok "Updated Valkey"
CLEAN_INSTALL=1 fetch_and_deploy_gh_release "degoog" "fccview/degoog" "prebuild" "latest" "/opt/degoog" "degoog_*_prebuild.tar.gz"
msg_info "Restoring Configuration & Data"
[[ -f /opt/degoog.env.bak ]] && mv /opt/degoog.env.bak /opt/degoog/.env
[[ -d /opt/degoog_data_backup ]] && mv /opt/degoog_data_backup /opt/degoog/data
if [[ -f /opt/degoog/.env ]]; then
grep -q "^DEGOOG_VALKEY_URL=" /opt/degoog/.env && sed -i "s|^DEGOOG_VALKEY_URL=.*|DEGOOG_VALKEY_URL=redis://valkey:6379|" /opt/degoog/.env || echo "DEGOOG_VALKEY_URL=redis://valkey:6379" >>/opt/degoog/.env
grep -q "^DEGOOG_CACHE_MAX_ENTRIES=" /opt/degoog/.env && sed -i "s|^DEGOOG_CACHE_MAX_ENTRIES=.*|DEGOOG_CACHE_MAX_ENTRIES=1000|" /opt/degoog/.env || echo "DEGOOG_CACHE_MAX_ENTRIES=1000" >>/opt/degoog/.env
grep -q "^DEGOOG_CACHE_TTL_MS=" /opt/degoog/.env && sed -i "s|^DEGOOG_CACHE_TTL_MS=.*|DEGOOG_CACHE_TTL_MS=43200000|" /opt/degoog/.env || echo "DEGOOG_CACHE_TTL_MS=43200000" >>/opt/degoog/.env
fi
msg_ok "Restored Configuration & Data"
msg_info "Starting Service"

View File

@@ -35,7 +35,7 @@ function update_script() {
msg_info "Creating backup"
[[ -f /opt/infisical_backup.sql ]] && rm -f /opt/infisical_backup.sql
DB_PASS=$(grep -Po '(?<=^Password:\s).*' ~/infisical.creds | head -n1)
DB_PASS=$(grep -Po '(?<=^Database Password:\s).*' ~/infisical.creds | head -n1)
PGPASSWORD=$DB_PASS pg_dump -U infisical -h localhost -d infisical_db > /opt/infisical_backup.sql
msg_ok "Created backup"

View File

@@ -53,7 +53,7 @@ function update_script() {
[[ -s /opt/koillection/.env.local && -n "$(tail -c 1 /opt/koillection/.env.local)" ]] && echo "" >>/opt/koillection/.env.local
echo 'APP_RUNTIME="Symfony\Component\Runtime\SymfonyRuntime"' >>/opt/koillection/.env.local
fi
NODE_VERSION="26" NODE_MODULE="yarn" setup_nodejs
export COMPOSER_ALLOW_SUPERUSER=1
export APP_RUNTIME='Symfony\Component\Runtime\SymfonyRuntime'
$STD composer install --no-dev -o --no-interaction --classmap-authoritative

View File

@@ -42,7 +42,7 @@ function update_script() {
PYTHON_VERSION="3.13" setup_uv
CLEAN_INSTALL=1 fetch_and_deploy_gh_release "musicseerr" "HabiRabbu/Musicseerr" "tarball"
NODE_VERSION="25" NODE_MODULE="pnpm@10.33.0" setup_nodejs
NODE_VERSION="22" NODE_MODULE="pnpm@10.33.0" setup_nodejs
msg_info "Building Frontend"
cd /opt/musicseerr/frontend

View File

@@ -30,7 +30,7 @@ function update_script() {
exit
fi
NODE_VERSION="24" setup_nodejs
NODE_VERSION="22" setup_nodejs
if check_for_gh_release "soulsync" "Nezreka/SoulSync"; then
msg_info "Stopping Service"

View File

@@ -17,7 +17,7 @@ msg_info "Installing Dependencies"
$STD apt install -y build-essential
msg_ok "Installed Dependencies"
NODE_VERSION="26" setup_nodejs
NODE_VERSION="24" setup_nodejs
msg_info "Setup Cross-Seed"
$STD npm install cross-seed@latest -g

View File

@@ -16,8 +16,7 @@ update_os
msg_info "Installing Dependencies"
$STD apt install -y \
git \
unzip \
valkey
unzip
msg_ok "Installed Dependencies"
msg_info "Installing Bun"
@@ -39,9 +38,6 @@ DEGOOG_PLUGINS_DIR=/opt/degoog/data/plugins
DEGOOG_THEMES_DIR=/opt/degoog/data/themes
DEGOOG_ALIASES_FILE=/opt/degoog/data/aliases.json
DEGOOG_PLUGIN_SETTINGS_FILE=/opt/degoog/data/plugin-settings.json
DEGOOG_VALKEY_URL=redis://valkey:6379
DEGOOG_CACHE_MAX_ENTRIES=1000
DEGOOG_CACHE_TTL_MS=43200000
# DEGOOG_SETTINGS_PASSWORDS=changeme
# DEGOOG_PUBLIC_INSTANCE=false
# LOGGER=debug
@@ -66,16 +62,11 @@ EOF
fi
msg_ok "Set up degoog"
msg_info "Starting Valkey Service"
systemctl enable -q --now valkey-server
msg_ok "Started Valkey Service"
msg_info "Creating Service"
cat <<EOF >/etc/systemd/system/degoog.service
[Unit]
Description=degoog
After=network.target valkey-server.service
Wants=valkey-server.service
After=network.target
[Service]
Type=simple

View File

@@ -13,7 +13,7 @@ setting_up_container
network_check
update_os
NODE_VERSION="26" NODE_MODULE="yarn" setup_nodejs
NODE_VERSION="24" NODE_MODULE="yarn" setup_nodejs
PG_VERSION="16" setup_postgresql
PHP_VERSION="8.5" PHP_APACHE="YES" setup_php
setup_composer

View File

@@ -15,7 +15,7 @@ update_os
PYTHON_VERSION="3.13" setup_uv
fetch_and_deploy_gh_release "musicseerr" "HabiRabbu/Musicseerr" "tarball"
NODE_VERSION="25" NODE_MODULE="pnpm@10.33.0" setup_nodejs
NODE_VERSION="22" NODE_MODULE="pnpm@10.33.0" setup_nodejs
msg_info "Building Frontend"
cd /opt/musicseerr/frontend

View File

@@ -23,7 +23,7 @@ $STD apt install -y \
msg_ok "Installed Dependencies"
UV_PYTHON="3.11" setup_uv
NODE_VERSION="24" setup_nodejs
NODE_VERSION="22" setup_nodejs
fetch_and_deploy_gh_release "soulsync" "Nezreka/SoulSync" "tarball"

View File

@@ -8227,13 +8227,11 @@ setup_ruby() {
#
# Variables:
# RUST_TOOLCHAIN - Rust toolchain to install (default: stable)
# RUST_PROFILE - Rust installation profile (default: default, e.g. minimal)
# RUST_CRATES - Comma-separated list of crates (e.g. "cargo-edit,wasm-pack@0.12.1")
# ------------------------------------------------------------------------------
setup_rust() {
local RUST_TOOLCHAIN="${RUST_TOOLCHAIN:-stable}"
local RUST_PROFILE="${RUST_PROFILE:-default}"
local RUST_CRATES="${RUST_CRATES:-}"
local CARGO_BIN="${HOME}/.cargo/bin"
@@ -8245,8 +8243,8 @@ setup_rust() {
# Scenario 1: Rustup not installed - fresh install
if ! command -v rustup &>/dev/null; then
msg_info "Setup Rust ($RUST_TOOLCHAIN, profile: $RUST_PROFILE)"
curl -fsSL https://sh.rustup.rs | $STD sh -s -- -y --profile "$RUST_PROFILE" --default-toolchain "$RUST_TOOLCHAIN" || {
msg_info "Setup Rust ($RUST_TOOLCHAIN)"
curl -fsSL https://sh.rustup.rs | $STD sh -s -- -y --default-toolchain "$RUST_TOOLCHAIN" || {
msg_error "Failed to install Rust"
msg_error "Hint: Check connectivity to sh.rustup.rs and static.rust-lang.org"
return 7