Skip to content

Set up the pinned Node in every Rust CI job and drop the unused trusted-server-js deps #1204

Description

@aram356

Description

Two small pieces of build hygiene around the TSJS build that every Rust build runs.

1. Most Rust CI jobs build the TSJS bundles with the runner's Node, not the pinned one

trusted-server-core depends on trusted-server-js (crates/trusted-server-core/Cargo.toml:47). The trusted-server-js build script runs npm ci when node_modules is missing, then npm run build unless TSJS_SKIP_BUILD=1, and embeds the resulting bundles (crates/trusted-server-js/build.rs:41-85). A fresh CI checkout has no node_modules, so every job that compiles core runs a Node build.

.tool-versions pins nodejs 24.12.0. Only two of the eleven jobs that compile core run actions/setup-node with that version before building. A third sets it up only after its Rust build. The other eight use whatever Node the runner image has on PATH. For the images used by recent runs, that is Node.js 22.23.2 on ubuntu-24.04 (image 20260907.300) and Node.js 24.20.0 on macos-26-arm64 (image 20260907.0351), per the images' software lists. Nothing in the repository flags the mismatch: crates/trusted-server-js/lib/package.json has no engines field, and there is no .nvmrc or .npmrc.

As a result, the bundles embedded in most of the Rust artifacts that CI builds, tests and lints are produced by a different Node major than the Vitest and format jobs use on Linux. The Node version can also change with a runner image update, with no change in the repository. crates/trusted-server-adapter-cloudflare/build.sh:2-6 already works around a related local problem (sourcing nvm because the build script picked up the wrong system Node), which shows the build script's Node matters.

2. The Cloudflare and Spin adapters declare a trusted-server-js dependency they never use

crates/trusted-server-adapter-cloudflare/Cargo.toml:33 and crates/trusted-server-adapter-spin/Cargo.toml:35 both have:

trusted-server-js = { workspace = true }

Neither crate's sources or tests mention trusted_server_js, and no commit ever has (git log -G trusted_server_js -- crates/trusted-server-adapter-cloudflare crates/trusted-server-adapter-spin is empty). Both crates were created with the line: Cloudflare in #643 and Spin in #735. #614 had already removed the same direct dependency from the Fastly adapter as "accessed transitively via trusted-server-core". Core still depends on the crate, so this is dependency-graph hygiene. Removing the lines does not remove the Node requirement. It does make the graph that the #1194 crate split has to redraw accurate.

Steps to reproduce

Jobs that compile core, at a4e01eb

The last column counts tsjs: Building per-module bundles lines in the job log of a recent run whose workflow files are identical to a4e01eb. test.yml and format.yml counts come from runs 35968913882 and 35968913845 (head 7e3997a), and integration-tests.yml counts from run 35974697391 (head bf96305).

Workflow / job Compiles core Pinned Node set up before the Rust build TSJS builds in log
test.yml / test-rust yes (cargo test-fastly) yes (test.yml:54-57, added for the template-cache harness) 5
test.yml / test-axum yes no 13
test.yml / test-cloudflare yes no 3
test.yml / test-spin yes no 4
test.yml / test-parity yes (trusted-server-integration-tests depends on core) no 2
test.yml / test-cli (macOS) yes no 10
test.yml / test-typescript no yes 0
format.yml / format-rust yes (all clippy steps) no 7
format.yml / check-claude-md-symlink, format-typescript, format-docs no JS jobs yes 0
integration-tests.yml / prepare-artifacts yes (composite action builds Fastly, Axum, Cloudflare; the Viceroy config script builds the integration-tests crate) no 4
integration-tests.yml / integration-tests yes (cargo test of the integration-tests crate) yes (integration-tests.yml:93-96) 1
integration-tests.yml / integration-tests-fastly-ec yes (composite action builds Axum, then cargo test) no 2
integration-tests.yml / browser-tests yes (composite action builds Axum) only after the Rust build (integration-tests.yml:181-184) 1
deploy-docs.yml / build, deploy no docs job yes n/a
codeql.yml / analyze no cargo step (build-mode: none) no n/a

The composite action .github/actions/setup-integration-test-env/action.yml reads the Node version (:42-45) and exports it, but never calls actions/setup-node. integration-tests-fastly-ec and browser-tests do not pass build-axum: "false", so the action's default (:16-19) builds Axum in both jobs. In the browser job log, the Axum build's TSJS step ran at 08:32:15 and setup-node at 08:32:39.

To see the Node versions:

gh api -H "Accept: application/vnd.github.raw" \
  "repos/actions/runner-images/contents/images/ubuntu/Ubuntu2404-Readme.md?ref=ubuntu24/20260907.300" | grep -n "Node.js"
gh api -H "Accept: application/vnd.github.raw" \
  "repos/actions/runner-images/contents/images/macos/macos-26-arm64-Readme.md?ref=macos-26-arm64/20260907.0351" | grep -n "Node.js"

Observed. Line 26 of each list is the Node on PATH. The #### Node.js sections list the cached versions that setup-node can select, 22.23.2 and 24.20.0 on both images.

26:- Node.js 22.23.2
196:#### Node.js
26:- Node.js 24.20.0
127:#### Node.js

The unused dependency

rustc's unused_crate_dependencies lint, applied to each adapter library only:

cargo rustc -p trusted-server-adapter-cloudflare --lib --target wasm32-unknown-unknown --features cloudflare -- -W unused-crate-dependencies
cargo rustc -p trusted-server-adapter-spin --lib --target wasm32-wasip1 --features spin -- -W unused-crate-dependencies

Observed, on the production wasm builds:

warning: extern crate `trusted_server_js` is unused in crate `trusted_server_adapter_cloudflare`
warning: extern crate `trusted_server_js` is unused in crate `trusted_server_adapter_spin`

On the native host target the lint also flags dependencies that are only used under cfg(target_arch = "wasm32"), so a workspace-wide lint would be noisy. trusted_server_js is the only dependency flagged on the wasm feature builds.

With the two lines deleted, on a copy of a4e01eb:

cargo check -p trusted-server-adapter-cloudflare -p trusted-server-adapter-spin --all-targets   -> Finished
cargo test  -p trusted-server-adapter-cloudflare -p trusted-server-adapter-spin                 -> 22 + 23 + 49 + 38 passed
cargo check -p trusted-server-adapter-cloudflare --target wasm32-unknown-unknown --features cloudflare -> Finished
cargo check -p trusted-server-adapter-spin --target wasm32-wasip1 --features spin                 -> Finished

The workspace entry is trusted-server-js = { path = "crates/trusted-server-js" } (Cargo.toml:115), with no features, so removing the direct edges does not change feature resolution.

Expected behavior

  • Every CI job that compiles trusted-server-core builds the TSJS bundles with the Node version in .tool-versions, the same one the JS jobs use.
  • Adapter crates declare only the dependencies they use.

Actual behavior

  • 8 of 11 Rust-building jobs never set up the pinned Node, and 1 sets it up only after its Rust build. On Linux those builds use Node 22.
  • Cloudflare and Spin carry an unused direct trusted-server-js dependency.

Root cause

  • The Node setup was added job by job for specific needs. In test-rust, for example, the step is named "Use Node.js for the served-seam contract": scripts/template-cache-local-test.sh, which that job runs, needs node to execute the GPT bundle (:69-70, :657). It was not added as a prerequisite of the core build script.
  • The composite integration action resolves the Node version but does not install it.
  • The adapter crates copied a dependency line that Remove unused dependencies and disable unnecessary test targets #614 had already removed from the Fastly adapter.

Impact

Proposed fix

  1. Add an actions/setup-node@v4 step that reads .tool-versions, as test-rust does, before the first cargo command in test-axum, test-cloudflare, test-spin, test-parity, test-cli and format-rust.
  2. In .github/actions/setup-integration-test-env/action.yml, add actions/setup-node@v4 with node-version: ${{ steps.node-version.outputs.node-version }} right after the "Retrieve Node.js version" step. That covers prepare-artifacts, integration-tests-fastly-ec and browser-tests. The later setup-node steps in integration-tests and browser-tests then only matter for their npm cache settings.
  3. Optionally, pass cache: npm with cache-dependency-path: crates/trusted-server-js/lib/package-lock.json so the build script's npm ci is cached.
  4. Delete trusted-server-js = { workspace = true } from the Cloudflare and Spin Cargo.toml files and let Cargo update Cargo.lock.

Optional, separate from this task: neither integration-tests-fastly-ec nor browser-tests uses the Axum binary its composite step builds. The Fastly EC job runs only test_ec_lifecycle_fastly, AXUM_BINARY_PATH is read only by tests/environments/axum.rs, and the browser tests never mention Axum. Passing build-axum: "false" would save one full Rust build per job.

Done when

  • Every job in the table that compiles core runs actions/setup-node with the .tool-versions Node before its first cargo command, and the composite action does the same.
  • A CI run shows node-version: 24.12.0 (or the current pin) in the setup step of each of those jobs.
  • The Cloudflare and Spin Cargo.toml files no longer list trusted-server-js, Cargo.lock is updated, and cargo check-cloudflare, cargo check-spin, cargo test-cloudflare and cargo test-spin pass.

Affected area

CI / Tooling

Version

main at a4e01eb

Related

Activity

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    Projects

    No projects

      Milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions