Skip to content

ci: Harden setup-nodejs against silent Node-version fall-through (no-changelog) - #30849

Merged
shortstacked merged 4 commits into
masterfrom
devp-169-harden-setup-nodejs-composite-action-against-silent-node
May 21, 2026
Merged

shortstacked merged 4 commits into
masterfrom
devp-169-harden-setup-nodejs-composite-action-against-silent-node

Conversation

@shortstacked

@shortstacked shortstacked commented May 20, 2026 •

Copy link
Copy Markdown
Collaborator

Summary

Three additive defences to .github/actions/setup-nodejs/action.yml so intermittent actions/setup-node failures become loud and immediate instead of surfacing 45s later as a confusing pnpm ELIFECYCLE:

  1. Cache the Node toolcache. New actions/[email protected] step keyed on node-toolcache-<os>-<arch>-<version>, gated if: runner.os == 'Linux'. First fresh runner pays the ~33s nodejs.org download; subsequent jobs (including parallel E2E shards) get a ~1s cache hit. setup-node finds the toolcache populated and skips the download entirely. Blacksmith transparently routes this through its S3 backend.
  2. Fail-fast Node version verification. New Verify Node.js Version step immediately after Setup Node.js asserts node --version matches inputs.node-version. On mismatch, emits ::error:: and exits 1. Converts the silent fall-through into an immediate, obvious failure. Mirrors the existing Verify PNPM Cache Directory defensive pattern that already references actions/setup-node#1137.
  3. Drop SAFE_CHAIN_LOGGING: verbose from the Install Dependencies step. The verbose MITM proxy log dump (~3000 lines of Set up MITM tunnel / Package X is clean) flushed after the ELIFECYCLE line, burying the actual error.

Linear: https://linear.app/n8n/issue/DEVP-169

Motivation

Eliminates an intermittent CI flake that costs wall-clock and runner minutes across 16 parallel E2E shards on retry. Converts a silent, confusing failure mode (looks like Safe-chain breakage, is actually Node provisioning) into a clear, debuggable one. Reuses existing patterns in the same file (Verify PNPM Cache Directory workaround, retry.mjs precedent) — no new dependencies or unfamiliar surface area.

Failing job that surfaced this: https://github.com/n8n-io/n8n/actions/runs/26100630929/job/76752083283

Test plan

  • I have seen this code, I have run this code, and I take responsibility for this code.
  • First CI run on this branch should be a cache miss — confirm actions/cache logs Cache not found for input keys: node-toolcache-Linux-X64-24.15.0.
  • Second run on this branch (after the first save) should be a cache hit — confirm Cache restored from key: node-toolcache-Linux-X64-24.15.0 and setup-node reports Found in cache @ /opt/hostedtoolcache/node/24.15.0/x64.
  • No Unsupported engine warnings in install logs.
  • Verify Node.js Version step prints Node v24.15.0 active as expected. and exits 0.

🤖 Generated with Claude Code

…changelog)

Cache the Node toolcache, fail fast when setup-node activates the wrong
Node version, and drop verbose Safe-chain logging that buries real
install errors. Closes DEVP-169.

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
@n8n-assistant

n8n-assistant Bot commented May 20, 2026 •

Copy link
Copy Markdown
Contributor

Recommended reviewers

Based on ownership of the 3 changed files in this PR:

Team Files owned Share
@n8n-io/qa-dx 3 100%

@github-actions

github-actions Bot commented May 20, 2026 •

Copy link
Copy Markdown
Contributor

Performance Comparison

Comparing current → latest master → 14-day baseline

Idle baseline with Agents module loaded

Metric Current Latest Master Baseline (avg) vs Master vs Baseline Status
agents-heap-used-baseline 170.18 MB 170.18 MB 158.20 MB (σ 19.26) +0.0% +7.6% ✅
agents-rss-baseline 401.00 MB 401.00 MB 349.88 MB (σ 74.66) +0.0% +14.6% ✅

Memory consumption baseline with starter plan resources

Metric Current Latest Master Baseline (avg) vs Master vs Baseline Status
memory-heap-used-baseline 130.40 MB 130.40 MB 124.49 MB (σ 3.14) +0.0% +4.7% ⚠️
memory-rss-baseline 389.10 MB 389.10 MB 299.43 MB (σ 37.75) +0.0% +29.9% 🔴

docker-stats

Metric Current Latest Master Baseline (avg) vs Master vs Baseline Status
docker-image-size-n8n 1402.88 MB 1525.76 MB 1460.05 MB (σ 108.87) -8.1% -3.9% ✅
docker-image-size-runners 405.00 MB 405.00 MB 400.25 MB (σ 9.63) +0.0% +1.2% ✅

Idle baseline with Instance AI module loaded

Metric Current Latest Master Baseline (avg) vs Master vs Baseline Status
instance-ai-heap-used-baseline 159.68 MB 159.68 MB 197.62 MB (σ 13.32) +0.0% -19.2% 🔴
instance-ai-rss-baseline 338.31 MB 338.31 MB 384.86 MB (σ 31.56) +0.0% -12.1% ⚠️
How to read this table
  • Current: This PR's value (or latest master if PR perf tests haven't run)
  • Latest Master: Most recent nightly master measurement
  • Baseline: Rolling 14-day average from master
  • vs Master: PR impact (current vs latest master)
  • vs Baseline: Drift from baseline (current vs rolling avg)
  • Status: ✅ within 1σ | ⚠️ 1-2σ | 🔴 >2σ regression

shortstacked and others added 3 commits May 20, 2026 22:43
Adversarial review of the prior commit surfaced two issues:

- The new Verify Node.js Version step does strict equality against
  inputs.node-version, but ci-master.yml's unit-test matrix passes the
  semver wildcard 22.x. Pin to 22.22.3 (current latest Node 22 LTS) to
  match the 24.15.0 convention, get reproducibility, and let the verify
  step work as intended on both matrix rows.
- The Restore Node.js Toolcache step hardcodes /x64 in the path but
  docker-build-push.yml runs this composite action on
  blacksmith-4vcpu-ubuntu-2204-arm via the linux/arm64 matrix entry in
  docker-config.mjs. Tighten the gate to runner.arch == 'X64' so the
  cache step no-ops cleanly on ARM instead of saving to a path
  setup-node never reads.

Closes DEVP-169.

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
…elog)

- Remove the "Node v… active as expected." success echo from the new
  Verify Node.js Version step. A green check on the step name is the
  standard CI signal; the echo just adds noise on every job.
- Tiny grammar fix in packages/testing/playwright/README.md. The .github/
  changes alone leave ci-filter's `ci` and `e2e` flags false, so build,
  unit, and E2E shards skip — touching a playwright file flips both flags
  on so the composite-action changes get exercised end-to-end.

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
@shortstacked
shortstacked marked this pull request as ready for review May 20, 2026 22:35
@shortstacked
shortstacked requested review from a team and Matsuuu May 20, 2026 22:35
@shortstacked
shortstacked enabled auto-merge May 20, 2026 22:38

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No issues found across 3 files

Architecture diagram
sequenceDiagram
    participant Runner as GitHub Runner
    participant Cache as actions/cache
    participant SetupNode as actions/setup-node
    participant NodeTool as Node.js Toolcache
    participant Verify as Verify Node.js Version
    participant SetupPNPM as pnpm/action-setup
    participant Install as Install Dependencies
    participant nodejs_org as nodejs.org

    Note over Runner,Install: ENHANCED: Node.js provisioning with cache & verification

    Runner->>SetupPNPM: Setup pnpm

    opt runner.os == 'Linux' && runner.arch == 'X64'
        Runner->>Cache: Restore Node.js Toolcache
        Note over Cache: key: node-toolcache-<os>-<arch>-<version>
        Cache-->>Runner: cache hit/miss result
    end

    Runner->>SetupNode: Setup Node.js
    SetupNode->>NodeTool: Check if version exists in toolcache

    alt Cache Hit (toolcache populated)
        NodeTool-->>SetupNode: Found @ /opt/hostedtoolcache/node/<version>/x64
        SetupNode-->>Runner: Skip download, use cached node
    else Cache Miss (first run or new version)
        SetupNode->>nodejs_org: Download Node.js (33s)
        nodejs_org-->>SetupNode: Downloaded
        SetupNode->>NodeTool: Install to toolcache
        SetupNode-->>Runner: Node ready
        Note over Runner,Cache: Cache will save for subsequent jobs
    end

    Runner->>SetupNode: Provide node-version input
    SetupNode-->>Runner: Node active in PATH

    Runner->>Verify: Verify Node.js Version
    Verify->>Verify: Compare actual vs expected version
    alt Version matches
        Verify-->>Runner: Node v<version> active as expected.
    else Version mismatch (silent fall-through detection)
        Verify-->>Runner: ::error:: setup-node did not activate Node <expected>
        Runner->>Verify: exit 1 (immediate failure)
    end

    Runner->>Install: Run install command
    Note over Install: SAFE_CHAIN_LOGGING removed (no verbose proxy logs)

    Install->>Install: timeout 300s INSTALL_COMMAND --reporter=append-only
    Install-->>Runner: Dependencies installed
Loading

Re-trigger cubic

@Matsuuu Matsuuu left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for this! This was also on my pet peeve list and just hadn't had time to explore it. Let's hope we find the actual issues more easily now!

@shortstacked
shortstacked added this pull request to the merge queue May 21, 2026
Merged via the queue into master with commit 25f3a3e May 21, 2026
63 checks passed
@shortstacked
shortstacked deleted the devp-169-harden-setup-nodejs-composite-action-against-silent-node branch May 21, 2026 06:27
shortstacked added a commit that referenced this pull request May 21, 2026
…e (no-changelog)

The Restore Node.js Toolcache step cached `/opt/hostedtoolcache/node/<v>/x64`
but not the sibling `x64.complete` marker file. Without the marker,
actions/setup-node treats the restored toolchain as invalid and re-downloads
from nodejs.org on every job — paying both the cache restore cost AND the
download cost. The re-download path also opens the silent fall-through window
tracked in actions/setup-node#1137, which the
verify-node-version step added in #30849 has been catching.

Cache the parent version directory so both `x64/` and `x64.complete` are
captured. Bump the cache key to v2 to invalidate stale marker-less entries.

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
@n8n-assistant

n8n-assistant Bot commented May 27, 2026

Copy link
Copy Markdown
Contributor

Got released with [email protected]

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants