Summary
A consumer who pins the Action to an immutable commit SHA still gets a floating surf binary, because version defaults to latest:
action.yml — inputs.version has default: latest
install.sh — tag="${SURF_VERSION:-latest}", then resolves releases/latest over the API at run time
So this workflow, which looks fully pinned:
- uses: Connorrmcd6/surface@091b937ae34ac81a02386604fed977dd24f1f0cf # v0.8.0
with:
args: check
installs whatever the newest release happens to be. A new release can change gate behaviour on a repo whose workflow file and hubs did not change — and a surf check verdict is a merge gate, so a behaviour change lands as a red check nobody can attribute to a local edit. This recurs on every release, for every consumer who pinned the SHA and reasonably assumed that was sufficient.
This is the remaining half of #118. That PR moved the installer to the copy bundled at the pinned ref, motivated by exactly this consumer:
A consumer who pins the action to an immutable commit SHA for supply-chain reasons —
#118 made the script provenance follow the SHA pin. The binary version the script downloads still does not.
Confirmed on a real run: with version: v0.8.0 set explicitly the log reads surf: downloading v0.8.0 (x86_64-unknown-linux-gnu) followed by surf: verifying checksum.... Without it, the default path resolves latest instead.
To preempt the obvious objection: the checksum verification added in #39/#60 is working correctly here and is not in question. It proves the download matches its own published hash — not that it is the version the consumer pinned.
Suggested fix
Have the release job write the tag into a VERSION file alongside the bundled install.sh, and default the binary version to that file instead of the literal latest. Concretely:
-
release workflow writes the tag (e.g. v0.8.0, one line, no trailing newline needed) to VERSION in the repo root, committed as part of the release commit so it exists at the tagged ref
-
action.yml changes inputs.version to default: '', and the install step resolves it:
env:
SURF_VERSION: ${{ inputs.version }}
run: |
[ -n "$SURF_VERSION" ] || SURF_VERSION="$(cat "${{ github.action_path }}/VERSION")"
export SURF_VERSION
sh "${{ github.action_path }}/install.sh"
install.sh needs no change — it already honours SURF_VERSION. Because both files are read at the pinned action ref, a SHA-pinned uses: then transitively pins the binary, completing what #118 started with no change required in consumer workflows. An explicit version: still overrides, and version: latest remains available for anyone who wants the floating behaviour.
A smaller alternative, if the floating default is deliberate: note in the README's Action section that version: must be set alongside a SHA pin, since the pin alone reads as sufficient.
Observed while adopting Surface as a CI docs-drift gate on a Rust workspace; verified against main HEAD and v0.8.0 on 2026-07-28.
Summary
A consumer who pins the Action to an immutable commit SHA still gets a floating
surfbinary, becauseversiondefaults tolatest:action.yml—inputs.versionhasdefault: latestinstall.sh—tag="${SURF_VERSION:-latest}", then resolvesreleases/latestover the API at run timeSo this workflow, which looks fully pinned:
installs whatever the newest release happens to be. A new release can change gate behaviour on a repo whose workflow file and hubs did not change — and a
surf checkverdict is a merge gate, so a behaviour change lands as a red check nobody can attribute to a local edit. This recurs on every release, for every consumer who pinned the SHA and reasonably assumed that was sufficient.This is the remaining half of #118. That PR moved the installer to the copy bundled at the pinned ref, motivated by exactly this consumer:
#118 made the script provenance follow the SHA pin. The binary version the script downloads still does not.
Confirmed on a real run: with
version: v0.8.0set explicitly the log readssurf: downloading v0.8.0 (x86_64-unknown-linux-gnu)followed bysurf: verifying checksum.... Without it, the default path resolveslatestinstead.To preempt the obvious objection: the checksum verification added in #39/#60 is working correctly here and is not in question. It proves the download matches its own published hash — not that it is the version the consumer pinned.
Suggested fix
Have the release job write the tag into a
VERSIONfile alongside the bundledinstall.sh, and default the binary version to that file instead of the literallatest. Concretely:release workflow writes the tag (e.g.
v0.8.0, one line, no trailing newline needed) toVERSIONin the repo root, committed as part of the release commit so it exists at the tagged refaction.ymlchangesinputs.versiontodefault: '', and the install step resolves it:install.shneeds no change — it already honoursSURF_VERSION. Because both files are read at the pinned action ref, a SHA-pinneduses:then transitively pins the binary, completing what #118 started with no change required in consumer workflows. An explicitversion:still overrides, andversion: latestremains available for anyone who wants the floating behaviour.A smaller alternative, if the floating default is deliberate: note in the README's Action section that
version:must be set alongside a SHA pin, since the pin alone reads as sufficient.Observed while adopting Surface as a CI docs-drift gate on a Rust workspace; verified against
mainHEAD andv0.8.0on 2026-07-28.