Files
ersatztv/scripts/lib/branch-rule-classifier.jq
T
timothyandtimothy 761e575836
Build ErsatzTV Image / API docs in sync (OpenAPI + endpoint index) (push) Skipped
Build ErsatzTV Image / Formatting (changed .cs conform to .editorconfig) (push) Skipped
Build ErsatzTV Image / CI toolchain image resolves (push) Successful in 6s
Build ErsatzTV Image / Delimiter ban (release path) (push) Successful in 21s
Build ErsatzTV Image / Build & test (.NET) (push) Successful in 8m45s
Build ErsatzTV Image / EF migration integrity (SQLite + MySql) (push) Successful in 6m22s
Build ErsatzTV Image / Functional E2E (curl + UI contracts) (push) Successful in 5m56s
Build ErsatzTV Image / Build & push image (amd64) (push) Successful in 4m33s
fix(787): derive the dropped-step guard's scope, and reconcile its snapshot against the server (#861)
Co-authored-by: Timothy <timothy@noreply.gitea.tblindustries.be>
2026-08-27 22:37:02 +00:00

37 lines
2.4 KiB
Plaintext

# THE governing-branch-protection-rule classifier, as ONE program with two callers (ersatztv#787).
#
# Input : the `GET /repos/{owner}/{repo}/branch_protections` array. Arg `$b`: the branch to resolve.
# Output: {verdict:"exact", rule:<the rule>} | {verdict:"undecidable"} | {verdict:"none"}
#
# It lived inline in `.claude/hooks/pretooluse-merge-consent.sh` until #787 needed the same question
# answered for `main` from a second caller. It was EXTRACTED rather than copied: this is a security
# predicate, and that hook's own history is the argument — the docs-only file enumeration was written
# twice, four rounds of hardening landed on one copy and never reached the other, and the copy with
# real authority ended up strictly weaker than the copy without. Two copies of a security predicate
# drift; one cannot.
#
# DO NOT "simplify" the undecidable-first ordering. Gitea picks the governing rule with
# `GetFirstMatched` over a list sorted by Priority, then by plain-name-ness, so a GLOB rule with a
# better Priority outranks an exactly-named one. Preferring the exact rule would inspect a rule Gitea
# might not be applying. `superset` deliberately over-approximates a glob (literal prefix + `.*` +
# literal suffix) instead of reimplementing gobwas/glob: refusing whenever a glob COULD match is
# sound without knowing the dialect, which is the only claim either caller makes.
def esc: gsub("(?<c>[.+?^${}()|\\[\\]\\\\])"; "\\" + .c);
def offs: [match("[*?\\[\\]{}\\\\]"; "g").offset];
def superset: . as $n | (offs) as $o
| ($n[0:$o[0]] | esc) + ".*" + ($n[($o[-1]+1):] | esc);
def nonascii: explode | any(. > 127);
. as $rules | $b as $base |
($rules | map(select((.branch_name // .rule_name // "") as $n
| (($n|offs|length) == 0)
and (($n|ascii_downcase) == ($base|ascii_downcase))))) as $exacts |
(($base|nonascii) or ($rules | any((.branch_name // .rule_name // "") as $n
| ($n|offs|length) == 0 and ($n|nonascii)))) as $unfoldable |
if ($rules | any((.branch_name // .rule_name // "") as $n
| (($n|offs|length) > 0)
and ($base | test("^" + ($n|superset) + "$")))) then {verdict:"undecidable"}
elif $unfoldable then {verdict:"undecidable"}
elif ($exacts | length) > 1 then {verdict:"undecidable"}
elif ($exacts | length) == 1 then {verdict:"exact", rule:($exacts | first)}
else {verdict:"none"} end