mirror of
https://github.com/Comfy-Org/github-workflows.git
synced 2026-11-05 00:17:37 +00:00
[PR #152] [MERGED] ci(bump-callers): preflight pathspec staleness + per-file decommission (BE-6676) #163
Labels
No labels
cursor-review-catalog-drift
groom
groom
pull-request
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
Comfy-Org/github-workflows#163
Loading…
Add table
Add a link
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?
📋 Pull Request Information
Original PR: https://github.com/Comfy-Org/github-workflows/pull/152
Author: @mattmillerai
Created: 8/11/2026
Status: ✅ Merged
Merged: 8/12/2026
Merged by: @mattmillerai
Base:
main← Head:matt/be-6676-preflight-pathspecs📝 Commits (2)
689e141ci(bump-callers): preflight pathspec staleness + per-file decommission (BE-6676)97bdbf1ci(bump-callers): close the silent-green holes in the preflight list inputs (BE-6676)📊 Changes
4 files changed (+857 additions, -17 deletions)
View changed files
📝
.github/bump-callers/README.md(+90 -5)📝
.github/bump-callers/preflight.sh(+388 -11)📝
.github/bump-callers/tests/test_preflight.sh(+376 -0)📝
AGENTS.md(+3 -1)📄 Description
ELI-5
preflight.shis the guard that decides, before a bump fleet re-pins its consumers, "is this run the newest one, and does the thing I'm about to pin still exist?" It answers the first question by comparing objects: one blob for the reusable workflow, optionally one tree OID for its asset directory. That works for seven of the eight fleets. It cannot work forpr-risk, whose trigger says "watchscripts/pr-risk/**— except its tests and its README", because a tree OID has no way to say "except". A test-only commit moves that OID, the guard concludes "someone newer will handle this", and nobody does: the fleet's one real run is thrown away and every caller freezes on a stale pin. Its deletion check has the mirror-image hole — it asks whether the directory still exists, and the directory happily survives once onlytests/and the README are left in it, so callers would get pinned to a commit where the scripts they execute are gone.This adds the two optional inputs that let that fleet be expressed:
WATCHED_PATHSPECS(ask git, with the exclusions, instead of comparing OIDs) andWATCHED_EXEC(probe the files a caller actually runs, not the folder they live in). Both are unset everywhere else, so nothing else changes.What changed
.github/bump-callers/preflight.shWATCHED_PATHSPECS(optional, newline-separated git pathspecs,:(exclude)allowed). When set it replaces theWATCHED/WATCHED_ASSETSobject comparison as the staleness test withgit diff --quiet <head-oid> <fetched-tip-oid> -- <pathspecs>: exit 0 → unchanged → re-point to the verified tip; exit 1 → changed → stale no-op; exit >1 →::error::and exit 1, because a comparison that could not be performed is not a verdict in either direction (reading it as "changed" freezes the fleet, reading it as "unchanged" re-points callers to a tip nothing was verified at — the same not-evidence principle asresolve_oid). It compares two trees and walks no history, so it composes with the--unshallowdeepening the direction guard needs without depending on it.WATCHED_EXEC(optional, newline-separated repo-relative files a pinned caller executes). Each is probed for deletion at the tip through the existingresolve_oid(absent →::warning::decommissioned,proceed=false) and again locally with[[ -f ]]at the bottom of the script. The tip probe runs before the staleness test on purpose: a deletion also changes the watched surface, and reporting it as "a newer commit has its own run" would swallow the::warning::that is the fleet's only chance to say live callers are about to hard-fail at startup.WATCHED_EXECrejects globs, trailing slashes and pathspec magic (nothing expands those, so every probe would report a decommission forever);WATCHED_PATHSPECSaccepts only:(exclude)<path>magic and refuses an all-negative list, which git reads as every path except those — the widest possible watched surface.COUPLED TO THE PATH FILTERnote now bindsWATCHED_PATHSPECSexplicitly — it MUST mirror the fleet'spaths:filter, exclusions included; dropping one:(exclude)line reinstates the false-stale freeze exactly..github/bump-callers/tests/test_preflight.sh— 52 new assertions across 6 cases (107 → 159 total). Each new verdict is paired with a run of the input it replaces, so the tests show the difference, not just the new behaviour: the excluded-only commit that the tree-OID config false-stales; a mixed excluded+watched commit that is still correctly stale; a deleted grader that the-ddirectory probe would have bumped straight past.Docs —
.github/bump-callers/README.md(input table, shape rules, when a fleet needs these, a second consumption snippet) and theAGENTS.mdbump-callers bullet.Verification
The 107 pre-existing preflight assertions pass unchanged, which is the evidence for "the seven simple fleets do not change behaviour". Independently: no
bump-*-callers.ymlentrypoint consumespreflight.shyet (onlytest-bump-callers.ymlreferences it), so this merge cannot alter a live fleet either way.Two behavioural claims in the code and error messages were verified against real git rather than asserted: an all-negative pathspec really does mean "everything except" (a change to an unlisted file makes
git diff --quiet -- ':(exclude)x'exit 1), and a pathspec git refuses — an absolute or../path, the reachable case the shape checks deliberately cannot catch — really does exit 128, which is what makes the>1branch live rather than dead code. There is a test for the latter.Judgment calls
$head_sha/$fetched_tip, not the literalFETCH_HEAD HEADthe ticket wrote. Same trees, butFETCH_HEADis a mutable ref and the surrounding code already deliberately reads the verified OIDs instead (see the comment above the direction guard); using the ref here would reintroduce exactly what that comment rules out.readloop, notmapfile.mapfileis bash 4+ and this file is run by hand on macOS's bash 3.2 (whose empty-array/set -ubehaviour the existing header also calls out). Same semantics — split on newlines, trim, drop blanks.WATCHED_ASSETSmay still be set alongsideWATCHED_PATHSPECS: its tree-OID comparison is superseded, its decommission probes still run. Documented in the header and README rather than rejected, because the combination is legitimate (pathspec staleness + a directory deletion probe) — but it is the one place an input's primary role goes quiet, so flagging it.:(exclude)magic is accepted.:!is valid git shorthand and is nonetheless rejected, in favour of one spelled-out form the error message names. Conservative on purpose: a magic prefix this script has not reasoned about, or a typo in one, changes or empties what gets compared with no other signal.WATCHED_EXEChas no separate "absent at this run's own commit" message the wayWATCHED/WATCHED_ASSETSdo. The both-absent case is caught by the tip probe and the HEAD-only case by the local probe, so every path reaches the sameproceed=false+::warning::verdict; only the log wording differs.bump-pr-risk-callers.ymlontopreflight.shwith these inputs is the follow-on change, exactly as BE-6475 left it.No unmet acceptance criteria.
Review resolution (
97bdbf1)All ten panel findings addressed; every one was the same shape — a check that
verifies less than the entrypoint asked for, behind a green run.
WATCHED_PATHSPECS— a list that selects nothing is now an error ratherthan an "unchanged" verdict (
git diff --quietexits 0 for both, and the secondre-points every caller to a tip at which nothing was compared): each positive
must select a tracked path in either tree, checked with the exclusions applied
and asked of
git diffitself, so coverage is never judged by looser rules thanthe verdict. The list must also select
WATCHED(and, whenWATCHED_ASSETSis set, something under it) — the header stated that as a MUST but only
convention enforced it.
!path, thepaths:filter's own negation syntax, isrejected with the
:(exclude)spelling named (git reads the!literally:excludes nothing, matches nothing, and satisfied the all-negative guard).
Whole-line
#comments are dropped, so the commented filter these docs invitepasting works as pasted.
WATCHED_EXEC— a directory entry is rejected and the tip probe insists on ablob, closing the case where the tip side ("tree resolves → present") and the
local
[[ -f ]]("absent") disagreed and the verdict turned on whether mainmoved. Absolute and
../paths are rejected for every watched path, which wasthe same disagreement by another route. The local probe is skipped once the run
has been re-pointed —
new_shais then the tip, already cleared by the tipprobe, and this checkout is no longer the SHA callers get pinned to. A file
deleted by this run's own commit is now attributed to it instead of to the tip.
Plus the parsed-list array is no longer named
LINES(bash manages that name).Verification. 199 assertions, up from 159; the 107 pre-existing ones still
pass unchanged, and green under bash 3.2 as well as 5.x. The one vacuous
assertion the panel caught ("reusable change is stale too" — an earlier commit in
the same case sat between the baseline and the tip, so it reported a change
whether or not the reusable was covered) is rebuilt on a fresh baseline and
paired with the omitting list the coverage guard now refuses. Each new guard was
mutation-checked: reverting it fails the assertion written for it.
Two behaviours were measured rather than assumed:
!pathreally does match andexclude nothing, and
git ls-treecannot serve as the pathspec oracle (itrejects
:(exclude)outright and does not expand**), which is why the checkuses an empty-tree
git diff.🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.