From c86ca65d7f89bffbfb8d769207f0b5906493ceaa Mon Sep 17 00:00:00 2001 From: Amaad Martin Date: Sat, 1 Aug 2026 10:26:51 -0700 Subject: [PATCH 1/2] ci: pin the validation toolchain and install from the lockfile `actions/setup-node@v6` was invoked with no inputs, so the job installed nothing and ran on whatever Node -- and therefore whatever bundled npm -- the runner image happened to ship. Pin `node-version: '24'`: Node 24 (Krypton) has been Active LTS since 2025-10-28 and is supported until 2028-04-30, while Node 20 (Iron) reached end-of-life on 2026-04-30. The value is quoted because YAML parses an unquoted two-segment version such as 20.10 as a float. Install with `npm ci` rather than `npm install`. `npm ci` installs strictly from the lockfile and hard-errors when the manifests and the lock disagree, instead of silently repairing the lockfile in the runner's throwaway workspace and discarding the repair. --- .github/workflows/validation.yaml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/validation.yaml b/.github/workflows/validation.yaml index 7bb00098e..6e50bd071 100644 --- a/.github/workflows/validation.yaml +++ b/.github/workflows/validation.yaml @@ -22,6 +22,8 @@ jobs: - name: Use Node.js uses: actions/setup-node@v6 + with: + node-version: '24' - name: Setup Python uses: actions/setup-python@v5 @@ -29,7 +31,7 @@ jobs: python-version: '3.11' - name: Install dependencies - run: npm install + run: npm ci - name: Check for secrets leaks run: npx secretlint "**/*" From ebcd44b4c9306d8a3d7e1ae61102c79abd08465f Mon Sep 17 00:00:00 2001 From: Amaad Martin Date: Sat, 1 Aug 2026 10:27:04 -0700 Subject: [PATCH 2/2] ci: fail validation when package-lock.json is out of date Nothing enforced that the committed package-lock.json is what npm resolves from the committed package.json, so lockfile rot never surfaced in the pull request that caused it. It accumulated and landed as collateral churn elsewhere: the stale `"dev": true` flag on adm-zip@0.5.17 was finally removed inside google/adk-js#564, a change about hoisting @google/genai, as 142 lines of lockfile diff a contributor then had to justify by hand. Regenerate the lockfile after the install and fail with an actionable annotation when the result differs from what is committed. The regeneration is load-bearing: npm ci never writes a package-lock, so `npm ci` followed by a bare `git diff --exit-code` is a check that can never fail -- worse than adding nothing, because it looks like protection. The gate is also not redundant with npm ci, which only verifies that the manifests' dependency specs are satisfied by the lock; resolution metadata like the adm-zip flag is recomputed only by regeneration. The `if ! git diff ...; then ... fi` wrapper is required because a multi-line run block executes under `bash -e`, which would abort the step before the echo could emit the annotation. Both outputs are kept: the diff says what drifted, the annotation says what to do about it. Lockfile content does not vary by platform under lockfileVersion 3, so the gate runs once on ubuntu-latest; the repo has no .gitattributes, so running the diff on windows-latest would risk whole-file noise from CRLF conversion. --- .github/workflows/validation.yaml | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/.github/workflows/validation.yaml b/.github/workflows/validation.yaml index 6e50bd071..0e5391f99 100644 --- a/.github/workflows/validation.yaml +++ b/.github/workflows/validation.yaml @@ -33,6 +33,15 @@ jobs: - name: Install dependencies run: npm ci + - name: Check package-lock.json is up to date + if: matrix.os == 'ubuntu-latest' + run: | + npm install --package-lock-only + if ! git diff --exit-code package-lock.json; then + echo "::error file=package-lock.json::package-lock.json is out of date. Run 'npm install' locally and commit the regenerated package-lock.json." + exit 1 + fi + - name: Check for secrets leaks run: npx secretlint "**/*"