Add bin/bump-deps: one command to see and bump every pinned dependency - #94
Merged
Conversation
- .chezmoiversion: 2.70.5 -> 2.71.1 (John's change). This is a floor, not a pin: it is the minimum chezmoi that may read this source directory. It now equals install.sh's CHEZMOI_VERSION, which is the constraint that matters -- if the floor ever exceeds the pinned installer, a fresh bootstrap installs chezmoi and then chezmoi refuses to run. bin/bump-deps asserts that. - CLAUDE.md: note that the GitLab SSH block was retired, so a reader is not left hunting for config that is deliberately gone.
Pins were scattered across four files with no shared mechanism, and Dependabot
reads none of them, so drift was invisible. bin/bump-deps walks all of them.
The design point worth keeping: a pin is not always a version. Four classes,
handled differently, because conflating them would be actively harmful --
auto-updating a GPG key fingerprint would defeat the entire reason it is pinned.
VERSION install.sh, ci.yaml, linux installs. Latest release, automatic.
EXTERNAL the 14 .chezmoiexternal.yaml entries. Move to master HEAD and
recompute the checksum, confirmed per entry with a compare URL,
because it pulls upstream code nobody has read into every shell.
CONTENT RUSTUP_INIT_SHA256 -- a hash over an *unversioned* URL. There is no
version to compare; a change means upstream rewrote the installer.
Reported, and requires --accept.
ANCHOR EZA_KEY_FPR -- a GPG public-key fingerprint. NEVER rewritten. A
mismatch is key rotation or an attack; it prints a security notice
and tells you to verify against upstream's own announcement.
It also asserts cross-file invariants that nothing previously checked:
- .chezmoiversion must not exceed install.sh's CHEZMOI_VERSION. The floor is the
minimum chezmoi permitted to read this source, so if it outruns the version the
bootstrap installs, a fresh machine installs chezmoi and is then refused by it.
Verified by simulating a 2.99.0 floor.
- no external may track a moving ref (master/HEAD)
- Actions must be pinned to an exact release, not a floating major tag
That last check found a real one: gitleaks-action was pinned to `v3`, which
upstream moves. Now v3.0.0.
Python rather than shell because this needs GitHub API parsing, sha256 over
downloads and per-class logic; it runs by hand so interpreter startup is
irrelevant. Stdlib only -- no pip, no venv -- since a dependency tool that needs
dependencies is its own bootstrap problem. Deliberately no YAML library: 36 of
.chezmoiexternal.yaml's 136 lines are comments explaining why things are pinned,
and load/dump would erase them, so edits are line-oriented and surgical.
- bin/bump-deps: new. --self-test proves the in-place editing changes only the
pinned value and no surrounding syntax, then restores every file; it is wired
into CI so that guarantee is enforced rather than assumed.
- .github/workflows/deps.yaml: new. Weekly --check, keeps one tracking issue in
sync, titles CONTENT/ANCHOR changes as security signals. Never commits.
- .github/workflows/ci.yaml: py_compile step, because the shellcheck steps select
by shebang and would skip Python entirely; plus the self-test; plus the
gitleaks-action pin fix.
- .gitignore: __pycache__/ from the py_compile step.
- CLAUDE.md: document the tool, the four classes, and the invariants.
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.
Addresses topic #4 from yesterday's backlog. Pins were spread across four files with no shared mechanism, and Dependabot reads none of them — so drift was invisible until someone went looking.
The design point
A pin is not always a version. There are four classes here, and a tool that treated them uniformly would be actively harmful — auto-updating a GPG key fingerprint would defeat the entire reason it's pinned.
VERSIONinstall.sh,ci.yaml, linux installsEXTERNAL.chezmoiexternal.yamlsha256. Confirmed per entry with a compare URL, because it pulls upstream code nobody has read into every interactive shell and tmux session.CONTENTRUSTUP_INIT_SHA256--accept.ANCHOREZA_KEY_FPRTwo things that look like pins but aren't, and are left alone:
.chezmoiversionis a floor, not a pin; Homebrew and apt packages are deliberately rolling.Cross-file invariants
Nothing previously checked these:
.chezmoiversion≤install.sh'sCHEZMOI_VERSION. The floor is the minimum chezmoi permitted to read this source. If it outruns the version the bootstrap installs, a fresh machine installs chezmoi and is then refused by it — silent bootstrap breakage. Verified by simulating a 2.99.0 floor.master/HEAD).Invariant 3 found a real one:
gitleaks-actionwas pinned tov3, a tag upstream moves. Nowv3.0.0.Interface
After
--applyit runschezmoi apply --dry-run --refresh-externalsand re-checks, so a bad checksum fails immediately rather than at your next real apply.Implementation choices
Python, stdlib only — no pip, no venv, no
requirements.txt. A dependency tool that needs dependencies is its own bootstrap problem.ghsupplies the API (already in both package lists, authenticated on CI). It runs by hand, so interpreter startup is irrelevant — unlike thebin/shell scripts on thePATHhot path.No YAML library, deliberately. 36 of
.chezmoiexternal.yaml's 136 lines are comments explaining why each thing is pinned.yaml.safe_load/dumpwould erase every one. Edits are line-oriented so the file stays byte-stable apart from the values being changed.Verification
Everything below was executed, not reasoned about:
--self-testrewrites each pin to a sentinel and asserts only the captured value changed — no surrounding syntax, no line-count change — then restores every file. It's wired into CI so that guarantee is enforced rather than assumed. Confirmsactions/checkoutcorrectly edits all 4 occurrences and the externals edit preserves comments.EZA_KEY_FPRproduces the security notice, exits non-zero, and is not rewritten even under--apply("nothing to do"). A changedRUSTUP_INIT_SHA256is reported and left alone until--accept rustup.master.tar.gz).--checkexits 0 with everything current.shellcheckcorrectly skips the Python file (shebang selection), which is exactly why thepy_compilestep was added — otherwise it'd be unchecked.applyjob's no-drift gate.Scheduled check
.github/workflows/deps.yaml— weekly cron plusworkflow_dispatch. Runs--check, keeps one tracking issue in sync (updates rather than reopening), closes it when everything is current, and titlesCONTENT/ANCHORchanges as security signals rather than routine updates. It never commits.The cron is the one thing I can't verify here; it needs a real scheduled run or a manual
workflow_dispatch. Worth triggering once by hand after merge.Also in this PR
The two housekeeping changes that were sitting uncommitted: your
.chezmoiversionbump to 2.71.1, and the CLAUDE.md note that the GitLab SSH block was retired.