Compare commits

...

3 Commits

Author SHA1 Message Date
CanbiZ (MickLesk)
a3b8312dee runs on vps 2026-02-25 11:02:46 +01:00
CanbiZ (MickLesk)
daaf1e09f6 fix typos in node_version 2026-02-25 10:36:32 +01:00
CanbiZ (MickLesk)
e7e78ae35d fix(workflow): improve Node.js version drift detection accuracy
1. Fix source URL regex: now captures 'Github: https://github.com/...'
   pattern (pipe-separated), not just '# Source: https://github.com/...'
   This was causing ~50 scripts to show 'No GitHub source'

2. Fix semver comparison: engines.node constraints like '>=18.0.0'
   no longer flag version 22 as drift. >= and ^ constraints are now
   properly evaluated (our_version >= min_major = satisfied)

3. Add fallback detection: when no Dockerfile or engines.node is found,
   check .nvmrc and .node-version files for Node version hints

4. Add subdirectory search: Dockerfile and package.json are now found
   via GitHub API tree search, not just in repo root

5. Use GitHub API to detect default branch instead of guessing
   main/master/dev with multiple HEAD requests
2026-02-25 10:35:20 +01:00
6 changed files with 137 additions and 22 deletions

View File

@@ -13,7 +13,7 @@ permissions:
jobs: jobs:
check-node-versions: check-node-versions:
if: github.repository == 'community-scripts/ProxmoxVE' if: github.repository == 'community-scripts/ProxmoxVE'
runs-on: ubuntu-latest runs-on: coolify-runner
steps: steps:
- name: Checkout Repository - name: Checkout Repository
@@ -110,22 +110,94 @@ jobs:
} }
# Extract Node major from engines.node in package.json # Extract Node major from engines.node in package.json
# Sets: ENGINES_NODE_RAW (raw string), ENGINES_MIN_MAJOR # Sets: ENGINES_NODE_RAW (raw string), ENGINES_MIN_MAJOR, ENGINES_IS_MINIMUM
extract_engines_node() { extract_engines_node() {
local content="$1" local content="$1"
ENGINES_NODE_RAW="" ENGINES_NODE_RAW=""
ENGINES_MIN_MAJOR="" ENGINES_MIN_MAJOR=""
ENGINES_IS_MINIMUM="false"
ENGINES_NODE_RAW=$(echo "$content" | jq -r '.engines.node // empty' 2>/dev/null || echo "") ENGINES_NODE_RAW=$(echo "$content" | jq -r '.engines.node // empty' 2>/dev/null || echo "")
if [[ -z "$ENGINES_NODE_RAW" ]]; then if [[ -z "$ENGINES_NODE_RAW" ]]; then
return return
fi fi
# Detect if constraint is a minimum (>=, ^) vs exact pinning
if [[ "$ENGINES_NODE_RAW" =~ ^(\>=|\^|\~) ]]; then
ENGINES_IS_MINIMUM="true"
fi
# Extract the first number (major) from the constraint # Extract the first number (major) from the constraint
# Handles: ">=24.13.1", "^22", ">=18.0.0", ">=18.15.0 <19 || ^20", etc. # Handles: ">=24.13.1", "^22", ">=18.0.0", ">=18.15.0 <19 || ^20", etc.
ENGINES_MIN_MAJOR=$(echo "$ENGINES_NODE_RAW" | grep -oP '\d+' | head -1 || echo "") ENGINES_MIN_MAJOR=$(echo "$ENGINES_NODE_RAW" | grep -oP '\d+' | head -1 || echo "")
} }
# Check if our_version satisfies an engines.node constraint
# Returns 0 if satisfied, 1 if not
# Usage: version_satisfies_engines "22" ">=18.0.0" "true"
version_satisfies_engines() {
local our="$1"
local min_major="$2"
local is_minimum="$3"
if [[ -z "$min_major" || -z "$our" ]]; then
return 1
fi
if [[ "$is_minimum" == "true" ]]; then
# >= or ^ constraint: our version must be >= min_major
if [[ "$our" -ge "$min_major" ]]; then
return 0
fi
fi
return 1
}
# Search for files in subdirectories via GitHub API tree
# Usage: find_repo_file "owner/repo" "branch" "filename" => sets REPLY to raw URL or empty
find_repo_file() {
local repo="$1"
local branch="$2"
local filename="$3"
REPLY=""
# Try root first (fast)
local root_url="https://raw.githubusercontent.com/${repo}/${branch}/${filename}"
if curl -sfI "$root_url" >/dev/null 2>&1; then
REPLY="$root_url"
return
fi
# Search via GitHub API tree (recursive)
local tree_url="https://api.github.com/repos/${repo}/git/trees/${branch}?recursive=1"
local tree_json
tree_json=$(curl -sf -H "Authorization: token $GH_TOKEN" "$tree_url" 2>/dev/null || echo "")
if [[ -z "$tree_json" ]]; then
return
fi
# Find first matching path (prefer shorter/root-level paths)
local match_path
match_path=$(echo "$tree_json" | jq -r --arg fn "$filename" \
'.tree[]? | select(.path | endswith("/" + $fn) or . == $fn) | .path' 2>/dev/null \
| sort | head -1 || echo "")
if [[ -n "$match_path" ]]; then
REPLY="https://raw.githubusercontent.com/${repo}/${branch}/${match_path}"
fi
}
# Extract Node major from .nvmrc or .node-version
# Sets: NVMRC_NODE_MAJOR
extract_nvmrc_node() {
local content="$1"
NVMRC_NODE_MAJOR=""
# .nvmrc/.node-version typically has: "v22.9.0", "22", "lts/iron", etc.
local ver
ver=$(echo "$content" | tr -d '[:space:]' | grep -oP '^v?\K[0-9]+' | head -1 || echo "")
NVMRC_NODE_MAJOR="$ver"
}
# Collect results # Collect results
declare -a issue_scripts=() declare -a issue_scripts=()
declare -a report_lines=() declare -a report_lines=()
@@ -143,7 +215,10 @@ jobs:
slug=$(basename "$script" | sed 's/-install\.sh$//') slug=$(basename "$script" | sed 's/-install\.sh$//')
# Extract Source URL (GitHub only) # Extract Source URL (GitHub only)
source_url=$(head -20 "$script" | grep -oP '(?<=# Source: )https://github\.com/[^\s]+' | head -1 || echo "") # Supports both:
# # Source: https://github.com/owner/repo
# # Source: https://example.com | Github: https://github.com/owner/repo
source_url=$(head -20 "$script" | grep -oP 'https://github\.com/[^\s|]+' | head -1 || echo "")
if [[ -z "$source_url" ]]; then if [[ -z "$source_url" ]]; then
report_lines+=("| \`$slug\` | — | — | — | — | ⏭️ No GitHub source |") report_lines+=("| \`$slug\` | — | — | — | — | ⏭️ No GitHub source |")
continue continue
@@ -167,12 +242,23 @@ jobs:
fi fi
fi fi
# Fetch upstream Dockerfile # Determine default branch via GitHub API (fast, single call)
detected_branch=""
api_default=$(curl -sf -H "Authorization: token $GH_TOKEN" \
"https://api.github.com/repos/${repo}" 2>/dev/null \
| jq -r '.default_branch // empty' 2>/dev/null || echo "")
if [[ -n "$api_default" ]]; then
detected_branch="$api_default"
else
detected_branch="main"
fi
# Fetch upstream Dockerfile (root + subdirectories)
df_content="" df_content=""
for branch in main master dev; do find_repo_file "$repo" "$detected_branch" "Dockerfile"
df_content=$(curl -sf "https://raw.githubusercontent.com/${repo}/${branch}/Dockerfile" 2>/dev/null || echo "") if [[ -n "$REPLY" ]]; then
[[ -n "$df_content" ]] && break df_content=$(curl -sf "$REPLY" 2>/dev/null || echo "")
done fi
DF_NODE_MAJOR="" DF_NODE_MAJOR=""
DF_SOURCE="" DF_SOURCE=""
@@ -180,19 +266,35 @@ jobs:
extract_dockerfile_node "$df_content" extract_dockerfile_node "$df_content"
fi fi
# Fetch upstream package.json # Fetch upstream package.json (root + subdirectories)
pkg_content="" pkg_content=""
for branch in main master dev; do find_repo_file "$repo" "$detected_branch" "package.json"
pkg_content=$(curl -sf "https://raw.githubusercontent.com/${repo}/${branch}/package.json" 2>/dev/null || echo "") if [[ -n "$REPLY" ]]; then
[[ -n "$pkg_content" ]] && break pkg_content=$(curl -sf "$REPLY" 2>/dev/null || echo "")
done fi
ENGINES_NODE_RAW="" ENGINES_NODE_RAW=""
ENGINES_MIN_MAJOR="" ENGINES_MIN_MAJOR=""
ENGINES_IS_MINIMUM="false"
if [[ -n "$pkg_content" ]]; then if [[ -n "$pkg_content" ]]; then
extract_engines_node "$pkg_content" extract_engines_node "$pkg_content"
fi fi
# Fallback: check .nvmrc or .node-version
NVMRC_NODE_MAJOR=""
if [[ -z "$DF_NODE_MAJOR" && -z "$ENGINES_MIN_MAJOR" ]]; then
for nvmfile in .nvmrc .node-version; do
find_repo_file "$repo" "$detected_branch" "$nvmfile"
if [[ -n "$REPLY" ]]; then
nvmrc_content=$(curl -sf "$REPLY" 2>/dev/null || echo "")
if [[ -n "$nvmrc_content" ]]; then
extract_nvmrc_node "$nvmrc_content"
[[ -n "$NVMRC_NODE_MAJOR" ]] && break
fi
fi
done
fi
# Determine upstream recommended major version # Determine upstream recommended major version
upstream_major="" upstream_major=""
upstream_hint="" upstream_hint=""
@@ -203,6 +305,9 @@ jobs:
elif [[ -n "$ENGINES_MIN_MAJOR" ]]; then elif [[ -n "$ENGINES_MIN_MAJOR" ]]; then
upstream_major="$ENGINES_MIN_MAJOR" upstream_major="$ENGINES_MIN_MAJOR"
upstream_hint="engines: $ENGINES_NODE_RAW" upstream_hint="engines: $ENGINES_NODE_RAW"
elif [[ -n "$NVMRC_NODE_MAJOR" ]]; then
upstream_major="$NVMRC_NODE_MAJOR"
upstream_hint=".nvmrc/.node-version"
fi fi
# Build display values # Build display values
@@ -214,13 +319,23 @@ jobs:
if [[ "$our_version" == "dynamic" ]]; then if [[ "$our_version" == "dynamic" ]]; then
status="🔄 Dynamic" status="🔄 Dynamic"
elif [[ "$our_version" == "unset" ]]; then elif [[ "$our_version" == "unset" ]]; then
status="⚠️ NODE_VERSION not set" if [[ -n "$upstream_major" ]]; then
status="⚠️ NODE_VERSION not set (upstream=$upstream_major via $upstream_hint)"
else
status="⚠️ NODE_VERSION not set (no upstream info found)"
fi
issue_scripts+=("$slug|$our_version|$upstream_major|$upstream_hint|$repo") issue_scripts+=("$slug|$our_version|$upstream_major|$upstream_hint|$repo")
drift_count=$((drift_count + 1)) drift_count=$((drift_count + 1))
elif [[ -n "$upstream_major" && "$our_version" != "$upstream_major" ]]; then elif [[ -n "$upstream_major" && "$our_version" != "$upstream_major" ]]; then
status="🔸 Drift → upstream=$upstream_major ($upstream_hint)" # Check if engines.node is a minimum constraint that our version satisfies
issue_scripts+=("$slug|$our_version|$upstream_major|$upstream_hint|$repo") if [[ -z "$DF_NODE_MAJOR" && "$ENGINES_IS_MINIMUM" == "true" ]] && \
drift_count=$((drift_count + 1)) version_satisfies_engines "$our_version" "$ENGINES_MIN_MAJOR" "$ENGINES_IS_MINIMUM"; then
status="✅ (engines: $ENGINES_NODE_RAW — ours: $our_version satisfies)"
else
status="🔸 Drift → upstream=$upstream_major ($upstream_hint)"
issue_scripts+=("$slug|$our_version|$upstream_major|$upstream_hint|$repo")
drift_count=$((drift_count + 1))
fi
fi fi
report_lines+=("| \`$slug\` | $our_version | $engines_display | $dockerfile_display | [$repo](https://github.com/$repo) | $status |") report_lines+=("| \`$slug\` | $our_version | $engines_display | $dockerfile_display | [$repo](https://github.com/$repo) | $status |")

View File

@@ -28,7 +28,7 @@ function update_script() {
exit exit
fi fi
NODE_VERSION=24 NODE_MODULE="yarn,npm,pm2" setup_nodejs NODE_VERSION="24" NODE_MODULE="yarn,npm,pm2" setup_nodejs
if check_for_gh_release "joplin-server" "laurent22/joplin"; then if check_for_gh_release "joplin-server" "laurent22/joplin"; then
msg_info "Stopping Services" msg_info "Stopping Services"

View File

@@ -29,7 +29,7 @@ function update_script() {
fi fi
if check_for_gh_release "Zigbee2MQTT" "Koenkk/zigbee2mqtt"; then if check_for_gh_release "Zigbee2MQTT" "Koenkk/zigbee2mqtt"; then
NODE_VERSION=24 NODE_MODULE="pnpm@$(curl -fsSL https://raw.githubusercontent.com/Koenkk/zigbee2mqtt/master/package.json | jq -r '.packageManager | split("@")[1]')" setup_nodejs NODE_VERSION="24" NODE_MODULE="pnpm@$(curl -fsSL https://raw.githubusercontent.com/Koenkk/zigbee2mqtt/master/package.json | jq -r '.packageManager | split("@")[1]')" setup_nodejs
msg_info "Stopping Service" msg_info "Stopping Service"
systemctl stop zigbee2mqtt systemctl stop zigbee2mqtt
msg_ok "Stopped Service" msg_ok "Stopped Service"

View File

@@ -39,7 +39,7 @@ $STD apt install -y \
texlive-xetex texlive-xetex
msg_ok "Installed Dependencies" msg_ok "Installed Dependencies"
NODE_VERSION=22 NODE_MODULE="bun" setup_nodejs NODE_VERSION="22" NODE_MODULE="bun" setup_nodejs
fetch_and_deploy_gh_release "ConvertX" "C4illin/ConvertX" "tarball" "latest" "/opt/convertx" fetch_and_deploy_gh_release "ConvertX" "C4illin/ConvertX" "tarball" "latest" "/opt/convertx"
msg_info "Installing ConvertX" msg_info "Installing ConvertX"

View File

@@ -21,7 +21,7 @@ msg_ok "Installed Dependencies"
PG_VERSION="17" setup_postgresql PG_VERSION="17" setup_postgresql
PG_DB_NAME="joplin" PG_DB_USER="joplin" setup_postgresql_db PG_DB_NAME="joplin" PG_DB_USER="joplin" setup_postgresql_db
NODE_VERSION=24 NODE_MODULE="yarn,npm,pm2" setup_nodejs NODE_VERSION="24" NODE_MODULE="yarn,npm,pm2" setup_nodejs
mkdir -p /opt/pm2 mkdir -p /opt/pm2
export PM2_HOME=/opt/pm2 export PM2_HOME=/opt/pm2
$STD pm2 install pm2-logrotate $STD pm2 install pm2-logrotate

View File

@@ -21,7 +21,7 @@ $STD apt install -y \
expect expect
msg_ok "Dependencies installed." msg_ok "Dependencies installed."
NODE_VERSION=24 setup_nodejs NODE_VERSION="24" setup_nodejs
fetch_and_deploy_gh_release "ProxmoxVE-Local" "community-scripts/ProxmoxVE-Local" "tarball" fetch_and_deploy_gh_release "ProxmoxVE-Local" "community-scripts/ProxmoxVE-Local" "tarball"
msg_info "Installing PVE Scripts local" msg_info "Installing PVE Scripts local"