nix: stop stamping release binaries as dirty, and check that they are not - #693
Open
kolyshkin wants to merge 2 commits into
Open
nix: stop stamping release binaries as dirty, and check that they are not#693kolyshkin wants to merge 2 commits into
kolyshkin wants to merge 2 commits into
Conversation
The static binaries attached to a release report a dirty tree: $ conmon.amd64 --version conmon version 2.2.1 commit: c8cc2c4-dirty The source handed to nix keeps its .git, on purpose: gitMinimal is in nativeBuildInputs so that the Makefile's git-vars target can stamp the real commit into the binary. Part of what git-vars does is decide whether to append -dirty, by looking at git status. patchShebangs rewrites the interpreter of every script it finds, which here means tracked files -- hack/github-actions-setup, test/run-tests.sh and friends. Running it before make therefore guarantees that git status is never empty and that every build is stamped dirty. Nothing needs it. conmon is a C program, the build runs no script from the tree, and installPhase installs a single binary; scripts that do end up in an output are handled by the fixupPhase that nixpkgs runs anyway. Signed-off-by: Kir Kolyshkin <kolyshkin@gmail.com> Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The preceding commit fixed the release binaries reporting a dirty tree. Nothing would have caught that, and nothing would catch it coming back: the version string is only ever read by whoever runs the binary. Check it in the workflow that builds it. Four of the five binaries cannot be run on the runner, so read the string out of the binary rather than asking conmon for it, which works the same way for every architecture. A mismatch means the Makefile's git-vars target saw something other than a clean checkout of this commit: a build step that modified a tracked file, or a sandbox where git did not work at all and the commit came out as "unknown". Signed-off-by: Kir Kolyshkin <kolyshkin@gmail.com> Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
jnovy
approved these changes
Aug 26, 2026
jnovy
left a comment
Collaborator
There was a problem hiding this comment.
LGTM - clean, well-scoped fix. The root cause analysis is thorough, the patchShebangs removal is correct (conmon is pure C, no scripts invoked during build), and the strings-based version check is a clever solution for cross-compiled binaries. Ship it.
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.
The static binaries attached to a release report a dirty tree:
The cause
The source handed to nix keeps its
.giton purpose:gitMinimalis innativeBuildInputsso that the Makefile'sgit-varstarget can stamp the real commit into the binary. Part of whatgit-varsdoes is decide whether to append-dirty, by looking atgit status --porcelain --untracked-files=no.patchShebangsrewrites the interpreter line of every script it finds, which here means tracked files. Running it beforemaketherefore guarantees thatgit statusis never empty and that every build is stamped dirty. Instrumenting the build phase, reading the status the same way the Makefile does, shows exactly that:Nothing needs it. conmon is a C program, the build runs no script from the tree, and
installPhaseinstalls a single binary; scripts that do end up in an output are handled by thefixupPhasenixpkgs runs anyway.The check
Nothing would have caught this, and nothing would catch it coming back, since the version string is only ever read by whoever runs the binary. The second commit checks it in the workflow that builds it. Four of the five binaries cannot be run on the runner, so the check reads the string out of the binary instead of asking conmon for it, which works the same way for every architecture.
It fails on a dirty stamp, and equally on a commit that is not the one being built — including the
unknownthatgit-varsfalls back to when git does not work at all.Testing
Built
nix/default.nixin thenixos/nix:2.15.0image, the same waystatic.ymldoes, before and after the change. Before:commit: <sha>-dirty. After:which matches HEAD exactly. The binary is otherwise unchanged: still a stripped, statically linked ELF, byte-for-byte the same size.
The check itself was run against three real binaries — the fixed one, and the dirty v2.2.1 and test-build ones — and passes and fails as it should, including when handed the wrong commit. It was then run for real by pushing a scratch tag to my fork, where all five architectures passed it,
stringsreading the string out of the ppc64le, riscv64 and s390x binaries just as well as the native ones. The tag and branch have since been deleted.