Skip to content

fix(gate): skip the Rust build cache on a repo with no Cargo manifest - #55

Merged
forkwright merged 1 commit into
mainfrom
fix/rust-cache-needs-a-manifest
Aug 25, 2026
Merged

fix(gate): skip the Rust build cache on a repo with no Cargo manifest#55
forkwright merged 1 commit into
mainfrom
fix/rust-cache-needs-a-manifest

Conversation

@forkwright

Copy link
Copy Markdown
Owner

Finding

Swatinem/rust-cache shells out to cargo metadata, which fails outright on a repo with no Cargo.toml:

commandFailed: {
command: 'cargo metadata --all-features --format-version 1 --no-deps',
stderr: 'error: could not find `Cargo.toml` ...'
}

The step ran unconditionally, so it failed the entire gate for a cache it could never have populated.

Not every consumer is a Rust repo

typikon calls this gate with fmt_cmd: "true", clippy_cmd: "true", nextest_cmd: "true" and uses check_cmd for its own non-Rust checks. That is deliberate — its caller carries a comment saying the repo "has no Rust code of its own". Confirmed: there is no Cargo.toml anywhere in typikon's tree.

Its gate has therefore been failing on a cache step rather than on anything it actually checks. Same shape applies to any other non-Rust consumer.

This is the fleet's own standard as a defect rather than as prose — FLEET-REPO-SETUP.md says adding Rust CI to a repo with no crates "produces a job that fails looking for a build, on every push, forever." That is exactly what happened here, from the reusable's side rather than the consumer's.

The guard

A positive test for what the cache actually needs. A repo that gains Rust later picks the cache back up with no further change; a repo that never had it stops being asked for one.

Verification

Both event-shape checkers pass against the edited file:

check_gate_evaluation.py → 10/10 cases behaved as specified. EXIT=0
check_event_shape_guards.py → OK: 16 pull_request-only values examined, none reaches git unguarded. EXIT=0

YAML parses; the five jobs are unchanged.

Note for consumers

This reaches repos that float @main on their next push. typikon currently pins this workflow by SHA with a # main comment — the shape that cannot be bumped, since this repo publishes no tags — so it will not receive the fix until that pin moves. That is being handled separately.

Swatinem/rust-cache shells out to `cargo metadata`, which fails outright where
there is no Cargo.toml: `error: could not find Cargo.toml`. The step ran
unconditionally, so it failed the whole gate for a cache it could never have
populated.
Not every consumer of this gate is a Rust repo. typikon passes `true` for fmt,
clippy and nextest and uses check_cmd for its own non-Rust checks -- deliberately,
with a comment saying so -- and has no Cargo.toml anywhere in its tree. Its gate
has been failing on this step rather than on anything it actually checks.
The guard is a positive test for what the cache needs, so a repo that gains Rust
later picks the cache back up with no further change, and a repo that never had
it stops being asked for one.
Both event-shape checkers still pass: gate evaluation 10/10, and 16
pull_request-only values examined with none reaching git unguarded.
@forkwright
forkwright merged commit 59da8cd into mainAug 25, 2026
@forkwright
forkwright deleted the fix/rust-cache-needs-a-manifest branch August 25, 2026 21:55
forkwright added a commit to forkwright/typikon that referenced this pull request Aug 25, 2026
## Finding
This repo's gate has been failing on a defect **already fixed
upstream**, and the failure looked like typikon's problem rather than a
stale pin.
The mechanism: `hybrid-gate.yml` ran `Swatinem/rust-cache`
unconditionally, which shells out to `cargo metadata` and fails outright
here —
```
error: could not find `Cargo.toml`
```
— because typikon has **no `Cargo.toml` anywhere**. That is deliberate:
this repo passes `fmt_cmd: "true"`, `clippy_cmd: "true"`, `nextest_cmd:
"true"` and uses `check_cmd` for its own non-Rust checks, and the caller
says so in a comment.
Fixed in `forkwright/.github#55`, which guards that step on a manifest
existing.
## Why the fix could not reach here
This repo pins the reusable **by SHA**, and `forkwright/.github`
publishes **no tags** — so dependabot has no version to compare a SHA
against and never touches the line. The pin does not get bumped; it
freezes.
## The previous comment's concern, answered rather than deleted
It said: a mutable branch ref lets the remote workflow's behaviour
change under an already-merged commit. That is true, and it is the real
tradeoff.
But the mitigation it proposed was a **manual** refresh — `gh api
repos/forkwright/.github/commits/main --jq '.sha'`, review the diff,
bump. Nobody ran it. So the pin did not stay reviewed; it just went
stale, far enough that the gate started failing on something fixed
upstream.
A first-party reusable that holds none of our secrets is not the
supply-chain case a SHA pin exists for. The ci-substrate SPEC settles
the convention — *"Triggers + permissions + inputs live in the caller;
logic is delegated. Pin `@main`."* A floating first-party ref **cannot**
go stale, which is a stronger property than any process for keeping it
fresh.
`akroasis` froze in exactly this way, on a revision predating a fix,
while its own comment asserted dependabot was maintaining the pin.
## Note
This PR runs its own gate against the **floated** ref, since
`pull_request` workflows execute the workflow file from the head branch
— so a green check here is evidence the fix works, not just that the
syntax parses.
Co-authored-by: forkwright <cody@forkwright.com>
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@forkwright