From 6b5f44faa5f86bfbfcf67272edacc020381e4549 Mon Sep 17 00:00:00 2001 From: leynos Date: Sun, 2 Aug 2026 20:53:27 +0200 Subject: [PATCH 1/4] Update setup-rust actions to Node.js 24 (#81) Pin every Node-based dependency in `setup-rust` to a verified Node.js 24 release and guard the revisions with manifest tests. --- .github/actions/setup-rust/CHANGELOG.md | 5 ++++ .github/actions/setup-rust/README.md | 2 +- .github/actions/setup-rust/action.yml | 16 ++++++------ .../tests/test_setup_rust_manifest.py | 25 +++++++++++++++++++ 4 files changed, 39 insertions(+), 9 deletions(-) diff --git a/.github/actions/setup-rust/CHANGELOG.md b/.github/actions/setup-rust/CHANGELOG.md index 0b1ba1e2..0c940aa5 100644 --- a/.github/actions/setup-rust/CHANGELOG.md +++ b/.github/actions/setup-rust/CHANGELOG.md @@ -1,5 +1,10 @@ # Changelog +## v1.0.16 - 2026-08-02 + +- Update actions/cache, sccache-action, and setup-msys2 to releases that use + the GitHub Actions Node.js 24 runtime. + ## v1.0.15 - 2026-07-29 - Add `rustflags` input forwarded to `actions-rust-lang/setup-rust-toolchain`. diff --git a/.github/actions/setup-rust/README.md b/.github/actions/setup-rust/README.md index b514ea62..08c579b4 100644 --- a/.github/actions/setup-rust/README.md +++ b/.github/actions/setup-rust/README.md @@ -132,7 +132,7 @@ compiled objects are stored in `~/.cache/sccache` and cached with a **separate cache key** from the directories above. This directory holds the sccache cache space and does not share data with the Rust dependency cache; the sccache step itself uses -`mozilla-actions/sccache-action@7d986dd989559c6ecdb630a3fd2557667be217ad`, +`mozilla-actions/sccache-action@fc920bf0ec8de6ee65d409111f7ec508035751ba`, pinned to a specific commit for reproducibility. ### Extent and limitations diff --git a/.github/actions/setup-rust/action.yml b/.github/actions/setup-rust/action.yml index 0d4765b9..32ed1747 100644 --- a/.github/actions/setup-rust/action.yml +++ b/.github/actions/setup-rust/action.yml @@ -168,8 +168,8 @@ runs: echo "NIGHTLY_SYSROOT=$(rustc +$OPENBSD_NIGHTLY --print sysroot)" >> "$GITHUB_ENV" shell: bash - name: Cache cargo registry - # v4.3.0 - uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 + # v6.1.0 + uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 with: path: | ~/.cargo/registry @@ -181,12 +181,12 @@ runs: # x86_64-apple-darwin binaries were dropped after sccache v0.12.0 - name: Run sccache (x86_64 macOS) if: ${{ inputs.use-sccache == 'true' && github.event_name != 'release' && runner.os == 'macOS' && runner.arch == 'X64' }} - uses: mozilla-actions/sccache-action@7d986dd989559c6ecdb630a3fd2557667be217ad + uses: mozilla-actions/sccache-action@fc920bf0ec8de6ee65d409111f7ec508035751ba with: version: v0.12.0 - name: Run sccache if: ${{ inputs.use-sccache == 'true' && github.event_name != 'release' && !(runner.os == 'macOS' && runner.arch == 'X64') }} - uses: mozilla-actions/sccache-action@7d986dd989559c6ecdb630a3fd2557667be217ad + uses: mozilla-actions/sccache-action@fc920bf0ec8de6ee65d409111f7ec508035751ba - name: Install system dependencies if: ${{ inputs.install-postgres-deps == 'true' && runner.os == 'Linux' }} run: sudo apt-get update && sudo apt-get install -y --no-install-recommends libpq-dev @@ -206,8 +206,8 @@ runs: - name: Install MSYS2 toolchain and SQLite if: ${{ inputs.install-sqlite-deps == 'true' && runner.os == 'Windows' }} - # v2.30.0 - uses: msys2/setup-msys2@4f806de0a5a7294ffabaff804b38a9b435a73bda + # v2.32.0 + uses: msys2/setup-msys2@66cd2cce69caa17b53920067426061ca1de3a884 with: msystem: MINGW64 update: true @@ -226,8 +226,8 @@ runs: - name: Cache OpenBSD stdlib if: ${{ inputs.with-openbsd == 'true' && runner.os == 'Linux' }} id: openbsd-stdlib-cache - # v4.3.0 - uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 + # v6.1.0 + uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 with: path: ${{ env.NIGHTLY_SYSROOT }}/lib/rustlib/x86_64-unknown-openbsd key: openbsd-stdlib-${{ runner.os }}-${{ hashFiles('rust-toolchain.toml') }} diff --git a/.github/actions/setup-rust/tests/test_setup_rust_manifest.py b/.github/actions/setup-rust/tests/test_setup_rust_manifest.py index 4491ff2e..d95693c4 100644 --- a/.github/actions/setup-rust/tests/test_setup_rust_manifest.py +++ b/.github/actions/setup-rust/tests/test_setup_rust_manifest.py @@ -16,6 +16,19 @@ PINNED_BINSTALL_SHA256 = ( "d3a93702160e0ec03e2a4e996855db1f01adee801fb84a43add24e0877ef8eae" ) +NODE24_ACTION_REVISIONS = { + "Cache cargo registry": "actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9", + "Run sccache (x86_64 macOS)": ( + "mozilla-actions/sccache-action@fc920bf0ec8de6ee65d409111f7ec508035751ba" + ), + "Run sccache": ( + "mozilla-actions/sccache-action@fc920bf0ec8de6ee65d409111f7ec508035751ba" + ), + "Install MSYS2 toolchain and SQLite": ( + "msys2/setup-msys2@66cd2cce69caa17b53920067426061ca1de3a884" + ), + "Cache OpenBSD stdlib": ("actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9"), +} def _load_steps() -> list[dict[str, object]]: @@ -198,6 +211,18 @@ def test_manifest_exposes_toolchain_input() -> None: assert "toolchain" in inputs +@pytest.mark.parametrize( + ("step_name", "expected_revision"), + NODE24_ACTION_REVISIONS.items(), +) +def test_node_actions_use_pinned_node24_revisions( + step_name: str, + expected_revision: str, +) -> None: + """Node-based dependencies should use the verified Node.js 24 revisions.""" + assert _get_step(step_name).get("uses") == expected_revision + + def test_install_postgres_deps_is_linux_only() -> None: """Postgres packages should only install on Linux when requested.""" condition = _get_step_condition("Install system dependencies") From 87a81fb62ca03794bc67d6ea84ceb9c795794f0e Mon Sep 17 00:00:00 2001 From: leynos Date: Mon, 3 Aug 2026 13:07:17 +0200 Subject: [PATCH 2/4] Document revised setup-rust pins (#81) List every Node.js-backed action revision changed by the setup-rust update so the README remains aligned with the composite action manifest. --- .github/actions/setup-rust/README.md | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/.github/actions/setup-rust/README.md b/.github/actions/setup-rust/README.md index 08c579b4..6365656b 100644 --- a/.github/actions/setup-rust/README.md +++ b/.github/actions/setup-rust/README.md @@ -130,10 +130,11 @@ cache compiler output. It sets `SCCACHE_GHA_ENABLED=true` and `RUSTC_WRAPPER=sccache` so subsequent build steps benefit from the cache. The compiled objects are stored in `~/.cache/sccache` and cached with a **separate cache key** from the directories above. This directory holds the sccache cache -space and does not share data with the Rust dependency cache; the sccache step -itself uses -`mozilla-actions/sccache-action@fc920bf0ec8de6ee65d409111f7ec508035751ba`, -pinned to a specific commit for reproducibility. +space and does not share data with the Rust dependency cache. The revised +Node.js-backed actions are pinned to specific commits for reproducibility: +`actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9`, +`mozilla-actions/sccache-action@fc920bf0ec8de6ee65d409111f7ec508035751ba` +and `msys2/setup-msys2@66cd2cce69caa17b53920067426061ca1de3a884`. ### Extent and limitations From 484a3661c57131f98b0a9cfd9ad27f4e67e938b4 Mon Sep 17 00:00:00 2001 From: leynos Date: Mon, 3 Aug 2026 21:17:13 +0200 Subject: [PATCH 3/4] Add runner coverage and pin maintenance guidance Document how maintainers verify and synchronize the Node.js 24 action pins. Exercise the local `setup-rust` composite action with `act` and prove that `rustc` and `cargo` remain available to downstream steps. --- .github/workflows/test-rustflags-export.yml | 26 ++++++++++ docs/developers-guide.md | 19 ++++++++ .../test_rustflags_export_workflow.py | 47 +++++++++++++++++-- 3 files changed, 89 insertions(+), 3 deletions(-) diff --git a/.github/workflows/test-rustflags-export.yml b/.github/workflows/test-rustflags-export.yml index dcb6606a..b8dc505f 100644 --- a/.github/workflows/test-rustflags-export.yml +++ b/.github/workflows/test-rustflags-export.yml @@ -94,3 +94,29 @@ jobs: - name: Observe RUSTFLAGS alongside an inherited value shell: bash run: echo "setup_rust_inherited_rustflags=[${RUSTFLAGS-unset}]" + + # act cannot reliably execute sccache's post-step. Disable sccache here so + # this supported Linux path can prove that setup-rust makes the Rust tools + # available to downstream steps through the composite-action boundary. + setup-rust-toolchain-available: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + with: + persist-credentials: false + - name: Setup stable Rust + uses: ./.github/actions/setup-rust + with: + toolchain: stable + install-binstall: "false" + use-sccache: "false" + - name: Verify Rust tools remain available + shell: bash + run: | + set -euo pipefail + rustc_version="$(rustc --version)" + cargo_version="$(cargo --version)" + test -n "${rustc_version}" + test -n "${cargo_version}" + echo "setup_rust_rustc=[${rustc_version}]" + echo "setup_rust_cargo=[${cargo_version}]" diff --git a/docs/developers-guide.md b/docs/developers-guide.md index cbef68ce..ebea4e07 100644 --- a/docs/developers-guide.md +++ b/docs/developers-guide.md @@ -153,6 +153,25 @@ act cannot execute the real `setup-uv` path on the local runner, document the reason and keep the unit or manifest tests that assert the pinned reference in sync with the new SHA. +## Maintaining `setup-rust` Node.js Action Pins + +The [`setup-rust` action manifest](../.github/actions/setup-rust/action.yml) +uses `actions/cache`, `mozilla-actions/sccache-action`, and +`msys2/setup-msys2`. Pin each action by a verified full commit SHA. + +When updating these Node.js 24 action dependencies: + +1. Inspect the upstream `action.yml` at the selected revision and verify that + its `runs.using` value declares the required Node.js runtime. +2. Update every affected `setup-rust` manifest step together so all supported + runner paths use the intended revisions. +3. Synchronize the exact revision strings in `NODE24_ACTION_REVISIONS` in the + [`setup-rust` manifest tests](../.github/actions/setup-rust/tests/test_setup_rust_manifest.py). +4. Run the manifest tests and supported runner-backed workflow validation. + +The static manifest assertions must remain in place: runner execution proves +that the action works, but cannot prove that a pin is the intended revision. + ## `setup-rust` cargo-binstall Pinning The `setup-rust` action pins `cargo-binstall` by downloading diff --git a/tests/workflows/test_rustflags_export_workflow.py b/tests/workflows/test_rustflags_export_workflow.py index b22b1534..074adc56 100644 --- a/tests/workflows/test_rustflags_export_workflow.py +++ b/tests/workflows/test_rustflags_export_workflow.py @@ -11,8 +11,10 @@ import re import typing as typ +from pathlib import Path import pytest +import yaml from .conftest import ( FIXTURES_DIR, @@ -22,10 +24,8 @@ skip_unless_workflow_tests, ) -if typ.TYPE_CHECKING: - from pathlib import Path - WORKFLOW = "test-rustflags-export.yml" +WORKFLOW_PATH = Path(__file__).resolve().parents[2] / ".github" / "workflows" / WORKFLOW # The workflow runs on the release event because the nested setup-rust skips # sccache for releases, whose post-step is unreliable under act. EVENT = "release" @@ -49,6 +49,33 @@ def _run(job: str, artefact_dir: Path) -> str: return logs +def test_setup_rust_toolchain_workflow_shape() -> None: + """The runner job exercises the intended local setup-rust path.""" + workflow = typ.cast( + "dict[str, typ.Any]", + yaml.safe_load(WORKFLOW_PATH.read_text(encoding="utf-8")), + ) + steps = workflow["jobs"]["setup-rust-toolchain-available"]["steps"] + setup_step = next(step for step in steps if step.get("name") == "Setup stable Rust") + assert setup_step["uses"] == "./.github/actions/setup-rust" + assert setup_step["with"] == { + "toolchain": "stable", + "install-binstall": "false", + "use-sccache": "false", + } + + verify_step = next( + step + for step in steps + if step.get("name") == "Verify Rust tools remain available" + ) + script = verify_step["run"] + assert "rustc --version" in script + assert "cargo --version" in script + assert 'test -n "${rustc_version}"' in script + assert 'test -n "${cargo_version}"' in script + + @skip_unless_act @skip_unless_workflow_tests def test_rust_build_release_exports_rustflags_to_later_steps( @@ -117,3 +144,17 @@ def test_setup_rust_leaves_an_inherited_rustflags_alone(artefact_dir: Path) -> N assert "debuginfo=2" not in logs.split("setup_rust_inherited_rustflags=")[-1], ( f"the input displaced the inherited value:\n{logs}" ) + + +@skip_unless_act +@skip_unless_workflow_tests +def test_setup_rust_exposes_rust_tools_to_later_steps(artefact_dir: Path) -> None: + """A supported Linux setup leaves rustc and cargo available downstream.""" + logs = _run("setup-rust-toolchain-available", artefact_dir) + + assert re.search(r"setup_rust_rustc=\[rustc \d+\.\d+\.\d+", logs), ( + f"rustc was not available after setup-rust:\n{logs}" + ) + assert re.search(r"setup_rust_cargo=\[cargo \d+\.\d+\.\d+", logs), ( + f"cargo was not available after setup-rust:\n{logs}" + ) From e39e3be053084bd8f7549d580328b09601916a0f Mon Sep 17 00:00:00 2001 From: leynos Date: Tue, 4 Aug 2026 02:56:00 +0200 Subject: [PATCH 4/4] Strengthen setup-rust toolchain contract coverage Remove the preinstalled stable toolchain before exercising the composite action and prove that setup selects stable for downstream Rust commands. Harden workflow-shape diagnostics and document the user-visible Node.js 24 dependency update. --- .../tests/test_setup_rust_manifest.py | 4 +- .github/workflows/test-rustflags-export.yml | 18 +++++++ docs/users-guide.md | 8 +++ .../test_rustflags_export_workflow.py | 51 ++++++++++++++----- 4 files changed, 67 insertions(+), 14 deletions(-) diff --git a/.github/actions/setup-rust/tests/test_setup_rust_manifest.py b/.github/actions/setup-rust/tests/test_setup_rust_manifest.py index d95693c4..889347cb 100644 --- a/.github/actions/setup-rust/tests/test_setup_rust_manifest.py +++ b/.github/actions/setup-rust/tests/test_setup_rust_manifest.py @@ -220,7 +220,9 @@ def test_node_actions_use_pinned_node24_revisions( expected_revision: str, ) -> None: """Node-based dependencies should use the verified Node.js 24 revisions.""" - assert _get_step(step_name).get("uses") == expected_revision + assert _get_step(step_name).get("uses") == expected_revision, ( + f"{step_name} must use the verified revision {expected_revision}" + ) def test_install_postgres_deps_is_linux_only() -> None: diff --git a/.github/workflows/test-rustflags-export.yml b/.github/workflows/test-rustflags-export.yml index b8dc505f..f5b29414 100644 --- a/.github/workflows/test-rustflags-export.yml +++ b/.github/workflows/test-rustflags-export.yml @@ -104,6 +104,15 @@ jobs: - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 with: persist-credentials: false + - name: Remove the preinstalled stable toolchain + shell: bash + run: | + set -euo pipefail + rustup toolchain uninstall stable || true + if rustup run stable rustc --version >/dev/null 2>&1; then + echo "stable remained installed before setup-rust" >&2 + exit 1 + fi - name: Setup stable Rust uses: ./.github/actions/setup-rust with: @@ -116,7 +125,16 @@ jobs: set -euo pipefail rustc_version="$(rustc --version)" cargo_version="$(cargo --version)" + active_toolchain="$(rustup show active-toolchain)" + case "${active_toolchain}" in + stable-*) ;; + *) + echo "expected stable toolchain, got: ${active_toolchain}" >&2 + exit 1 + ;; + esac test -n "${rustc_version}" test -n "${cargo_version}" + echo "setup_rust_toolchain=[${active_toolchain}]" echo "setup_rust_rustc=[${rustc_version}]" echo "setup_rust_cargo=[${cargo_version}]" diff --git a/docs/users-guide.md b/docs/users-guide.md index a620b9c0..6c845dfe 100644 --- a/docs/users-guide.md +++ b/docs/users-guide.md @@ -14,6 +14,14 @@ for common scenarios. - [`rust-build-release` README](../.github/actions/rust-build-release/README.md) – full input and output tables. +## Node.js 24 action dependencies + +`setup-rust` pins its Node.js-backed cache, sccache and MSYS2 dependencies to +revisions that support the GitHub Actions Node.js 24 runtime. This removes the +Node.js 20 deprecation warnings without changing the action's inputs or cache +configuration. See the [`setup-rust` README](../.github/actions/setup-rust/README.md) +for the pinned revisions and cache behaviour. + ## The problem The nested `actions-rust-lang/setup-rust-toolchain` action exports diff --git a/tests/workflows/test_rustflags_export_workflow.py b/tests/workflows/test_rustflags_export_workflow.py index 074adc56..5b9e410e 100644 --- a/tests/workflows/test_rustflags_export_workflow.py +++ b/tests/workflows/test_rustflags_export_workflow.py @@ -10,7 +10,6 @@ from __future__ import annotations import re -import typing as typ from pathlib import Path import pytest @@ -51,29 +50,52 @@ def _run(job: str, artefact_dir: Path) -> str: def test_setup_rust_toolchain_workflow_shape() -> None: """The runner job exercises the intended local setup-rust path.""" - workflow = typ.cast( - "dict[str, typ.Any]", - yaml.safe_load(WORKFLOW_PATH.read_text(encoding="utf-8")), + workflow = yaml.safe_load(WORKFLOW_PATH.read_text(encoding="utf-8")) + assert isinstance(workflow, dict), f"{WORKFLOW_PATH} must contain a YAML mapping" + jobs = workflow.get("jobs") + assert isinstance(jobs, dict), f"{WORKFLOW_PATH} must define a jobs mapping" + job = jobs.get("setup-rust-toolchain-available") + assert isinstance(job, dict), ( + "workflow must define the setup-rust-toolchain-available job" + ) + steps = job.get("steps") + assert isinstance(steps, list), ( + "setup-rust-toolchain-available must define a steps collection" + ) + assert all(isinstance(step, dict) for step in steps), ( + "every setup-rust-toolchain-available step must be a mapping" + ) + + setup_steps = [step for step in steps if step.get("name") == "Setup stable Rust"] + assert len(setup_steps) == 1, "expected exactly one Setup stable Rust step" + setup_step = setup_steps[0] + assert setup_step["uses"] == "./.github/actions/setup-rust", ( + "Setup stable Rust must call the local setup-rust action" ) - steps = workflow["jobs"]["setup-rust-toolchain-available"]["steps"] - setup_step = next(step for step in steps if step.get("name") == "Setup stable Rust") - assert setup_step["uses"] == "./.github/actions/setup-rust" assert setup_step["with"] == { "toolchain": "stable", "install-binstall": "false", "use-sccache": "false", - } + }, "Setup stable Rust must select the isolated stable toolchain path" - verify_step = next( + verify_steps = [ step for step in steps if step.get("name") == "Verify Rust tools remain available" + ] + assert len(verify_steps) == 1, ( + "expected exactly one Verify Rust tools remain available step" ) + verify_step = verify_steps[0] script = verify_step["run"] - assert "rustc --version" in script - assert "cargo --version" in script - assert 'test -n "${rustc_version}"' in script - assert 'test -n "${cargo_version}"' in script + assert "rustc --version" in script, "verification must execute rustc" + assert "cargo --version" in script, "verification must execute cargo" + assert 'test -n "${rustc_version}"' in script, ( + "verification must assert that rustc returned a version" + ) + assert 'test -n "${cargo_version}"' in script, ( + "verification must assert that cargo returned a version" + ) @skip_unless_act @@ -152,6 +174,9 @@ def test_setup_rust_exposes_rust_tools_to_later_steps(artefact_dir: Path) -> Non """A supported Linux setup leaves rustc and cargo available downstream.""" logs = _run("setup-rust-toolchain-available", artefact_dir) + assert re.search(r"setup_rust_toolchain=\[stable-[^]]+", logs), ( + f"setup-rust did not select the required stable toolchain:\n{logs}" + ) assert re.search(r"setup_rust_rustc=\[rustc \d+\.\d+\.\d+", logs), ( f"rustc was not available after setup-rust:\n{logs}" )