mirror of
https://github.com/community-scripts/ProxmoxVE.git
synced 2026-02-25 14:35:56 +01:00
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:
200
.github/workflows/check-node-versions.yml
generated
vendored
Normal file
200
.github/workflows/check-node-versions.yml
generated
vendored
Normal 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
|
||||
Reference in New Issue
Block a user