Dependency pinning
A Gleam git dependency names a ref, and the two values you can give it are
wrong in opposite ways. A symbolic ref (ref = "v0.4") is readable but not
reproducible: the tag moves and the next resolve silently follows it. A bare
commit SHA is reproducible but loses the intent, so nobody knows what to bump
it to. trellis pin keeps both, in the same style
ratchet uses for GitHub Actions: the
ref becomes the SHA, and the ref it tracks moves into a comment on the same
line.
trellis pin [pkgs...] # resolve symbolic refs to SHAs, record the intenttrellis pin --update [pkgs...] # re-resolve each recorded intent to its latest SHAtrellis pin --check [pkgs...] # fail if a pinned SHA drifted from its tracked reftrellis pin --unpin [pkgs...] # restore the symbolic refsThis is the consumer half of series tags: a
release force-moves lat_cli-v0.4 so consumers can track the series, and
pin is how a consumer follows that tag deliberately instead of silently.
It works the same on any moving ref — a branch, or another tool’s tags.
Pinning
Section titled “Pinning”pin scans [dependencies] and [dev-dependencies] of the selected
packages, all of them when none are named, for git requirements whose ref is
not already a full commit SHA. Each ref is resolved with
git ls-remote <url> <ref>, so there is no clone, any host works, and
authentication goes through git’s own credential machinery. An annotated tag
pins the commit it points at, not the tag object. Then ref is rewritten and
the original recorded in a # trellis:pin comment on the dependency’s own
line:
[dependencies]vestibule = { git = "https://github.com/example/monorepo.git", ref = "v0.4", path = "packages/vestibule" }vestibule = { git = "https://github.com/example/monorepo.git", ref = "93deb4c38d4b3f5848681ff7e9d59883db751c67", path = "packages/vestibule" } # trellis:pin v0.4$ trellis pin[lat_cli] pinned vestibule 93deb4c tracking v0.4The rest of the file is preserved byte for byte, and the locked commit in
manifest.toml is patched surgically, with no gleam update and no Hex
traffic. A package without a manifest is fine: gleam locks the pinned commit
on its next download.
The comment is the record of intent. It lives on the dependency’s own line, so it survives copy-paste between manifests, and there is no separate table to orphan when a dependency is removed. A dependency whose comment is deleted stops updating: it is still pinned, but no longer followed. The converse also holds: a hand-written SHA with no comment is left alone, because there is no recorded ref to follow.
Following the ref
Section titled “Following the ref”--update re-resolves every recorded ref and rewrites the SHAs that moved:
$ trellis pin --update[lat_cli] updated vestibule 51cbad6 was 93deb4c, tracking v0.4The result is a plain gleam.toml diff to review, so following a moving tag
becomes a deliberate, visible bump instead of a silent re-resolution. A
dependency already at its ref’s tip is untouched.
--unpin restores every recorded ref and removes the comments, returning the
manifest to its pre-pin text:
$ trellis pin --unpin[lat_cli] unpinned vestibule restored v0.4Catching a force-moved ref
Section titled “Catching a force-moved ref”--check verifies that each pinned SHA is an ancestor of (or equal to) the
commit its tracked ref points at now. A SHA no longer reachable from its ref
means the tag or branch was force-moved past it, which is the supply-chain
signal this exists to catch:
$ trellis pin --checkdrift: [lat_cli] `vestibule` is pinned at 51cbad6 which is not reachable from its tracked ref `v0.4` (now 8b9acd6) — the ref may have been force-movedIt exits 1 on any drift, so it drops straight into a PR gate:
# on: pull_request- run: trellis pin --checkdoctor runs the same check, but as an advisory warning: it needs the
network, and re-pinning past a force-move is a supply-chain decision
doctor --fix must not make. A network failure degrades to a warning too,
so an offline doctor run stays useful. In --format json these findings
carry check: pinned_ref — see JSON output.
A pin that is merely behind its ref does not drift. A series tag moves
forward once per release, and the old release commit stays an ancestor of
the new one, so --check stays green through ordinary releases — being
behind is what --update is for. Drift means the ref’s history was actually
rewritten, which is why it warrants an alarm rather than a bump.