flake-update-cascade: process repositories in dependency order and propagate advanced pins #171
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#171
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 one
flake-update-cascaderun bring every repository in the workspace to a consistent set of locks, instead of one run per level of the dependency chain.Primary goals:
--prruns — a run that cannot go further because the next commit sits on an unmerged PR ends by naming those PRs and stating that the same command re-run after merge continues the cascade.Current state
The tool cannot converge in one run for two independent reasons, and
--prmode adds a third.Repositories are processed in the order
workspace_collect_reposreturns them (flake/flake-update-cascade:78,lib/workspace.sh:10), a directory walk. Under the<workspace>/allod/<repo>layout that isarchetypes, deploy, inventory, nexus, profiles, secrets, tools, vm, close to reverse dependency order:archetypesanddeployare processed, committed, and pushed beforenexus,secrets, andvm, which they pin. The public chain isvm → nexus → archetypes → deploy, so avmcommit made by the run is not seen byarchetypesuntil the next run, and thearchetypescommit that then results is not seen bydeployuntil the run after that.The set of inputs to update is fixed at argument parsing (
flake/flake-update-cascade:47) and only those names are resolved in each lock (get_update_paths,flake/flake-update-cascade:85). Nothing records that a repository processed earlier in the run received a new commit, so a later repository that pins it is not asked to move that pin.flake-status --upstreamcannot pre-empt this either: it compares against the remote head, which only moves once the run has pushed.In
--prmode a downstream pin is resolved on the default branch, so it cannot lock a commit that is still on a PR branch; every level of the chain needs its PR merged before the next level can move. That limit is inherent, and the tool does not report it.The commit message names every input on the command line (
INPUT_LABEL,flake/flake-update-cascade:149, used at:406and:491) rather than the inputs that moved in that repository, so a commit on one repository names inputs that exist only in another.Evidence:
allod/deployreceived four lock commits on 2026-09-08,bd12ed4,204b739,21ed97d,633be25, each naming the inputs the previous wave had moved. The original design note put transitive updates out of scope and no revision since has added ordering, so this is a gap in the design rather than a regression.Design
Graph. For each repository with a
flake.lock, every directly pinned root input (a string edge, not afollowsarray) whoseoriginal.urlnormalises to the same repository as another workspace repository'soriginremote is an edge from that repository to this one, labelled with the input name. Normalise to host, owner, and repository name; ignore scheme, user, port, and a.gitsuffix, so anssh://…:2222/pin matches anhttps://remote. Pins of repositories absent from the workspace are external and take no part in ordering.Order. Topological sort with ties broken by the current directory order. A cycle is a preflight error naming the cycle; nothing is touched. Preflight stays whole-workspace and atomic as today.
Update set per repository. The requested names resolved through the lock graph as today, plus every workspace-internal root pin whose locked
revdiffers from the target repository's remote-tracking headorigin/<branch>, where<branch>is the pin'soriginal.refor the default branch when none is given; a rev-only pin with no ref is left alone. Preflight runsgit fetch originin every repository that is a pin target so the tracking refs are current, and a push made by this run advances the tracking ref of the repository pushed. Because a repository is processed after its upstreams, that head already includes anything this run pushed to them. One rule therefore covers both commits the run itself makes and lock PRs merged since the last run, with no per-run bookkeeping of what advanced. A pin whose rev matches its target is not touched, so a repository whose only stale inputs are external behaves exactly as today. This is the one visible change on an existing workspace: a run naming only an external input also moves internal pins that are behind.Transitive nodes such as
archetypes/vmmove through the intermediate repository's own lock bump, as they do now; a transitive node a downstream lock owns ahead of the intermediate repository is the existing gotcha and stays out of scope.Commit message.
flake.lock: update <names>, where the names are the inputs that actually moved in that repository, in lock order.--prmode. The run creates or updates PRs in dependency order as today. A repository whose internal pin targets a repository that this run moved only on a PR branch is reported, not updated. The run ends with a summary listing those repositories, the PRs that block them, and the statement that the same command re-run after merge continues the cascade. Exit status stays 0 when every attempted update succeeded; the summary is information, not failure.--dry-run. Prints the plan in dependency order. Propagation from commits this run would push cannot be computed without pushing, so a dry run shows the first wave and says so; propagation from already-merged commits is shown in full.Validation
Extend
tests/flake/flake-update-cascade/testlib.shwith a fixture of three chained repositories, leaf, mid, and top, whose locks carryoriginal.urlentries matching the mockedremote get-url origin, with the mockgitserving a per-repository tracking head that advances onpush.--prmode, one run: leaf gets a PR; mid and top are reported as waiting on it with its number; nothing is committed there; exit 0.nix flake update.Scope
In scope: the ordering, the update-set rule, the commit-message change, the
--prand--dry-runreporting, the tests above, and theflake/README.mdand--helptext that describe the tool as updating named inputs.Sequencing: this follows allod/tools#159. The Go port lands bug-for-bug first, directory order included, and this change is the next Go PR after cutover. If allod/tools#159 has no implementation PR open when this issue is picked up, the same design applies to the Bash tool and the port then inherits it; that is the fallback, not the plan.
Tracked elsewhere:
flake-status --upstreamcomparing to the default branch rather than the locked ref, allod/tools#170; transitive nodes a downstream lock owns ahead of the intermediate repository, the existingnix.mdgotcha; pinning unmerged PR revisions byrevso a--prcascade could chain without merges, deliberately not proposed because it makes every downstream PR depend on upstream merge order.