CI: install the cratestack CLI via cratestack's own action - #322
Merged
Conversation
Both places CI installs the CLI ran `cargo install cratestack-cli --version <pin> --locked`, which compiles an ~18MB binary from source on every run. cratestack ships a composite action for exactly this (.github/actions/install-cratestack-cli): it downloads the prebuilt binary from that repo's own GitHub Releases, verifies it against the published .sha256 sidecar, and puts it on PATH — no Rust toolchain involved. That is both faster and a supply-chain check `cargo install` never performed. Pinned to @v0.8.3 rather than @main, per this repo's own rule against unpinned dependencies inside a pipeline. v0.8.3 is also the first tag carrying the installer's retry hardening (cratestack#578/#618) — v0.8.0 and earlier ship an older script that does not distinguish a transient connection failure from a permanent 404. The action ref and the `version:` input are deliberately independent, and this is NOT the duplicated-value drift AGENTS.md's release-engineering notes warn about: GitHub forbids expressions in `uses:`, so the ref cannot be derived from the pin even in principle. `cargo xtask cratestack-pin` remains the single source of truth for which version is installed; the ref only selects which revision of the installer script runs. Both comment blocks say so, so the next reader does not "fix" them into agreement. The `js` job still shells out to cargo, but only to read the pin — a second, toolchain-free parser there would recreate exactly the triplicated extraction #204 closed. Verified by reproducing the action's own download path for the exact asset ubuntu-latest will fetch: cratestack-cli-x86_64-unknown-linux- gnu-v0.8.3.tar.gz and its .sha256 both return HTTP 200, the checksum matches (25429742f9c6adf7...), and the archive contains exactly the bare `cratestack` binary the action's extract step expects, as a linux x86-64 ELF. actionlint clean (local and via docker); `cargo xtask workflow-paths` passes. Also corrects two stale paths in a comment block this change already touches: schema/migrations/... -> backends/migrations/..., and schema/schema.cstack -> schemas/vsms.cstack. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
5 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Replaces both
cargo install cratestack-cli --version <pin> --lockedsteps in CI with cratestack's own composite action,install-cratestack-cli. It downloads the prebuilt binary from that repo's GitHub Releases, verifies it against the published.sha256sidecar, and puts it onPATH— no Rust toolchain, no source build.Intent
Maintainer request: "cratestack has a gh action … let's use it."
cargo install cratestack-cli --lockedcompiles an ~18MB binary from source on every run, in two separate jobs. The action does a download + checksum instead — faster, and it adds a supply-chain checkcargo installnever performed.Scope
Two steps in
.github/workflows/ci.yml:schema-drift(0001_init matchescratestack migrate diff)js(pnpm biome, typecheck, build, test)Both keep
cargo xtask cratestack-pinas the version source. Nothing else changes.Two decisions worth stating
Pinned
@v0.8.3, not@main. This repo's own release-engineering notes treat an unpinned dependency inside a pipeline as real, not hypothetical, breakage. v0.8.3 is also the first tag carrying the installer's retry hardening (cratestack#578/#618) — checked, rather than assumed: v0.8.0 and v0.7.16 both point at an older blob (f8682c1), while v0.8.3 matchesmain(53033f5). That hardening is precisely the distinction between a transient connection failure and a permanent 404, which matters for a step that gates two jobs.The action ref and the
version:input are independent, and that is not drift. GitHub forbids expressions inuses:, so the ref cannot be derived from the pin even in principle.cargo xtask cratestack-pinstays the single source of truth for which version is installed; the ref only selects which revision of the installer script runs. Both comment blocks say so explicitly, so the next reader doesn't "fix" them into agreement and then wonder why bumping one didn't change the other.Deliberately NOT done
jsjob still shells out tocargo— only to read the pin. A second, toolchain-free parser there (ased) would recreate exactly the triplicated extraction CI never checks that committed migrations match schema.cstack #204 closed, which had already drifted once. The install step no longer needs a toolchain; reading the pin still does.actionlintwas not added to CI. Used locally to check this change; wiring it up as a gate is a separate decision.Verification
actionlintclean — both the local install andrhysd/actionlintvia Docker, exit 0.cargo xtask workflow-pathspasses (36 paths). YAML re-parsed to confirm both steps resolve to the action with the expectedwith:block.But actionlint only proves the YAML is well-formed, not that the action can fetch anything — the failure mode that actually matters here is an asset-name mismatch, which is invisible to a linter. So I reproduced the action's own download path for the exact asset
ubuntu-latestwill request, using its own curl flags:That covers steps 2–5 of the action (target-triple resolution, asset URL, checksum, extract layout). Step 1 (version resolution) is skipped entirely because we pass an explicit version rather than
latest.The real gate is this PR's own CI run — these two jobs are the change.
schema-driftpassing means the action delivered a working 0.8.3 CLI, sincemigrations-currentregenerates DDL and byte-compares it;jspassing meansjust client-gen/client-checkgot one too.Screenshots / Evidence
Terminal output inline above.
Risk Assessment
What breaks if I'm wrong: two CI jobs fail loudly and immediately —
schema-driftandjsboth invokecratestackright after installing it. There is no silent-failure path: a missing asset is a hard::error::, a checksum mismatch is a hard::error::, and a CLI that installed but doesn't run fails atcargo xtask migrations-currentorjust client-gen. Nothing merges tomainon a bad install.The one non-obvious risk: this makes CI depend on cratestack's GitHub Releases rather than crates.io. If a release asset were ever deleted or renamed, these jobs break where
cargo installwould have kept working. Mitigated by the checksum sidecar being published alongside, by the pinned ref, and by both being verified above — but it is a genuine change in what CI depends on, not a pure speedup.No production code, no runtime behaviour, no schema, no migrations touched.
AI Usage Declaration
Claude read the action's
action.ymlin full before adopting it, chose and justified the pinned ref against the actual blob SHAs at each tag, made the edits, and reproduced the download path. Every claim above is the output of a command that was run.Reviewer Focus
@v0.8.3ref. A commit SHA would be stricter supply-chain practice, but every other action in this workflow is tag-pinned (actions/checkout@v4,taiki-e/install-action@v2,EmbarkStudios/cargo-deny-action@v2), so I matched house convention. Happy to switch to@53033f5if you'd rather.jsjob should keep touching cargo at all. Reading the pin is the only reason left. Alternatives all mean a second parser, which CI never checks that committed migrations match schema.cstack #204 explicitly closed — but if you'd accept, say, committing the pin to a small file that both cargo and bash can read, that's a different design worth its own change.Checklist
docs/roadmap.mdchecked — no edit needed; this changes no milestone, gate, dependency or decision.action.ymldocuments.