Skip to content

Make the action say which build of itself ran (#21) - #22

Open
SebHenn wants to merge 1 commit into
mainfrom
fix/21-action-provenance
Open

Make the action say which build of itself ran (#21)#22
SebHenn wants to merge 1 commit into
mainfrom
fix/21-action-provenance

Conversation

@SebHenn

@SebHenn SebHenn commented Aug 26, 2026

Copy link
Copy Markdown
Owner

Fixes the actionable half of #21.

The tag was already correct

The issue's premise — that v1 still points at v0.3.0 — does not hold against the remote:

$ git ls-remote origin refs/tags/v1
a5717737c92fcf33bf2123ed8cc401a9c3532c27   refs/tags/v1
$ git rev-parse v0.3.1
a5717737c92fcf33bf2123ed8cc401a9c3532c27

release.yml's finalize moved it at 18:58 on release day, ~18h before the issue was
filed, and action.yml at v1 has the set +e fix at line 96. What the report observed was
a stale local tag: git fetch does not move a tag that already exists locally, so any
clone that had ever seen v1 kept reporting the old commit. This repo's own clone reproduced
it exactly (! [rejected] v1 -> v1 (would clobber existing tag)).

That makes the second half of the issue the real defect: nothing in the action's output
named the build that ran
, so the wrong conclusion was the reasonable one.

What changed

action.yml stamps its own version and prints one line before it runs anything — repeated
under the step summary and the PR comment, and exposed as the action-version /
image-digest outputs:

cifail action v0.3.1 (SebHenn/ci-failure-intelligence@v1) | cifail 0.3.1 | image ghcr.io/sebhenn/cifail:latest@sha256:…

Three separately-moving things, named: the action build, the CLI inside the image, and the
image's immutable digest (RepoDigests, not the tag the caller wrote — :latest moves too,
and runners cache). Printed first, so it survives a docker run that dies.

A stamp nobody checks lies in exactly the situation it exists for, so:

  • scripts/check-versions.sh (already run on every PR) fails when the stamp drifts from
    Directory.Build.props — verified by planting 9.9.9 and watching it fail;
  • ci.yml's existing step-script harness asserts the rendered line. The
    ${{ github.action_ref }} plumbing can only be exercised by a real uses:, so the test
    passes those vars in explicitly and pins the rendering.

Also, since it is the same failure mode

release.yml's v1 move is now guarded on both sides: skipped when the tag being
released is not the newest version tag (re-releasing an older patch line must not drag v1
backwards), and verified against git ls-remote afterwards — previously the force-push
was fire-and-forget and the job went green whether or not it landed.

Verification

Docker isn't available on the dev box, so the step script was extracted with ci.yml's own
extractor and run against a stand-in that shells out to the real CLI: provenance renders
correctly for @v1, for a local uses: ./ checkout, and for an image with no RepoDigests;
gate mode, the exit-2 usage path, and the SARIF write all still behave. YAML parses, all
shell bodies pass bash -n, check-versions.sh passes.

🤖 Generated with Claude Code

`@v1` and `:latest` are both moving references, and a runner may serve either from
cache, so nothing in a step's output identified the build that produced it. A fix
that shipped weeks ago was indistinguishable from a fix that does not work — which
is exactly how #21 was diagnosed, from a clone whose `v1` was stale because `git
fetch` does not move an existing tag. (The tag itself was moved by `finalize` on
release day; `git ls-remote` confirms v1 == v0.3.1.)

The action now stamps its own version and prints one provenance line before it runs
anything, repeating it under the step summary and the PR comment:

  cifail action v0.3.1 (SebHenn/ci-failure-intelligence@v1) | cifail 0.3.1 |
  image ghcr.io/sebhenn/cifail:latest@sha256:...

It names three separately-moving things: the action build, the CLI inside the image,
and the image's immutable digest (RepoDigests, not the tag the caller wrote). Also
exposed as the `action-version` and `image-digest` outputs. Printing it first means
it survives a `docker run` that dies.

A stamp nobody checks would lie in precisely the situation it exists for, so
check-versions.sh (already run on every PR) fails when it drifts from
Directory.Build.props, and ci.yml's existing step-script harness asserts the
rendered line.

release.yml's v1 move is guarded on both sides while here: skipped when the tag
being released is not the newest version tag (re-releasing an older patch line
must not drag v1 backwards), and verified against `git ls-remote` afterwards,
because a fire-and-forget force-push left the job green either way.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Sign up for free to 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