Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .github/actions/setup-rust/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -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`.
Expand Down
9 changes: 5 additions & 4 deletions .github/actions/setup-rust/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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@7d986dd989559c6ecdb630a3fd2557667be217ad`,
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

Expand Down
16 changes: 8 additions & 8 deletions .github/actions/setup-rust/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand All @@ -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
Expand All @@ -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') }}
Expand Down
27 changes: 27 additions & 0 deletions .github/actions/setup-rust/tests/test_setup_rust_manifest.py
Original file line number Diff line number Diff line change
Expand Up @@ -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]]:
Expand Down Expand Up @@ -198,6 +211,20 @@ 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, (
f"{step_name} must use the verified revision {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")
Expand Down
44 changes: 44 additions & 0 deletions .github/workflows/test-rustflags-export.yml
Original file line number Diff line number Diff line change
Expand Up @@ -94,3 +94,47 @@ 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: 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:
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)"
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}]"
Comment thread
coderabbitai[bot] marked this conversation as resolved.
19 changes: 19 additions & 0 deletions docs/developers-guide.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
8 changes: 8 additions & 0 deletions docs/users-guide.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
74 changes: 70 additions & 4 deletions tests/workflows/test_rustflags_export_workflow.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,10 @@
from __future__ import annotations

import re
import typing as typ
from pathlib import Path

import pytest
import yaml

from .conftest import (
FIXTURES_DIR,
Expand All @@ -22,10 +23,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"
Expand All @@ -49,6 +48,56 @@ 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 = 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"
)
assert setup_step["with"] == {
"toolchain": "stable",
"install-binstall": "false",
"use-sccache": "false",
}, "Setup stable Rust must select the isolated stable toolchain path"

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, "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"
)
Comment on lines +69 to +98

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.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Assert the stable-toolchain removal precondition.

Add a structural assertion for the Remove the preinstalled stable toolchain step. Require its script to uninstall stable and verify that rustup run stable rustc fails before local setup-rust runs.

Without this assertion, a later removal of the isolation step can make the Act test pass from a runner-provided stable toolchain.

Proposed fix
+    removal_steps = [
+        step
+        for step in steps
+        if step.get("name") == "Remove the preinstalled stable toolchain"
+    ]
+    assert len(removal_steps) == 1, "expected exactly one stable removal step"
+    removal_script = removal_steps[0].get("run")
+    assert isinstance(removal_script, str), "stable removal step must have a script"
+    assert "rustup toolchain uninstall stable" in removal_script, (
+        "stable removal step must uninstall the stable toolchain"
+    )
+    assert "rustup run stable rustc" in removal_script, (
+        "stable removal step must verify stable is unavailable"
+    )
+
     setup_steps = [step for step in steps if step.get("name") == "Setup stable Rust"]
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/workflows/test_rustflags_export_workflow.py` around lines 69 - 98,
Extend the workflow structure test around the existing setup_steps and
verify_steps assertions to locate exactly one “Remove the preinstalled stable
toolchain” step before “Setup stable Rust”. Assert its run script uninstalls the
stable toolchain and verifies that “rustup run stable rustc” fails, ensuring the
local setup-rust action is tested without relying on a runner-provided stable
toolchain.



@skip_unless_act
@skip_unless_workflow_tests
def test_rust_build_release_exports_rustflags_to_later_steps(
Expand Down Expand Up @@ -117,3 +166,20 @@ 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_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}"
)
assert re.search(r"setup_rust_cargo=\[cargo \d+\.\d+\.\d+", logs), (
f"cargo was not available after setup-rust:\n{logs}"
)
Loading