flake-update-cascade: read branch heads over the git protocol instead of spending GitHub API calls on unchanged inputs #174
Labels
No labels
blocked
bug
decision
duplicate
enhancement
help wanted
invalid
landed?
question
ready-to-merge
stale
wontfix
bug
duplicate
enhancement
help wanted
invalid
question
wontfix
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set
Reference
allod/tools#174
Loading…
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Make a
flake-update-cascaderun that finds nothing to move cost no GitHub API calls, so a workspace-wide update fits the unauthenticated budget that every machine behind one public address shares.Primary goals:
git ls-remote, once per branch per run, and compare it to the locked revision.already up to datewith nonixinvocation, in every mode.nix flake lock --override-input <path> <prefix><rev>, which fetches that revision and never resolves the branch again.flake.nixreference that is not a GitHub or git branch, a remote it cannot reach) is still updated bynix flake update, so the fallback is today's behavior, not a skip.flake.lock, commit message, PR title and body, and every skip and failure path stay as they are; the existing suites hold unchanged.Current state
nix flake update <inputs>resolves every named GitHub input throughhttps://api.github.com/repos/<owner>/<repo>/commits/<ref>, once per input per repository, even when the locked revision already is the branch head. GitHub allows sixty unauthenticated requests an hour per public address, and every machine on a LAN behind one address draws on the same sixty. A cascade over a workspace of about twenty flake repositories withnixpkgs nixpkgs-unstable home-manager forge-nixpkgs nixpkgs-tailscalespends roughly forty-six calls when nothing has moved; a dry run costs the same, so a dry run followed by the real run exhausts the budget on its own. Once it is exhausted every remaining resolution fails and Nix falls back to its cache:The
already up to datethat follows is Nix reporting its cached answer, not a comparison against the branch, so the run's report is a guess for every repository after the budget runs out.The tool never asks whether anything moved:
cmd/flake-update-cascade/main.gohands each repository's update paths straight tonixFlakeUpdatefromdryRunRepoandupdateAndCheck, andinternal/flakelockreadsinputsandlocked.revbut notoriginal, so nothing in the program knows which repository and branch a pin came from.Measured:
git ls-remote https://github.com/<owner>/<repo> refs/heads/<ref>is a git-protocol ref advertisement, not a REST request, andcurl -s https://api.github.com/rate_limitshowsusedunchanged across it.nix flake lock --override-input <path> github:<owner>/<repo>/<rev>keeps the lock entry'soriginalintact and costs no API call either, even for a revision not yet in the store: Nix 2.34 fetches a named GitHub revision over the git protocol, andusedstays unchanged across a dry run that pins one.Design
Classify each update path by its lock node's
original. A node whoseoriginalnames arevis fixed and never moves. Agithubnode with onlyowner,repoand an optionalrefreads its head athttps://github.com/<owner>/<repo>and pins with the prefixgithub:<owner>/<repo>/; agitnode with onlyurland an optionalrefreads its head aturland pins withgit+<url>?rev=. Any other shape — anindirectregistry reference, atarball, apath, adir,hostorsubmodulesattribute the override would have to carry — is left tonix flake update. A missingrefis the default branch,HEAD; arefunderrefs/is used as given; any otherrefis tried as a branch and then as a tag.Heads are read with
git ls-remote --exit-code, stdin from/dev/nullandGIT_TERMINAL_PROMPT=0so a remote that wants credentials fails instead of prompting, bounded at sixty seconds, and cached per URL and ref for the run so a branch three repositories pin is read once. A failed read is reported on the repository's own lines —<path>: could not resolve <ref> at <url>; asking nix instead— and that path joins the ones Nix resolves.After the pull, before the temporary lock copy, the plan for a repository is: paths whose head equals the lock need nothing; paths whose head differs become
--override-input <path> <prefix><head>arguments to onenix flake lock; paths the tool cannot resolve go to onenix flake update, with--reference-lock-fileand--output-lock-filein dry-run mode so both commands write the same temporary lock. When both lists are empty the repository printsalready up to dateand nothing else runs. The evaluation gate, the revision report, and every commit and PR step are unchanged.Validation
A new suite,
tests/flake/flake-update-cascade/resolve-heads.sh, with a mockgit ls-remoteand a mocknix flake lockadded totestlib.sh: nothing moved makes onels-remotefor a branch two repositories pin and nonixcall; a moved GitHub input is pinned withnix flake lock --override-inputand nonix flake updateruns, in direct,--prand--dry-runmodes; a movedgitinput pins with thegit+<url>?rev=prefix; an unreachable remote prints the fallback line and runsnix flake update; arev-pinned input is neither read nor updated; a repository with one resolvable and one opaque input runs both commands against the same temporary lock in dry-run mode; a failednix flake lockrestores the lock and ends the repository with status 1 like a failed update does. Unit tests cover theoriginalclassification and thels-remoteoutput parsing. Every existing suite passes unchanged: their fixtures carry nooriginal, so they take thenix flake updatepath as before.Live: a dry run over a real workspace with the built program,
rate_limitread before and after, expectingusedunchanged whether or not anything moved; and a direct-mode run against a disposable fixture whose input is a localgit+file://repository one commit ahead of the lock, including a transitivemid/deppath, expecting the head to be pinned, evaluated and committed.Scope
In scope: the classification, the head cache, the two-command apply, the early
already up to date, the tests above, and aflake/README.mdparagraph on what a run costs.Tracked elsewhere: processing order and pin propagation, allod/tools#171;
flake-status --upstreamcomparing to the default branch, allod/tools#170. Authenticating Nix withaccess-tokenswould raise the budget to five thousand an hour and stays available as a fallback on any one machine, but it is not a change to this tool.Landed on master as
090ceab. Measured with the deployed binary on a development machine over fourteen workspace repositories and five GitHub inputs: the dry run finished in 42 s, seven repositories reported a moved nixos-unstable revision through nix flake lock, and rate_limit.used stayed at 0 before and after.