Plan 0024
Version-tracking forks, base the series on the source nixpkgs already pins
Forks that overlay a nixpkgs recipe (git, #3612) currently hand-pin an input that must equal nixpkgs version, guarded by a comment ritual and an eval assert. Proposal: a trackNixpkgs fork mode where the series applies to the nixpkgs src itself, so a nixpkgs bump needs zero action unless the series genuinely conflicts.
- Status
- Input wanted RFC
- Ambition
- 4 one new fork mode in existing machinery, not a new system
- Impact
- 5 deletes a manual repin ritual that will otherwise fire on every nixpkgs git bump
- Effort
- 4 rebase-patches gains a derived-base mode; patched-src consumes a package src
- Risk
- 3 dist tarball vs git tag tree deltas are the one sharp edge, and it is checkable
- Maturity
- 2 git (#3612) is the first and only nixpkgs-recipe fork; pattern is fresh
- Leverage
- 6 every future fork of a nixpkgs-packaged tool inherits zero-upkeep tracking
- Taste
- 6 the judgment is refusing a second pin for a version nixpkgs already owns
- Authors
- Created
- Updated
- Tracking issue
- -
- Supersedes
- -
- Superseded by
- -
Problem
The git fork (#3610, PR #3612) overlays nixpkgs’ git recipe onto a patched tree, which forces its base to equal nixpkgs’ git version. Today that invariant lives in upkeep:
- a
git-srcinput pinned by rev to thev2.54.0tag, with a seven-line comment explaining the ritual (“on a nixpkgs git bump, repin to the matching tag and run rebase-patches”); - an eval-time assert in
packages/gitthat fails when nixpkgs moves; autoconf+make configurein the package, because the git tag tree lacks the generatedconfigurethe kernel.org dist tarball ships.
Every nixpkgs git bump demands a human: repin, relock, regenerate dag, rebuild. One fact (git’s version) has two owners, and the second owner is a comment.
Proposal: trackNixpkgs fork mode
A fork entry declares that its base IS the nixpkgs source:
{
name = "git";
trackNixpkgs = {
attr = "git"; # pkgs.git supplies version and src
tagTemplate = "v{version}"; # upstream tag for the rebase checkout
};
url = "https://github.com/git/git.git";
patchDir = "packages/git/patches";
# no input, no autoUpdate: there is nothing to update
} Consequences, all deletions:
- No flake input, no lock node.
ix.patchedSrcapplies the series to the unpackedpkgs.git.src(applyPatches unpacks tarballs already). - No assert. Drift is impossible by construction; version has one owner.
- No
make configurehack. The dist tarball shipsconfigureand theversionfile; the package overlay shrinks tosrc = patchedSrc. - The right failure mode.
checks.patched-src-gitbreaks exactly when a nixpkgs bump conflicts with the series, at eval-adjacent build speed. A clean apply means a nixpkgs bump needs zero action, which is most bumps.
rebase-patches gains the derived-base mode: for a trackNixpkgs entry
it resolves pkgs.<attr>.version, clones url at tagTemplate.format(version), and runs the same real-rebase regeneration
it does today. upstream-sync and the dag checks read the same JSON and
do not change.
The sharp edge
Dist tarballs are make dist products: the git tarball adds generated
files (configure, version) to the tag tree. The series must therefore
touch only files present in both trees. That is checkable mechanically at
export time: every path in every patch must exist in the git tag tree the
rebase ran against, and the canonical-form assertion already walks patch
contents. A project whose tarball diverges structurally from its tags
cannot use this mode and keeps a pinned input; the mode is opt-in per
entry.
Alternatives considered
- Status quo (pinned input + assert + comment): works, but the upkeep fires on every nixpkgs bump forever, and the comment is the enforcement.
- Derived fetch (
fetchFromGitHub { rev = "v${git.version}"; hash }): kills drift but keeps a hash to bump per nixpkgs move and adds a second download of a source nixpkgs already fetched. Half the upkeep survives.
Migration
Convert git first; it is the only nixpkgs-recipe fork today. btop and nushell keep their inputs: their recipes are version-agnostic overrides where a free-floating base is the point. The mode exists for the narrow case where the recipe and the source must agree on a version.
Drafted by Claude Code (Claude Opus 4.5); comments wanted, hence the RFC flag.
andrewgazelka