Skip to content

cargo-out-dir: resolve a build script's OUT_DIR as an action output #25

cargo-out-dir: resolve a build script's OUT_DIR as an action output

cargo-out-dir: resolve a build script's OUT_DIR as an action output #25

Workflow file for this run

name: selftest
# Exercise every composite action end-to-end against the fixture crate, so the
# actions are verified before any consumer relies on them.
on:
push:
branches: [main]
pull_request:
permissions:
contents: read
jobs:
selftest:
name: exercise the composite actions
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- name: install-toolchain
uses: ./.github/actions/install-toolchain
with:
components: rustfmt clippy
# rust-cache: warms the cargo cache + sets CARGO_INCREMENTAL=0 for the build steps below.
- name: rust-cache (fixture)
uses: ./.github/actions/rust-cache
with:
prefix: fixture
cache-target: "true"
target-dir: fixtures/sample-crate/target
lockfiles: fixtures/sample-crate/Cargo.lock
# Prove the env export crosses into a later step (the build inherits incremental=off).
- name: rust-cache exported CARGO_INCREMENTAL=0
run: test "$CARGO_INCREMENTAL" = "0"
- name: lint-and-test (fixture)
uses: ./.github/actions/lint-and-test
with:
working-directory: fixtures/sample-crate
# cargo-out-dir: the fixture's build.rs bakes a marker into OUT_DIR;
# the action must resolve exactly that directory (dependencies' build
# scripts emit the same message — the package filter picks the right
# one).
- name: cargo-out-dir (fixture)
id: fixture_out_dir
uses: ./.github/actions/cargo-out-dir
with:
working-directory: fixtures/sample-crate
package: cicd-rust-selftest-fixture
- name: cargo-out-dir resolved the baked marker
run: |
dir="${{ steps.fixture_out_dir.outputs.out-dir }}"
test -d "$dir" || { echo "::error::out-dir is not a directory: $dir"; exit 1; }
test -f "$dir/fixture-marker" || { echo "::error::marker missing in $dir"; exit 1; }
echo "✓ OUT_DIR resolved with baked content"
# Negative control: an unknown package must fail loudly instead of
# emitting an empty path for a later step to copy from.
- name: cargo-out-dir rejects an unknown package (negative control)
id: bogus_out_dir
continue-on-error: true
uses: ./.github/actions/cargo-out-dir
with:
working-directory: fixtures/sample-crate
package: no-such-package
- name: assert the unknown package failed
run: |
if [ "${{ steps.bogus_out_dir.outcome }}" != "failure" ]; then
echo "::error::unknown package did not fail"; exit 1
fi
echo "✓ unknown package rejected"
- name: check-release-readiness (fixture)
uses: ./.github/actions/check-release-readiness
with:
working-directory: fixtures/sample-crate
expected-version: "0.1.0"
# cargo-install + cargo-use in HOST mode (docker:false): wrap a plain
# `cargo install` on the runner, then run the tool from the shared bin dir.
- name: cargo-install (host) a CLI crate into a shared cache
uses: ./.github/actions/cargo-install
with:
docker: "false"
tool: npm-utils
args: "--features cli"
locked: "false" # probe tests the action plumbing, not --locked semantics
cargo-cache: .cargo-tools-host
- name: cargo-use (host) runs the installed tool
uses: ./.github/actions/cargo-use
with:
docker: "false"
cargo-cache: .cargo-tools-host
args: "npm-utils --version"
# Negative control: a malicious tool name is rejected on the runner by the
# cargo-install allowlist before any shell runs it — no command injection.
- name: cargo-install rejects a malicious tool name (negative control)
id: evil_tool
continue-on-error: true
uses: ./.github/actions/cargo-install
with:
docker: "false"
tool: "evil; touch /tmp/cicd-pwned"
- name: assert the injection was rejected
run: |
if [ "${{ steps.evil_tool.outcome }}" != "failure" ]; then
echo "::error::malicious tool name was not rejected"; exit 1
fi
if [ -e /tmp/cicd-pwned ]; then
echo "::error::injection executed: /tmp/cicd-pwned exists"; exit 1
fi
echo "✓ malicious tool name rejected; no injection"
# The save half of rust-cache: prune target/ to dependency artifacts,
# upload under the restore's key. Runs as (nearly) the last step, the
# position consumers use; a fresh key exercises prune + upload, an
# exact-hit re-run skips both (the stored entry is already pruned).
- name: rust-cache-save (fixture; prune + upload)
uses: ./.github/actions/rust-cache-save
with:
working-directory: fixtures/sample-crate
- name: prune kept the dependency and dropped the member
if: env.RUST_CI_TARGET_HIT != 'true'
run: |
deps=fixtures/sample-crate/target/debug/deps
ls "$deps" | grep -q '^liblibc-' || {
echo "::error::dependency artifacts missing after prune"; exit 1; }
if ls "$deps" | grep -q '^cicd_rust_selftest_fixture-'; then
echo "::error::workspace member artifacts survived the prune"; exit 1
fi
echo "✓ prune kept libc, dropped the member"
# save: false is the pull-request mode — the step must no-op.
- name: rust-cache-save honours save=false (no-op control)
uses: ./.github/actions/rust-cache-save
with:
save: "false"
working-directory: fixtures/sample-crate
# The changelog action, both modes, against the fixture crate: the check gate
# (bump present, bump missing, breaking without more than a patch bump) and
# the cut (heading + compare link rewritten, the version exported, a second
# cut refused once nothing is unreleased).
selftest-changelog:
name: exercise the changelog action
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
# cargo metadata resolves the crate version; the runner's default toolchain suffices.
- name: install-toolchain
uses: ./.github/actions/install-toolchain
- name: check passes with a bumped version
uses: ./.github/actions/changelog
with:
mode: check
working-directory: fixtures/sample-crate
baseline-version: "0.0.9"
- name: check fails without a bump (negative control)
id: nobump
continue-on-error: true
uses: ./.github/actions/changelog
with:
mode: check
working-directory: fixtures/sample-crate
baseline-version: "0.1.0"
- name: stage a breaking entry on a patch bump
run: |
rm -rf "$RUNNER_TEMP/breaking"
rsync -a --exclude='target*' fixtures/sample-crate/ "$RUNNER_TEMP/breaking/"
sed -i 's/^version = "0.1.0"/version = "0.1.1"/' "$RUNNER_TEMP/breaking/Cargo.toml"
awk '{print} /^## \[Unreleased\]/{print ""; print "- **Breaking:** a sample breaking entry."}' \
"$RUNNER_TEMP/breaking/CHANGELOG.md" > "$RUNNER_TEMP/breaking/CHANGELOG.md.new"
mv "$RUNNER_TEMP/breaking/CHANGELOG.md.new" "$RUNNER_TEMP/breaking/CHANGELOG.md"
- name: check refuses a breaking entry on a patch bump (negative control)
id: breaking_patch
continue-on-error: true
uses: ./.github/actions/changelog
with:
mode: check
working-directory: ${{ runner.temp }}/breaking
baseline-version: "0.1.0"
- name: check accepts a breaking entry with a minor bump
run: sed -i 's/^version = "0.1.1"/version = "0.2.0"/' "$RUNNER_TEMP/breaking/Cargo.toml"
- uses: ./.github/actions/changelog
with:
mode: check
working-directory: ${{ runner.temp }}/breaking
baseline-version: "0.1.0"
- name: stage a copy for the cut
run: |
rm -rf "$RUNNER_TEMP/cut"
rsync -a --exclude='target*' fixtures/sample-crate/ "$RUNNER_TEMP/cut/"
- name: cut releases the [Unreleased] section
uses: ./.github/actions/changelog
with:
mode: cut
working-directory: ${{ runner.temp }}/cut
date: "2026-02-02"
- name: the cut rewrote the heading and link and exported the version
run: |
grep -q '^## \[0.1.0\] - 2026-02-02$' "$RUNNER_TEMP/cut/CHANGELOG.md"
grep -q '^\[0.1.0\]: https://example.invalid/sample/compare/v0.0.9\.\.\.v0.1.0$' \
"$RUNNER_TEMP/cut/CHANGELOG.md"
if grep -q 'Unreleased' "$RUNNER_TEMP/cut/CHANGELOG.md"; then
echo "::error::an [Unreleased] remnant survived the cut"; exit 1
fi
test "$CHANGELOG_VERSION" = "0.1.0"
echo "✓ cut rewrote the section, the link, and exported CHANGELOG_VERSION"
- name: a second cut is refused (negative control)
id: cut_again
continue-on-error: true
uses: ./.github/actions/changelog
with:
mode: cut
working-directory: ${{ runner.temp }}/cut
- name: assert the negative controls failed
run: |
if [ "${{ steps.nobump.outcome }}" != "failure" ]; then
echo "::error::a missing bump was not refused"; exit 1
fi
if [ "${{ steps.breaking_patch.outcome }}" != "failure" ]; then
echo "::error::a breaking entry on a patch bump was not refused"; exit 1
fi
if [ "${{ steps.cut_again.outcome }}" != "failure" ]; then
echo "::error::a second cut was not refused"; exit 1
fi
echo "✓ all changelog negative controls refused"
# The Docker action set: build the image locally (no GHCR dependency to prove)
# and run fetch (networked) -> lint/test (sealed, --network none) -> readiness
# (networked) against the fixture.
selftest-docker:
name: exercise the Docker actions
runs-on: ubuntu-latest
# CICD_PROBE rides the runner env so the env-passthrough proofs below can
# (conditionally) forward it into the container; the expectation is supplied
# per step via the literal `env` input (CICD_EXPECT) so it always crosses.
env:
CICD_PROBE: forwarded
steps:
- uses: actions/checkout@v6
- name: build-image (local, cached)
uses: ./.github/actions/build-image
with:
tag: rust-ci:selftest
cache: "true"
# Negative control: the sealing flag seal_run applies (--network=none) really
# blocks egress — a curl from inside a sealed container must fail.
- name: sealed network is blocked (negative control)
run: |
if docker run --rm --network=none --cap-drop=ALL --security-opt=no-new-privileges \
rust-ci:selftest bash -c 'curl -sSf --max-time 5 https://crates.io >/dev/null 2>&1'; then
echo "::error::network reachable inside a --network=none container"; exit 1
fi
echo "✓ --network=none blocks network egress"
- name: cargo-fetch (networked)
uses: ./.github/actions/cargo-fetch
with:
image: rust-ci:selftest
working-directory: fixtures/sample-crate
# cargo-docker: a sealed build off the warmed cache (exercises the low-level wrapper).
- name: cargo-docker (sealed build)
uses: ./.github/actions/cargo-docker
with:
image: rust-ci:selftest
working-directory: fixtures/sample-crate
args: "build --locked"
- name: lint-and-test (sealed, --network none) + env passthrough
uses: ./.github/actions/lint-and-test-docker
with:
image: rust-ci:selftest
working-directory: fixtures/sample-crate
# Positive proof: env-include forwards CICD_PROBE across the sealed
# boundary; CICD_EXPECT=present makes the fixture assert it arrived.
env-include: "CICD_.*"
env: |
CICD_EXPECT=present
# Negative control: the DEFAULT include (CARGO_.*) must NOT forward an
# unrelated host var. CICD_PROBE is in the runner env but should not cross.
- name: lint-and-test (default include does not leak)
uses: ./.github/actions/lint-and-test-docker
with:
image: rust-ci:selftest
working-directory: fixtures/sample-crate
env: |
CICD_EXPECT=absent
# Prove the no-warmup path: networked, a cold cache, no cargo-fetch before it.
- name: lint-and-test (networked, no warmup) + exclusion wins
uses: ./.github/actions/lint-and-test-docker
with:
image: rust-ci:selftest
working-directory: fixtures/sample-crate
offline: "false"
cargo-cache: .cargo-cache-online
target-dir: target-online
# Exclusion wins: CICD_PROBE matches env-include but is also in
# env-exclude, so it must NOT cross (CICD_EXPECT=absent asserts that).
env-include: "CICD_.*"
env-exclude: "CARGO_HOME|RUSTUP_HOME|CARGO_TARGET_DIR|CICD_PROBE"
env: |
CICD_EXPECT=absent
- name: publish-dry-run (networked prep, sealed verify-build)
uses: ./.github/actions/publish-dry-run
with:
image: rust-ci:selftest
working-directory: fixtures/sample-crate
expected-version: "0.1.0"
# cargo-install + cargo-use: install a real CLI crate into a SHARED cache in
# one sealed container, then run it from a SEPARATE sealed, offline container.
# Proves an installed tool persists in the host-mounted CARGO_HOME and resolves
# via the prepended bin dir — including the `cargo <subcommand>` form. npm-utils
# is pure-Rust (no openssl) and gates its binaries behind the `cli` feature, so
# this also exercises the free-form `args` pass-through.
- name: cargo-install (sealed) a CLI crate into the shared cache
uses: ./.github/actions/cargo-install
with:
image: rust-ci:selftest
tool: npm-utils
args: "--features cli"
locked: "false" # probe tests the action plumbing, not --locked semantics
cargo-cache: .cargo-tools
- name: both installed binaries landed in the shared host dir
run: test -x .cargo-tools/bin/npm-utils && test -x .cargo-tools/bin/cargo-npm-utils
- name: cargo-use (sealed, offline) runs the installed cargo subcommand
uses: ./.github/actions/cargo-use
with:
image: rust-ci:selftest
cargo-cache: .cargo-tools
offline: "true"
args: "cargo npm-utils --help"
# Negative control: cargo-use offline really seals the network — a tool that
# reaches out must fail (proves the action wires OFFLINE -> --network=none).
- name: cargo-use offline blocks egress (negative control)
id: use_offline_egress
continue-on-error: true
uses: ./.github/actions/cargo-use
with:
image: rust-ci:selftest
cargo-cache: .cargo-tools
offline: "true"
args: "curl -sSf --max-time 5 https://crates.io"
- name: assert cargo-use sealed the network
run: |
if [ "${{ steps.use_offline_egress.outcome }}" != "failure" ]; then
echo "::error::cargo-use offline did not block network egress"; exit 1
fi
echo "✓ cargo-use offline sealed the network"
# build-image `msrv` sentinel: build at the fixture's DECLARED rust-version, read
# from Cargo.toml (no hand-managed version), so a consumer can run the whole Docker
# pipeline on the support floor. The fixture pins 1.82.
- name: build-image resolves the msrv sentinel from Cargo.toml
uses: ./.github/actions/build-image
with:
rust-version: msrv
working-directory: fixtures/sample-crate
tag: rust-ci:bi-msrv
- name: the sentinel image IS the declared MSRV (1.82)
run: |
v="$(docker run --rm rust-ci:bi-msrv rustc --version)"
echo "$v"
case "$v" in
*" 1.82"*) echo "✓ sentinel image is rust 1.82" ;;
*) echo "::error::sentinel image is not rust 1.82: $v"; exit 1 ;;
esac
# Negative control: a non-numeric Cargo.toml rust-version is rejected by the shared
# resolver before it can become a docker tag — proving build-image wires the
# validation into the sentinel path (the smuggled command must not run).
- name: craft a bad-MSRV manifest (for the negative control)
run: |
mkdir -p "$RUNNER_TEMP/bad-msrv"
printf '[package]\nname = "bad"\nversion = "0.0.0"\nrust-version = "1.0; touch pwned"\n' \
> "$RUNNER_TEMP/bad-msrv/Cargo.toml"
- name: build-image rejects a non-numeric declared rust-version (negative control)
id: bi_bad
continue-on-error: true
uses: ./.github/actions/build-image
with:
rust-version: msrv
working-directory: ${{ runner.temp }}/bad-msrv
tag: rust-ci:bi-bad
- name: assert the bad declared rust-version was rejected
run: |
if [ "${{ steps.bi_bad.outcome }}" != "failure" ]; then
echo "::error::build-image accepted a non-numeric rust-version"; exit 1
fi
test ! -e "$RUNNER_TEMP/bad-msrv/pwned"
echo "✓ build-image rejected a non-numeric declared rust-version"
# msrv: build an image at the fixture's DECLARED rust-version and cargo check there.
# Exercises the Cargo.toml extraction, the image-at-MSRV build, and the sealed check.
# locked: true takes the committed-lockfile path — the fixture pins one.
- name: msrv (image at the declared MSRV, sealed check)
uses: ./.github/actions/msrv
with:
working-directory: fixtures/sample-crate
package: cicd-rust-selftest-fixture
locked: "true"
# Negative control: a non-numeric rust-version is rejected before it can reach a docker
# tag or shell (proves the input validation; the smuggled command must not run).
- name: msrv rejects a non-numeric rust-version (negative control)
id: msrv_bad
continue-on-error: true
uses: ./.github/actions/msrv
with:
working-directory: fixtures/sample-crate
rust-version: "latest; touch pwned"
- name: assert the bad rust-version was rejected
run: |
if [ "${{ steps.msrv_bad.outcome }}" != "failure" ]; then
echo "::error::msrv accepted a non-numeric rust-version"; exit 1
fi
test ! -e fixtures/sample-crate/pwned
echo "✓ msrv rejected a non-numeric rust-version"
# A crate with NO committed lockfile must still work: the source is mounted read-only for the
# sealed check, so the action resolves the lock up front rather than failing to write it there.
- name: stage a copy of the fixture with no Cargo.lock
run: |
rm -rf "$RUNNER_TEMP/fixture-nolock"
mkdir -p "$RUNNER_TEMP/fixture-nolock"
rsync -a --exclude='target*' --exclude='Cargo.lock' fixtures/sample-crate/ "$RUNNER_TEMP/fixture-nolock/"
test ! -e "$RUNNER_TEMP/fixture-nolock/Cargo.lock"
# Negative control: locked: true demands a committed lockfile, so a crate without one fails
# with a clear up-front error — not the cryptic read-only-filesystem failure of writing it in-container.
- name: msrv (locked true) rejects a crate with no committed lockfile
id: msrv_nolock_locked
continue-on-error: true
uses: ./.github/actions/msrv
with:
working-directory: ${{ runner.temp }}/fixture-nolock
package: cicd-rust-selftest-fixture
locked: "true"
- name: assert the missing lockfile was rejected
run: |
if [ "${{ steps.msrv_nolock_locked.outcome }}" != "failure" ]; then
echo "::error::msrv locked:true accepted a crate with no Cargo.lock"; exit 1
fi
test ! -e "$RUNNER_TEMP/fixture-nolock/Cargo.lock"
echo "✓ msrv locked:true requires a committed lockfile"
# Default (locked: false): resolve a fresh lockfile at the MSRV up front, then check it sealed.
- name: msrv (locked false) resolves a lockfile when none is committed
uses: ./.github/actions/msrv
with:
working-directory: ${{ runner.temp }}/fixture-nolock
package: cicd-rust-selftest-fixture
# The resolution happens in a disposable copy, so the lockless checkout must STAY lockless —
# the action may not leave an untracked Cargo.lock behind.
- name: assert the lockless checkout stayed lockless
run: |
test ! -e "$RUNNER_TEMP/fixture-nolock/Cargo.lock"
echo "✓ msrv locked:false left no Cargo.lock behind"
# locked: false must not rewrite a committed lockfile either: fresh resolution happens in
# the disposable copy while the checkout's Cargo.lock stays byte-identical.
- name: snapshot the committed lockfile
run: cp fixtures/sample-crate/Cargo.lock "$RUNNER_TEMP/Cargo.lock.before"
- name: msrv (locked false) resolves fresh without touching the committed lockfile
uses: ./.github/actions/msrv
with:
working-directory: fixtures/sample-crate
package: cicd-rust-selftest-fixture
- name: assert the committed lockfile is byte-identical
run: |
cmp "$RUNNER_TEMP/Cargo.lock.before" fixtures/sample-crate/Cargo.lock
echo "✓ msrv locked:false preserved the committed Cargo.lock byte-for-byte"
# Regression: a consumer's build.rs may read git metadata (git describe, the commit
# hash), so the disposable copy must preserve it — here in the plain .git DIRECTORY
# form. The fixture's build.rs asserts `git rev-parse HEAD` succeeds whenever
# FIXTURE_REQUIRE_GIT is set (forwarded into the container via the literal env input).
- name: stage a git-repo fixture with no Cargo.lock (.git directory)
run: |
rm -rf "$RUNNER_TEMP/fixture-gitdir"
mkdir -p "$RUNNER_TEMP/fixture-gitdir"
rsync -a --exclude='target*' --exclude='Cargo.lock' fixtures/sample-crate/ "$RUNNER_TEMP/fixture-gitdir/"
git -C "$RUNNER_TEMP/fixture-gitdir" init -q -b main
git -C "$RUNNER_TEMP/fixture-gitdir" -c user.name=selftest -c user.email=selftest@invalid add -A
git -C "$RUNNER_TEMP/fixture-gitdir" -c user.name=selftest -c user.email=selftest@invalid commit -q -m fixture
test -d "$RUNNER_TEMP/fixture-gitdir/.git"
- name: msrv (locked false) preserves git metadata in the copy (.git directory)
uses: ./.github/actions/msrv
with:
working-directory: ${{ runner.temp }}/fixture-gitdir
package: cicd-rust-selftest-fixture
env: |
FIXTURE_REQUIRE_GIT=1
# The same guarantee for the .git FILE form: a linked worktree of that repo (the
# form submodules use as well).
- name: stage a linked-worktree fixture (.git file)
run: |
rm -rf "$RUNNER_TEMP/fixture-gitfile"
git -C "$RUNNER_TEMP/fixture-gitdir" worktree add -q -b wt "$RUNNER_TEMP/fixture-gitfile"
test -f "$RUNNER_TEMP/fixture-gitfile/.git"
test ! -e "$RUNNER_TEMP/fixture-gitfile/Cargo.lock"
- name: msrv (locked false) preserves git metadata in the copy (.git file / worktree)
uses: ./.github/actions/msrv
with:
working-directory: ${{ runner.temp }}/fixture-gitfile
package: cicd-rust-selftest-fixture
env: |
FIXTURE_REQUIRE_GIT=1
# install-toolchain must also work on Windows. windows-latest exercises the rustup-present path;
# windows-11-arm ships no Rust at all, exercising the rustup-init.exe bootstrap. Building the
# fixture proves the toolchain is usable, not merely installed (and, on arm64, that the image
# provides the MSVC arm64 link tools).
selftest-windows:
name: install-toolchain · ${{ matrix.runner }}
strategy:
fail-fast: false
matrix:
runner: [windows-latest, windows-11-arm]
runs-on: ${{ matrix.runner }}
steps:
- uses: actions/checkout@v6
- uses: ./.github/actions/install-toolchain
with:
components: clippy
- name: toolchain is installed + on PATH
shell: bash
run: rustc --version && cargo --version && cargo clippy --version
- name: build the fixture
shell: bash
working-directory: fixtures/sample-crate
run: cargo build --locked
# rust-cache must OBEY an explicit CARGO_INCREMENTAL (not force it off): set it at job level and
# confirm the action leaves it untouched for the build steps that would follow.
selftest-cache-passthrough:
name: rust-cache obeys CARGO_INCREMENTAL
runs-on: ubuntu-latest
env:
CARGO_INCREMENTAL: "1"
steps:
- uses: actions/checkout@v6
- uses: ./.github/actions/rust-cache
with:
prefix: passthrough
- name: explicit CARGO_INCREMENTAL is preserved
run: test "$CARGO_INCREMENTAL" = "1"
# rust-cache must resolve the cargo home from CARGO_HOME (container images bake the
# toolchain outside $HOME/.cargo): seed a sentinel under a custom CARGO_HOME, let the
# post step save it, and prove a second job restores it into the same custom location.
selftest-cache-cargo-home-save:
name: rust-cache honors CARGO_HOME (save)
runs-on: ubuntu-latest
env:
CARGO_HOME: /tmp/cargo-home
steps:
- uses: actions/checkout@v6
- name: seed a sentinel in the custom cargo home
run: |
mkdir -p "$CARGO_HOME/registry/index" "$CARGO_HOME/registry/cache" "$CARGO_HOME/git/db"
echo "${{ github.run_id }}" > "$CARGO_HOME/registry/cache/selftest-sentinel"
- uses: ./.github/actions/rust-cache
with:
prefix: cargohome-${{ github.run_id }}
selftest-cache-cargo-home-restore:
name: rust-cache honors CARGO_HOME (restore)
needs: selftest-cache-cargo-home-save
runs-on: ubuntu-latest
env:
CARGO_HOME: /tmp/cargo-home
steps:
- uses: actions/checkout@v6
- uses: ./.github/actions/rust-cache
with:
prefix: cargohome-${{ github.run_id }}
save: "false"
- name: sentinel came back into the custom cargo home
run: test "$(cat "$CARGO_HOME/registry/cache/selftest-sentinel")" = "${{ github.run_id }}"
# save:"false" must not create a cache entry: run restore-only under a unique prefix,
# then assert no entry with that prefix exists.
selftest-cache-no-save:
name: rust-cache save:false saves nothing
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: ./.github/actions/rust-cache
with:
prefix: nosave-${{ github.run_id }}
save: "false"
selftest-cache-no-save-assert:
name: rust-cache save:false left no entry
needs: selftest-cache-no-save
runs-on: ubuntu-latest
permissions:
contents: read
actions: read
steps:
- name: no cache entry exists for the nosave prefix
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
COUNT=$(gh cache list --repo ${{ github.repository }} --limit 100 --json key \
--jq '[.[] | select(.key | startswith("nosave-${{ github.run_id }}-"))] | length')
test "$COUNT" -eq 0