From c3c5f148b7669ea360b6f823c18a5343f15cf1ae Mon Sep 17 00:00:00 2001 From: Tom Moor Date: Wed, 1 Apr 2026 19:47:40 -0400 Subject: [PATCH] Add Node LTS auto-update script (#11927) * Add Node LTS auto-update script * fix: Validate LTS version and update CI step name in Node update workflow Add semver validation for the fetched LTS version to prevent creating PRs with invalid node versions (e.g. null) if the upstream API changes. Also update the human-readable step name in ci.yml during major bumps. Co-Authored-By: Claude Opus 4.6 --------- Co-authored-by: Claude Opus 4.6 --- .github/workflows/update-node.yml | 94 +++++++++++++++++++++++++++++++ 1 file changed, 94 insertions(+) create mode 100644 .github/workflows/update-node.yml diff --git a/.github/workflows/update-node.yml b/.github/workflows/update-node.yml new file mode 100644 index 0000000000..beef80ed35 --- /dev/null +++ b/.github/workflows/update-node.yml @@ -0,0 +1,94 @@ +name: Update Node.js LTS + +on: + schedule: + # Run every Monday at 9:00 UTC + - cron: "0 9 * * 1" + workflow_dispatch: + +permissions: + contents: write + pull-requests: write + +jobs: + update-node: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v5 + + - name: Check for Node.js LTS update + id: check + run: | + # Get current Node version from Dockerfile + CURRENT_VERSION=$(grep -oP 'FROM node:\K[0-9]+\.[0-9]+\.[0-9]+' Dockerfile.base) + echo "current=$CURRENT_VERSION" >> "$GITHUB_OUTPUT" + echo "Current Node.js version: $CURRENT_VERSION" + + # Fetch the latest LTS release (any major version) from nodejs.org + LATEST_VERSION=$(curl -s https://nodejs.org/dist/index.json | \ + jq -r '[.[] | select(.lts != false)][0].version' | \ + sed 's/^v//') + + if ! [[ "$LATEST_VERSION" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then + echo "::error::Failed to fetch a valid LTS version (got '$LATEST_VERSION')" + exit 1 + fi + + echo "latest=$LATEST_VERSION" >> "$GITHUB_OUTPUT" + echo "Latest Node.js LTS version: $LATEST_VERSION" + + if [ "$CURRENT_VERSION" = "$LATEST_VERSION" ]; then + echo "updated=false" >> "$GITHUB_OUTPUT" + echo "Already up to date." + else + echo "updated=true" >> "$GITHUB_OUTPUT" + echo "Update available: $CURRENT_VERSION -> $LATEST_VERSION" + fi + + - name: Update Node.js version references + if: steps.check.outputs.updated == 'true' + env: + CURRENT: ${{ steps.check.outputs.current }} + LATEST: ${{ steps.check.outputs.latest }} + run: | + CURRENT_MAJOR=$(echo "$CURRENT" | cut -d. -f1) + LATEST_MAJOR=$(echo "$LATEST" | cut -d. -f1) + + # Update Dockerfiles + sed -i "s/node:${CURRENT}-slim/node:${LATEST}-slim/g" Dockerfile + sed -i "s/node:${CURRENT} /node:${LATEST} /g" Dockerfile.base + + # Update references that depend on major version + if [ "$CURRENT_MAJOR" != "$LATEST_MAJOR" ]; then + # .nvmrc + echo "$LATEST_MAJOR" > .nvmrc + + # CI workflow: step name, node-version, and cache keys + sed -i "s/Use Node.js ${CURRENT_MAJOR}.x/Use Node.js ${LATEST_MAJOR}.x/g" .github/workflows/ci.yml + sed -i "s/node-version: ${CURRENT_MAJOR}.x/node-version: ${LATEST_MAJOR}.x/g" .github/workflows/ci.yml + # Update cache keys: replace node-modules-[optional old version] with new version + sed -i -E "s/node-modules-([0-9]+\.x-)?/node-modules-${LATEST_MAJOR}.x-/g" .github/workflows/ci.yml + + # package.json engines field: append new major version + sed -i "s/\"node\": \"\(.*\)\"/\"node\": \"\1 || ${LATEST_MAJOR}\"/" package.json + fi + + echo "Updated Node.js from $CURRENT to $LATEST" + + - name: Create pull request + if: steps.check.outputs.updated == 'true' + uses: peter-evans/create-pull-request@v7 + with: + commit-message: "fix: Update Node.js to ${{ steps.check.outputs.latest }}" + title: "fix: Update Node.js to ${{ steps.check.outputs.latest }}" + body: | + Automated update of Node.js in Docker images. + + - **Previous version:** ${{ steps.check.outputs.current }} + - **New version:** ${{ steps.check.outputs.latest }} + + [Release notes](https://nodejs.org/en/blog/release/v${{ steps.check.outputs.latest }}) + branch: automated/update-node-lts + delete-branch: true + labels: dependencies