ci: add weekly Node.js version drift check workflow

Scans all install scripts using setup_nodejs, fetches upstream
package.json (engines.node) and Dockerfile (FROM node:XX),
compares with our NODE_VERSION and opens/updates a GitHub issue
on mismatch. Runs weekly on Monday at 06:00 UTC.
This commit is contained in:
CanbiZ (MickLesk)
2026-02-24 11:03:54 +01:00
parent 500d9db558
commit 4815a44b91

200
.github/workflows/check-node-versions.yml generated vendored Normal file
View File

@@ -0,0 +1,200 @@
name: Check Node.js Version Drift
on:
workflow_dispatch:
schedule:
# Runs weekly on Monday at 06:00 UTC
- cron: "0 6 * * 1"
permissions:
contents: read
issues: write
env:
REPORT_FILE: node-version-report.md
jobs:
check-node-versions:
if: github.repository == 'community-scripts/ProxmoxVE'
runs-on: ubuntu-latest
steps:
- name: Checkout Repository
uses: actions/checkout@v4
with:
ref: main
- name: Check upstream Node.js versions
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
set -euo pipefail
echo "================================================"
echo " Checking Node.js version drift in install scripts"
echo "================================================"
# Collect results
declare -a mismatches=()
declare -a results=()
total=0
skipped=0
for script in install/*-install.sh; do
[[ ! -f "$script" ]] && continue
# Check if script uses setup_nodejs
if ! grep -q 'setup_nodejs' "$script"; then
continue
fi
total=$((total + 1))
slug=$(basename "$script" | sed 's/-install\.sh$//')
# Extract Source URL from header (lines 1-10)
source_url=$(head -20 "$script" | grep -oP '(?<=# Source: )https://github\.com/[^ ]+' | head -1)
if [[ -z "$source_url" ]]; then
skipped=$((skipped + 1))
results+=("| $slug | - | - | - | - | ⏭️ No GitHub source |")
continue
fi
# Extract owner/repo from URL
repo=$(echo "$source_url" | sed -E 's|https://github\.com/||; s|/$||; s|\.git$||')
if [[ -z "$repo" || "$repo" != */* ]]; then
skipped=$((skipped + 1))
results+=("| $slug | - | - | - | - | ⏭️ Invalid repo: $repo |")
continue
fi
# Extract current NODE_VERSION from script
# Handles: NODE_VERSION="22", NODE_VERSION="24", or dynamic (curl)
current_version=$(grep -oP 'NODE_VERSION="(\d+)"' "$script" | head -1 | grep -oP '\d+' || echo "")
if [[ -z "$current_version" ]]; then
# Check for dynamic version (e.g. curl-based)
if grep -q 'NODE_VERSION=\$(' "$script"; then
current_version="dynamic"
else
current_version="unset"
fi
fi
# Fetch upstream package.json engines.node
engines_node=""
pkg_raw=$(curl -sf "https://raw.githubusercontent.com/${repo}/HEAD/package.json" 2>/dev/null || echo "")
if [[ -n "$pkg_raw" ]]; then
engines_node=$(echo "$pkg_raw" | jq -r '.engines.node // empty' 2>/dev/null || echo "")
fi
# Fetch upstream Dockerfile FROM node:XX
dockerfile_node=""
for branch in main master; do
df_raw=$(curl -sf "https://raw.githubusercontent.com/${repo}/${branch}/Dockerfile" 2>/dev/null || echo "")
if [[ -n "$df_raw" ]]; then
# Extract node version from FROM node:XX or nodesource/setup_XX.x
dockerfile_node=$(echo "$df_raw" | grep -oP '(?<=FROM node:)\d+' | head -1 || echo "")
if [[ -z "$dockerfile_node" ]]; then
dockerfile_node=$(echo "$df_raw" | grep -oP '(?<=nodesource/setup_)\d+' | head -1 || echo "")
fi
break
fi
done
# Determine upstream recommended version
upstream_version=""
if [[ -n "$dockerfile_node" ]]; then
upstream_version="$dockerfile_node"
fi
# Parse engines.node for minimum major version
engines_min=""
if [[ -n "$engines_node" ]]; then
engines_min=$(echo "$engines_node" | grep -oP '\d+' | head -1 || echo "")
fi
# Determine status
status="✅"
if [[ "$current_version" == "dynamic" ]]; then
status="🔄 Dynamic"
elif [[ "$current_version" == "unset" ]]; then
status="⚠️ No NODE_VERSION set"
mismatches+=("$slug")
elif [[ -n "$upstream_version" && "$current_version" != "$upstream_version" ]]; then
status="🔸 Drift (upstream: $upstream_version)"
mismatches+=("$slug")
elif [[ -n "$engines_min" && "$engines_node" == ">="* ]]; then
# Check if engines requires a higher version than what we use
if [[ "$engines_min" -gt "$current_version" ]]; then
status="🔴 Below minimum (engines: $engines_node)"
mismatches+=("$slug")
fi
fi
engines_display="${engines_node:-—}"
dockerfile_display="${dockerfile_node:-—}"
results+=("| $slug | $current_version | $engines_display | $dockerfile_display | $repo | $status |")
done
# Build report
{
echo "## Node.js Version Drift Report"
echo ""
echo "Generated: $(date -u +%Y-%m-%dT%H:%M:%SZ)"
echo "Scripts checked: $total | Skipped: $skipped | Mismatches: ${#mismatches[@]}"
echo ""
echo "| Script | Our Version | engines.node | Dockerfile | Upstream Repo | Status |"
echo "|--------|-------------|-------------|------------|---------------|--------|"
printf '%s\n' "${results[@]}" | sort
echo ""
if [[ ${#mismatches[@]} -gt 0 ]]; then
echo "### Scripts requiring attention"
echo ""
for m in "${mismatches[@]}"; do
echo "- \`$m\`"
done
else
echo "All Node.js versions are in sync with upstream. ✅"
fi
} > "$REPORT_FILE"
cat "$REPORT_FILE"
# Export for next steps
echo "mismatch_count=${#mismatches[@]}" >> "$GITHUB_OUTPUT"
echo "total=$total" >> "$GITHUB_OUTPUT"
id: check
- name: Create or update issue on drift
if: steps.check.outputs.mismatch_count != '0'
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
TITLE="[Automated] Node.js version drift detected"
BODY=$(cat "$REPORT_FILE")
# Check if issue already exists
EXISTING=$(gh issue list --label "automated,dependencies" --search "$TITLE" --state open --json number --jq '.[0].number // empty')
if [[ -n "$EXISTING" ]]; then
gh issue edit "$EXISTING" --body "$BODY"
echo "Updated existing issue #$EXISTING"
else
gh issue create \
--title "$TITLE" \
--body "$BODY" \
--label "automated,dependencies"
echo "Created new issue"
fi
- name: Close issue if no drift
if: steps.check.outputs.mismatch_count == '0'
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
TITLE="[Automated] Node.js version drift detected"
EXISTING=$(gh issue list --label "automated,dependencies" --search "$TITLE" --state open --json number --jq '.[0].number // empty')
if [[ -n "$EXISTING" ]]; then
gh issue close "$EXISTING" --comment "All Node.js versions are now in sync with upstream. Closing automatically."
echo "Closed issue #$EXISTING"
fi