@biffo/cli 0.243.2 → 0.244.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -53,6 +53,47 @@ cd "$root" || exit 0
|
|
|
53
53
|
# cannot run, that is a reason to stop, not to wave the push through.
|
|
54
54
|
sh scripts/biffo.sh rewrite-scope-check || exit 1
|
|
55
55
|
|
|
56
|
+
# --- Claim guard (#1231 instance 2) -------------------------------------------
|
|
57
|
+
#
|
|
58
|
+
# `scripts/claim.sh` (packaged inside `@biffo/cli`, run through the bridge
|
|
59
|
+
# above) has asked the estate's four-signal question about "is this issue
|
|
60
|
+
# taken?" since #1209 -- but nothing ever RAN it. It shipped advisory-only, so
|
|
61
|
+
# the fix for the collisions it exists to catch was itself subject to the same
|
|
62
|
+
# failure: a collision happens precisely when someone does not think to check.
|
|
63
|
+
#
|
|
64
|
+
# `--guard <branch>` is the enforced form. It derives the issue number from the
|
|
65
|
+
# branch name (`<type>/<number>-slug>`) and, when the branch names one, checks
|
|
66
|
+
# for another remote branch or another OPEN PR naming the same issue -- NEVER
|
|
67
|
+
# the `in-progress` label (three of four real collisions were "work exists,
|
|
68
|
+
# label does not") and NEVER identity (a claim on #1109 was recorded as
|
|
69
|
+
# `github-actions[bot]` because of a repo-local `user.email` override). It
|
|
70
|
+
# excludes the branch being pushed and any PR whose head IS that branch, so
|
|
71
|
+
# pushing your own branch a second time cannot block you on your own work.
|
|
72
|
+
#
|
|
73
|
+
# A branch that names no issue returns before any network call -- most
|
|
74
|
+
# branches are fine, and noise kills a gate. Measured on a branch that DOES
|
|
75
|
+
# name one: the added latency is the cost of two `gh` lookups, similar in
|
|
76
|
+
# order of magnitude to `rewrite-scope-check` above.
|
|
77
|
+
branch=$(git symbolic-ref --quiet --short HEAD) || branch=""
|
|
78
|
+
|
|
79
|
+
if [ -n "$branch" ]; then
|
|
80
|
+
sh scripts/biffo.sh claim --guard "$branch"
|
|
81
|
+
claim_status=$?
|
|
82
|
+
|
|
83
|
+
if [ "$claim_status" -eq 1 ]; then
|
|
84
|
+
exit 1
|
|
85
|
+
fi
|
|
86
|
+
|
|
87
|
+
# $claim_status -eq 2 is "cannot tell" -- no network, gh unauthenticated, an
|
|
88
|
+
# API error -- and claim.sh has already printed a warning. Deliberately NOT
|
|
89
|
+
# blocked here: unlike every other gate in this file, this is a COORDINATION
|
|
90
|
+
# gate, not a correctness one. A false block costs the ability to push at
|
|
91
|
+
# all; a miss costs a recoverable collision caught early (every collision in
|
|
92
|
+
# #1209's own incident was caught before duplicate work merged). Set
|
|
93
|
+
# BIFFO_CLAIM_STRICT=1 to make claim.sh itself collapse cannot-tell into
|
|
94
|
+
# exit 1 instead, if you would rather block than guess.
|
|
95
|
+
fi
|
|
96
|
+
|
|
56
97
|
# Through the version-pinned CLI since #1109, so a repo cannot run a gate two
|
|
57
98
|
# versions old -- eight of them did, and tabsii-crm checked ONE thing in eight
|
|
58
99
|
# on a 700-line change and printed `verify passed` (#855). There is one copy of
|
|
@@ -53,6 +53,47 @@ cd "$root" || exit 0
|
|
|
53
53
|
# cannot run, that is a reason to stop, not to wave the push through.
|
|
54
54
|
sh scripts/biffo.sh rewrite-scope-check || exit 1
|
|
55
55
|
|
|
56
|
+
# --- Claim guard (#1231 instance 2) -------------------------------------------
|
|
57
|
+
#
|
|
58
|
+
# `scripts/claim.sh` (packaged inside `@biffo/cli`, run through the bridge
|
|
59
|
+
# above) has asked the estate's four-signal question about "is this issue
|
|
60
|
+
# taken?" since #1209 -- but nothing ever RAN it. It shipped advisory-only, so
|
|
61
|
+
# the fix for the collisions it exists to catch was itself subject to the same
|
|
62
|
+
# failure: a collision happens precisely when someone does not think to check.
|
|
63
|
+
#
|
|
64
|
+
# `--guard <branch>` is the enforced form. It derives the issue number from the
|
|
65
|
+
# branch name (`<type>/<number>-slug>`) and, when the branch names one, checks
|
|
66
|
+
# for another remote branch or another OPEN PR naming the same issue -- NEVER
|
|
67
|
+
# the `in-progress` label (three of four real collisions were "work exists,
|
|
68
|
+
# label does not") and NEVER identity (a claim on #1109 was recorded as
|
|
69
|
+
# `github-actions[bot]` because of a repo-local `user.email` override). It
|
|
70
|
+
# excludes the branch being pushed and any PR whose head IS that branch, so
|
|
71
|
+
# pushing your own branch a second time cannot block you on your own work.
|
|
72
|
+
#
|
|
73
|
+
# A branch that names no issue returns before any network call -- most
|
|
74
|
+
# branches are fine, and noise kills a gate. Measured on a branch that DOES
|
|
75
|
+
# name one: the added latency is the cost of two `gh` lookups, similar in
|
|
76
|
+
# order of magnitude to `rewrite-scope-check` above.
|
|
77
|
+
branch=$(git symbolic-ref --quiet --short HEAD) || branch=""
|
|
78
|
+
|
|
79
|
+
if [ -n "$branch" ]; then
|
|
80
|
+
sh scripts/biffo.sh claim --guard "$branch"
|
|
81
|
+
claim_status=$?
|
|
82
|
+
|
|
83
|
+
if [ "$claim_status" -eq 1 ]; then
|
|
84
|
+
exit 1
|
|
85
|
+
fi
|
|
86
|
+
|
|
87
|
+
# $claim_status -eq 2 is "cannot tell" -- no network, gh unauthenticated, an
|
|
88
|
+
# API error -- and claim.sh has already printed a warning. Deliberately NOT
|
|
89
|
+
# blocked here: unlike every other gate in this file, this is a COORDINATION
|
|
90
|
+
# gate, not a correctness one. A false block costs the ability to push at
|
|
91
|
+
# all; a miss costs a recoverable collision caught early (every collision in
|
|
92
|
+
# #1209's own incident was caught before duplicate work merged). Set
|
|
93
|
+
# BIFFO_CLAIM_STRICT=1 to make claim.sh itself collapse cannot-tell into
|
|
94
|
+
# exit 1 instead, if you would rather block than guess.
|
|
95
|
+
fi
|
|
96
|
+
|
|
56
97
|
# Through the version-pinned CLI since #1109, so a repo cannot run a gate two
|
|
57
98
|
# versions old -- eight of them did, and tabsii-crm checked ONE thing in eight
|
|
58
99
|
# on a 700-line change and printed `verify passed` (#855). There is one copy of
|
package/package.json
CHANGED
package/scripts/claim.sh
CHANGED
|
@@ -42,6 +42,7 @@
|
|
|
42
42
|
# sh scripts/claim.sh 1234 # check, and claim if free
|
|
43
43
|
# sh scripts/claim.sh 1234 --check # report only, change nothing
|
|
44
44
|
# sh scripts/claim.sh 1234 -R owner/repo
|
|
45
|
+
# sh scripts/claim.sh --guard <branch> # pre-push gate — see below
|
|
45
46
|
#
|
|
46
47
|
# 0 free — and claimed, unless --check
|
|
47
48
|
# 1 taken, or already closed — the reason is printed
|
|
@@ -51,15 +52,50 @@
|
|
|
51
52
|
# A check that cannot see its input must not report "free".
|
|
52
53
|
#
|
|
53
54
|
# Requires `gh`, authenticated. Uses gh's embedded jq, so no jq binary needed.
|
|
55
|
+
#
|
|
56
|
+
# ## `--guard <branch>` — the pre-push gate (#1231 instance 2)
|
|
57
|
+
#
|
|
58
|
+
# The four-signal check above is advisory: nothing ever runs it for you, so a
|
|
59
|
+
# collision happens precisely when someone does not think to check. `--guard`
|
|
60
|
+
# is the enforced half, called from `.githooks/pre-push` on every push.
|
|
61
|
+
#
|
|
62
|
+
# It answers a narrower question than the plain form above, on purpose:
|
|
63
|
+
#
|
|
64
|
+
# - **Derives the issue from the branch name.** Pattern `<type>/<number>-…`,
|
|
65
|
+
# e.g. `feat/1234-thing`. A branch that does not name an issue (most of
|
|
66
|
+
# them) is skipped SILENTLY, before any network call — most branches are
|
|
67
|
+
# fine and noise kills a gate.
|
|
68
|
+
# - **Never checks the `in-progress` label.** Of four real collisions in one
|
|
69
|
+
# morning, three were "work exists, label does not" — so a gate keyed on
|
|
70
|
+
# the label would have caught one of four. This checks only what git and
|
|
71
|
+
# GitHub cannot help but know: an open PR, a remote branch.
|
|
72
|
+
# - **Never compares identity.** A claim on #1109 was recorded as
|
|
73
|
+
# `github-actions[bot]` because of a repo-local `user.email` override —
|
|
74
|
+
# usernames are not a trustworthy signal here.
|
|
75
|
+
# - **Excludes the branch being pushed, and any PR whose head IS that
|
|
76
|
+
# branch**, from counting as a conflict. Without this, pushing your own
|
|
77
|
+
# branch a second time blocks you on your own work.
|
|
78
|
+
# - **A real conflict — another branch or another open PR naming the same
|
|
79
|
+
# issue — exits 1**, naming what was found. AGENTS.md permits stealing a
|
|
80
|
+
# claim that is over an hour stale, with a comment; the message points
|
|
81
|
+
# there rather than being a dead end.
|
|
82
|
+
# - **"Cannot tell" (no network, `gh` unauthenticated, an API error) warns
|
|
83
|
+
# and exits 2 — never 1.** By default the caller should treat 2 as a pass:
|
|
84
|
+
# this is a COORDINATION gate, not a correctness one, and a false block
|
|
85
|
+
# costs the ability to push at all while a miss costs a recoverable
|
|
86
|
+
# collision caught early (see "What it cannot do" above). Set
|
|
87
|
+
# `BIFFO_CLAIM_STRICT=1` to make this script itself collapse cannot-tell
|
|
88
|
+
# into exit 1 instead, for anyone who would rather block than guess.
|
|
54
89
|
|
|
55
90
|
set -u
|
|
56
91
|
|
|
57
92
|
ISSUE=""
|
|
58
93
|
REPO=""
|
|
59
94
|
CHECK_ONLY=""
|
|
95
|
+
GUARD_BRANCH=""
|
|
60
96
|
|
|
61
97
|
usage() {
|
|
62
|
-
sed -n '2,
|
|
98
|
+
sed -n '2,84p' "$0" | sed 's/^# \{0,1\}//'
|
|
63
99
|
exit 2
|
|
64
100
|
}
|
|
65
101
|
|
|
@@ -70,6 +106,10 @@ while [ $# -gt 0 ]; do
|
|
|
70
106
|
shift 2
|
|
71
107
|
;;
|
|
72
108
|
--check) CHECK_ONLY=1; shift ;;
|
|
109
|
+
--guard)
|
|
110
|
+
GUARD_BRANCH="${2:-}"
|
|
111
|
+
shift 2
|
|
112
|
+
;;
|
|
73
113
|
-h | --help) usage ;;
|
|
74
114
|
*)
|
|
75
115
|
ISSUE="$1"
|
|
@@ -78,13 +118,6 @@ while [ $# -gt 0 ]; do
|
|
|
78
118
|
esac
|
|
79
119
|
done
|
|
80
120
|
|
|
81
|
-
case "$ISSUE" in
|
|
82
|
-
'' | *[!0-9]*)
|
|
83
|
-
echo "claim: give an issue number, e.g. sh scripts/claim.sh 1234" >&2
|
|
84
|
-
exit 2
|
|
85
|
-
;;
|
|
86
|
-
esac
|
|
87
|
-
|
|
88
121
|
RED=$(printf '\033[31m')
|
|
89
122
|
GREEN=$(printf '\033[32m')
|
|
90
123
|
YELLOW=$(printf '\033[33m')
|
|
@@ -94,12 +127,117 @@ OFF=$(printf '\033[0m')
|
|
|
94
127
|
gh_issue() { if [ -n "$REPO" ]; then gh issue "$@" --repo "$REPO"; else gh issue "$@"; fi; }
|
|
95
128
|
gh_pr() { if [ -n "$REPO" ]; then gh pr "$@" --repo "$REPO"; else gh pr "$@"; fi; }
|
|
96
129
|
|
|
130
|
+
# `git ls-remote --heads ""` fails outright ("fatal: bad repository ''") rather
|
|
131
|
+
# than falling back to the default remote — `${REPO:+url}` expands to an empty
|
|
132
|
+
# STRING ARGUMENT when $REPO is unset, not to no argument at all. That silently
|
|
133
|
+
# broke signal 3 below for every caller that does not pass -R, which is nearly
|
|
134
|
+
# all of them: the branch check never fired, and nothing noticed because the
|
|
135
|
+
# label and open-PR signals usually catch a collision first. Found while
|
|
136
|
+
# building `--guard`, which depends on this signal actually working.
|
|
137
|
+
remote_branches() {
|
|
138
|
+
if [ -n "$REPO" ]; then
|
|
139
|
+
git ls-remote --heads "https://github.com/$REPO.git"
|
|
140
|
+
else
|
|
141
|
+
git ls-remote --heads
|
|
142
|
+
fi
|
|
143
|
+
}
|
|
144
|
+
|
|
97
145
|
LABEL=in-progress
|
|
98
146
|
TAKEN=0
|
|
99
147
|
REASONS=""
|
|
100
148
|
|
|
101
149
|
note() { REASONS="${REASONS} $1\n"; TAKEN=1; }
|
|
102
150
|
|
|
151
|
+
# --- --guard <branch>: the enforced pre-push gate -----------------------------
|
|
152
|
+
#
|
|
153
|
+
# Deliberately short-circuits before any of the four-signal machinery below —
|
|
154
|
+
# it asks two of those four questions, not all four, and answers a different
|
|
155
|
+
# question ("would this push collide with someone else's live work?" rather
|
|
156
|
+
# than "is this issue free to claim?").
|
|
157
|
+
if [ -n "$GUARD_BRANCH" ]; then
|
|
158
|
+
guard_issue=$(printf '%s' "$GUARD_BRANCH" | sed -n 's#^[^/]*/\([0-9][0-9]*\)-.*#\1#p')
|
|
159
|
+
|
|
160
|
+
# No issue named by the branch — most branches, e.g.
|
|
161
|
+
# `security/brace-expansion-5-0-9`. Skip silently, and — this is the point —
|
|
162
|
+
# before touching the network at all.
|
|
163
|
+
if [ -z "$guard_issue" ]; then
|
|
164
|
+
exit 0
|
|
165
|
+
fi
|
|
166
|
+
|
|
167
|
+
cannot_tell=0
|
|
168
|
+
conflict=0
|
|
169
|
+
findings=""
|
|
170
|
+
cannot_tell_reasons=""
|
|
171
|
+
|
|
172
|
+
# --- an open PR referencing the issue, excluding our own branch's PR --------
|
|
173
|
+
pr_err=$(mktemp)
|
|
174
|
+
open_prs=$(gh_pr list --state open --limit 100 --json number,title,body,headRefName \
|
|
175
|
+
--jq "[.[] | select(((.title + \" \" + .body) | test(\"(^|[^0-9])#$guard_issue([^0-9]|\$)\")) or (.headRefName | test(\"(^|[^0-9])$guard_issue([^0-9]|\$)\")))] | .[] | select(.headRefName != \"$GUARD_BRANCH\") | \"#\(.number) \(.headRefName)\"" \
|
|
176
|
+
2>"$pr_err")
|
|
177
|
+
pr_status=$?
|
|
178
|
+
pr_err_text=$(cat "$pr_err")
|
|
179
|
+
rm -f "$pr_err"
|
|
180
|
+
|
|
181
|
+
if [ "$pr_status" -ne 0 ]; then
|
|
182
|
+
cannot_tell=1
|
|
183
|
+
cannot_tell_reasons="${cannot_tell_reasons}${pr_err_text} "
|
|
184
|
+
elif [ -n "$open_prs" ]; then
|
|
185
|
+
conflict=1
|
|
186
|
+
findings="${findings} ${RED}open PR${OFF} $(printf '%s' "$open_prs" | head -1)\n"
|
|
187
|
+
fi
|
|
188
|
+
|
|
189
|
+
# --- a remote branch naming the issue, excluding our own branch -------------
|
|
190
|
+
branch_err=$(mktemp)
|
|
191
|
+
raw_branches=$(remote_branches 2>"$branch_err")
|
|
192
|
+
branch_status=$?
|
|
193
|
+
branch_err_text=$(cat "$branch_err")
|
|
194
|
+
rm -f "$branch_err"
|
|
195
|
+
|
|
196
|
+
if [ "$branch_status" -ne 0 ]; then
|
|
197
|
+
cannot_tell=1
|
|
198
|
+
cannot_tell_reasons="${cannot_tell_reasons}${branch_err_text} "
|
|
199
|
+
else
|
|
200
|
+
other_branch=$(printf '%s\n' "$raw_branches" |
|
|
201
|
+
sed 's|.*refs/heads/||' |
|
|
202
|
+
grep -E "(^|[^0-9])$guard_issue([^0-9]|$)" |
|
|
203
|
+
grep -v -x "$GUARD_BRANCH" | head -1)
|
|
204
|
+
if [ -n "$other_branch" ]; then
|
|
205
|
+
conflict=1
|
|
206
|
+
findings="${findings} ${RED}branch${OFF} $other_branch\n"
|
|
207
|
+
fi
|
|
208
|
+
fi
|
|
209
|
+
|
|
210
|
+
if [ "$conflict" -eq 1 ]; then
|
|
211
|
+
printf '%b' "${RED}claim --guard: issue #$guard_issue looks claimed by someone else.${OFF}\n$findings"
|
|
212
|
+
echo
|
|
213
|
+
echo "${DIM}If you believe it is abandoned (no activity for over an hour), AGENTS.md${OFF}"
|
|
214
|
+
echo "${DIM}permits stealing it deliberately — say so in a comment on the issue first,${OFF}"
|
|
215
|
+
echo "${DIM}then push. Never steal a fresh claim.${OFF}"
|
|
216
|
+
exit 1
|
|
217
|
+
fi
|
|
218
|
+
|
|
219
|
+
if [ "$cannot_tell" -eq 1 ]; then
|
|
220
|
+
if [ -n "${BIFFO_CLAIM_STRICT:-}" ]; then
|
|
221
|
+
echo "${RED}claim --guard: cannot tell whether #$guard_issue is claimed elsewhere${OFF} — ${DIM}$cannot_tell_reasons${OFF}" >&2
|
|
222
|
+
echo "${DIM}BIFFO_CLAIM_STRICT=1 is set, so cannot-tell blocks the push.${OFF}" >&2
|
|
223
|
+
exit 1
|
|
224
|
+
fi
|
|
225
|
+
echo "${YELLOW}claim --guard: cannot tell whether #$guard_issue is claimed elsewhere${OFF} — ${DIM}$cannot_tell_reasons${OFF}" >&2
|
|
226
|
+
echo "${DIM}Warning and letting the push through: this is a coordination gate, not a${OFF}" >&2
|
|
227
|
+
echo "${DIM}correctness one. Set BIFFO_CLAIM_STRICT=1 to block instead of guessing.${OFF}" >&2
|
|
228
|
+
exit 2
|
|
229
|
+
fi
|
|
230
|
+
|
|
231
|
+
exit 0
|
|
232
|
+
fi
|
|
233
|
+
|
|
234
|
+
case "$ISSUE" in
|
|
235
|
+
'' | *[!0-9]*)
|
|
236
|
+
echo "claim: give an issue number, e.g. sh scripts/claim.sh 1234" >&2
|
|
237
|
+
exit 2
|
|
238
|
+
;;
|
|
239
|
+
esac
|
|
240
|
+
|
|
103
241
|
# --- 0. Does the issue exist, and is it still open? --------------------------
|
|
104
242
|
|
|
105
243
|
meta=$(gh_issue view "$ISSUE" --json state,title,labels \
|
|
@@ -150,7 +288,7 @@ fi
|
|
|
150
288
|
#
|
|
151
289
|
# Catches work that has been pushed but has no PR yet. Whole-number match again.
|
|
152
290
|
|
|
153
|
-
branches=$(
|
|
291
|
+
branches=$(remote_branches 2>/dev/null |
|
|
154
292
|
sed 's|.*refs/heads/||' |
|
|
155
293
|
grep -E "(^|[^0-9])$ISSUE([^0-9]|$)" 2>/dev/null)
|
|
156
294
|
|