Skip to content

nix: stop stamping release binaries as dirty, and check that they are not - #693

Open
kolyshkin wants to merge 2 commits into
containers:mainfrom
kolyshkin:fix-dirty-version
Open

nix: stop stamping release binaries as dirty, and check that they are not#693
kolyshkin wants to merge 2 commits into
containers:mainfrom
kolyshkin:fix-dirty-version

Conversation

@kolyshkin

@kolyshkin kolyshkin commented Aug 26, 2026

Copy link
Copy Markdown
Collaborator

The static binaries attached to a release report a dirty tree:

$ conmon.amd64 --version
conmon version 2.2.1
commit: c8cc2c4db27531bd4e084ce7857f73cd21ee639d-dirty

The cause

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 --porcelain --untracked-files=no.

patchShebangs rewrites the interpreter line of every script it finds, which here means tracked files. Running it before make therefore guarantees that git status is 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:

MAKESHELL=[ M hack/get_ci_vm.sh  M hack/github-actions-setup  M test/run-tests.sh]

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 nixpkgs 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 unknown that git-vars falls back to when git does not work at all.

Testing

Built nix/default.nix in the nixos/nix:2.15.0 image, the same way static.yml does, before and after the change. Before: commit: <sha>-dirty. After:

$ ./conmon --version
conmon version 2.2.1
commit: 946b260af0834846aa89b586da94a29095647178

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, strings reading 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.

kolyshkin and others added 2 commits August 25, 2026 19:23
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>
@kolyshkin kolyshkin changed the title nix: do not patch shebangs before building nix: stop stamping release binaries as dirty, and check that they are not Aug 26, 2026

@jnovy jnovy left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants