@biffo/cli 0.298.18 → 0.298.20
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.
- package/package.json +1 -1
- package/scripts/claim.sh +124 -14
package/package.json
CHANGED
package/scripts/claim.sh
CHANGED
|
@@ -101,9 +101,12 @@
|
|
|
101
101
|
# morning, three were "work exists, label does not" — so a gate keyed on
|
|
102
102
|
# the label would have caught one of four. This checks only what git and
|
|
103
103
|
# GitHub cannot help but know: an open PR, a remote branch.
|
|
104
|
-
# - **Never compares identity.** A claim on #1109 was
|
|
105
|
-
# `github-actions[bot]` because of a repo-local `user.email`
|
|
106
|
-
# usernames are not a trustworthy signal here.
|
|
104
|
+
# - **Never compares identity by GitHub username.** A claim on #1109 was
|
|
105
|
+
# recorded as `github-actions[bot]` because of a repo-local `user.email`
|
|
106
|
+
# override — usernames are not a trustworthy signal here. What #1698 adds
|
|
107
|
+
# below is not a username comparison: it is the claim record's own
|
|
108
|
+
# `claim-branch` field, written by `--as` at claim time, checked against
|
|
109
|
+
# the branch actually being pushed.
|
|
107
110
|
# - **Excludes the branch being pushed, and any PR whose head IS that
|
|
108
111
|
# branch**, from counting as a conflict. Without this, pushing your own
|
|
109
112
|
# branch a second time blocks you on your own work.
|
|
@@ -122,6 +125,18 @@
|
|
|
122
125
|
# waved through the most ordinary rival there is. Note the discount applies
|
|
123
126
|
# to the BRANCH signal only: an OPEN PR is a live claim regardless of
|
|
124
127
|
# lineage and is never discounted.
|
|
128
|
+
# - **The discount above is ALSO gated on the claim record (#1698).** Patch
|
|
129
|
+
# equivalence alone cannot tell "my own rebase of my own predecessor" from
|
|
130
|
+
# "an independent rival's rebase of the same starting point" — two sessions
|
|
131
|
+
# rebasing the same predecessor onto the same tip produce identical
|
|
132
|
+
# patches, with zero merge commits, and no open PR on either side for the
|
|
133
|
+
# first several minutes. `branch_is_absorbed` therefore only DISCOUNTS a
|
|
134
|
+
# candidate when the issue's claim record — `claim_guard_branch`, read from
|
|
135
|
+
# the same durable comment `--as` already writes, never a new store —
|
|
136
|
+
# names the branch being pushed as the CURRENT claim. Missing, unreadable,
|
|
137
|
+
# written before this field existed, or naming a different branch: all
|
|
138
|
+
# fail CLOSED into a conflict, same as an ordinary rival, with a message
|
|
139
|
+
# explaining why rather than a silent pass.
|
|
125
140
|
# - **A real conflict — another branch or another open PR naming the same
|
|
126
141
|
# issue — exits 1**, naming what was found. AGENTS.md permits stealing a
|
|
127
142
|
# claim that is over an hour stale, with a comment; the message points
|
|
@@ -141,6 +156,7 @@ REPO=""
|
|
|
141
156
|
HOLDER=""
|
|
142
157
|
RELEASE=""
|
|
143
158
|
HOLDER_MARK="claim-holder:"
|
|
159
|
+
BRANCH_MARK="claim-branch:"
|
|
144
160
|
CHECK_ONLY=""
|
|
145
161
|
GUARD_BRANCH=""
|
|
146
162
|
|
|
@@ -280,6 +296,36 @@ claim_held_by() {
|
|
|
280
296
|
esac
|
|
281
297
|
}
|
|
282
298
|
|
|
299
|
+
# claim_guard_branch <issue> -- tri-state read of the RECORDED branch on
|
|
300
|
+
# $issue's most recent claim comment (the `claim-branch:` field the write
|
|
301
|
+
# side below adds, #1698). Prints the branch name and returns 0 when a claim
|
|
302
|
+
# comment carries one; returns 1 when the comment list is readable but gives
|
|
303
|
+
# no confirmed branch (no claim comment at all, or one written before this
|
|
304
|
+
# field existed); returns 2 when the comment list itself could not be read.
|
|
305
|
+
#
|
|
306
|
+
# 1 and 2 are BOTH "not confirmed", never "confirmed free" -- the same
|
|
307
|
+
# fail-closed shape `claim_held_by` already uses for the holder token, for the
|
|
308
|
+
# same reason: a caller that collapsed them would treat an unclaimed issue or
|
|
309
|
+
# a flaky API call as proof nobody else is here, which is exactly the
|
|
310
|
+
# fail-open `--guard`'s own lineage discount shipped with (#1696) and this
|
|
311
|
+
# function exists to close.
|
|
312
|
+
claim_guard_branch() {
|
|
313
|
+
_c=$(gh_issue view "$1" --json comments \
|
|
314
|
+
--jq "[.comments[]? | select(.body | contains(\"$HOLDER_MARK\"))] | last | .body // \"\"" \
|
|
315
|
+
2>/dev/null) || return 2
|
|
316
|
+
[ -n "$_c" ] || return 1
|
|
317
|
+
case "$_c" in
|
|
318
|
+
*"$BRANCH_MARK"*)
|
|
319
|
+
_gb_rest=${_c#*"$BRANCH_MARK"}
|
|
320
|
+
_gb_branch=${_gb_rest%% *}
|
|
321
|
+
[ -n "$_gb_branch" ] || return 1
|
|
322
|
+
printf '%s\n' "$_gb_branch"
|
|
323
|
+
return 0
|
|
324
|
+
;;
|
|
325
|
+
*) return 1 ;;
|
|
326
|
+
esac
|
|
327
|
+
}
|
|
328
|
+
|
|
283
329
|
# This repo as `owner/name`, so a closing reference can be checked against it.
|
|
284
330
|
# GitHub records a CROSS-REPO closing reference in the same list as a local one
|
|
285
331
|
# -- tabsii-lms#43 closes tabsii-platform#553 -- so the number alone is not an
|
|
@@ -743,6 +789,39 @@ if [ -n "$GUARD_BRANCH" ]; then
|
|
|
743
789
|
findings="${findings} ${RED}open PR${OFF} $(printf '%s' "$open_prs" | head -1)\n"
|
|
744
790
|
fi
|
|
745
791
|
|
|
792
|
+
# --- the claim record's own idea of who currently holds $guard_issue --------
|
|
793
|
+
#
|
|
794
|
+
# #1698: `branch_is_absorbed` below answers "does the candidate carry work I
|
|
795
|
+
# lack?" -- a content test, not an identity one. Two independent sessions
|
|
796
|
+
# rebasing the same predecessor onto the same tip produce patch-id-equivalent
|
|
797
|
+
# commits, so content alone cannot tell "my own rebase of my own predecessor"
|
|
798
|
+
# from a genuine rival's rebase of the same starting point. Read once, used
|
|
799
|
+
# below to gate every absorption on this push -- not by comparing $2's shape
|
|
800
|
+
# the way `claim_held_by` does (`--guard` runs with no token, by design, see
|
|
801
|
+
# the header), but by checking whether the record's own `claim-branch` names
|
|
802
|
+
# THIS branch.
|
|
803
|
+
_guard_confirmed_branch=$(claim_guard_branch "$guard_issue")
|
|
804
|
+
_guard_confirmed_rc=$?
|
|
805
|
+
case "$_guard_confirmed_rc" in
|
|
806
|
+
0)
|
|
807
|
+
if [ "$_guard_confirmed_branch" = "$GUARD_BRANCH" ]; then
|
|
808
|
+
_guard_identity_ok=1
|
|
809
|
+
_guard_identity_reason=""
|
|
810
|
+
else
|
|
811
|
+
_guard_identity_ok=0
|
|
812
|
+
_guard_identity_reason="the claim record for #$guard_issue names \`$_guard_confirmed_branch\`, not this branch"
|
|
813
|
+
fi
|
|
814
|
+
;;
|
|
815
|
+
1)
|
|
816
|
+
_guard_identity_ok=0
|
|
817
|
+
_guard_identity_reason="#$guard_issue carries no claim record naming a branch (unclaimed, or claimed before #1698's fix)"
|
|
818
|
+
;;
|
|
819
|
+
*)
|
|
820
|
+
_guard_identity_ok=0
|
|
821
|
+
_guard_identity_reason="the claim record for #$guard_issue could not be read"
|
|
822
|
+
;;
|
|
823
|
+
esac
|
|
824
|
+
|
|
746
825
|
# --- a remote branch naming the issue, excluding our own branch -------------
|
|
747
826
|
branch_err=$(mktemp)
|
|
748
827
|
raw_branches=$(remote_branches 2>"$branch_err")
|
|
@@ -759,11 +838,15 @@ if [ -n "$GUARD_BRANCH" ]; then
|
|
|
759
838
|
# substring search for $guard_issue inside the candidate's raw text. See
|
|
760
839
|
# `derive_branch_issue` above for why that asymmetry was the defect.
|
|
761
840
|
# Each candidate that names the same issue is then classified by LINEAGE
|
|
762
|
-
# (see `branch_is_absorbed`)
|
|
763
|
-
#
|
|
841
|
+
# (see `branch_is_absorbed`) AND, when lineage says absorbed, by IDENTITY
|
|
842
|
+
# (`$_guard_identity_ok`, above): a genuine rival is always `rival`; a
|
|
843
|
+
# content-superseded predecessor is only actually `absorbed` when the
|
|
844
|
+
# claim record also confirms this push as the current claim -- otherwise
|
|
845
|
+
# it is `denied` and treated as a conflict too (#1698 fails this closed,
|
|
846
|
+
# rather than the silent pass it replaces), with a message explaining why.
|
|
764
847
|
# The candidate's SHA -- `git ls-remote`'s first, tab-separated field -- is
|
|
765
|
-
# what makes
|
|
766
|
-
#
|
|
848
|
+
# what makes lineage answerable, so it is no longer thrown away by a `sed`
|
|
849
|
+
# that kept only the name.
|
|
767
850
|
_classified=$(printf '%s\n' "$raw_branches" |
|
|
768
851
|
while IFS= read -r _line; do
|
|
769
852
|
case "$_line" in
|
|
@@ -778,27 +861,39 @@ if [ -n "$GUARD_BRANCH" ]; then
|
|
|
778
861
|
[ "$_cand_issue" = "$guard_issue" ] || continue
|
|
779
862
|
_cand_sha=$(printf '%s\n' "$_line" | cut -f1)
|
|
780
863
|
if branch_is_absorbed "$_cand_sha" "$guard_tip"; then
|
|
781
|
-
|
|
864
|
+
if [ "$_guard_identity_ok" -eq 1 ]; then
|
|
865
|
+
printf 'absorbed %s\n' "$_cand"
|
|
866
|
+
else
|
|
867
|
+
printf 'denied %s\n' "$_cand"
|
|
868
|
+
fi
|
|
782
869
|
else
|
|
783
870
|
printf 'rival %s\n' "$_cand"
|
|
784
871
|
fi
|
|
785
872
|
done)
|
|
786
873
|
|
|
787
874
|
other_branch=$(printf '%s\n' "$_classified" | sed -n 's/^rival //p' | head -1)
|
|
875
|
+
denied_branch=$(printf '%s\n' "$_classified" | sed -n 's/^denied //p' | head -1)
|
|
788
876
|
absorbed_branches=$(printf '%s\n' "$_classified" | sed -n 's/^absorbed //p')
|
|
789
877
|
if [ -n "$other_branch" ]; then
|
|
790
878
|
conflict=1
|
|
791
879
|
findings="${findings} ${RED}branch${OFF} $other_branch\n"
|
|
880
|
+
elif [ -n "$denied_branch" ]; then
|
|
881
|
+
conflict=1
|
|
882
|
+
findings="${findings} ${RED}branch${OFF} $denied_branch ${DIM}(content looks like a superseded predecessor, but $_guard_identity_reason)${OFF}\n"
|
|
792
883
|
fi
|
|
793
884
|
fi
|
|
794
885
|
|
|
795
886
|
# A discount a guard grants silently is a guard nobody can audit, so say so
|
|
796
|
-
# -- on stderr, only when it actually fired
|
|
797
|
-
#
|
|
887
|
+
# -- on stderr, only when it actually fired. One line per branch (#1698):
|
|
888
|
+
# $absorbed_branches can hold more than one name, and explaining several in
|
|
889
|
+
# the singular sent whoever read it looking for the wrong branch.
|
|
798
890
|
if [ -n "$absorbed_branches" ]; then
|
|
799
|
-
printf '%
|
|
800
|
-
|
|
801
|
-
|
|
891
|
+
printf '%s\n' "$absorbed_branches" | while IFS= read -r _ab; do
|
|
892
|
+
[ -n "$_ab" ] || continue
|
|
893
|
+
echo "${DIM}claim --guard: discounted $_ab -- every commit on it is already carried by${OFF}" >&2
|
|
894
|
+
echo "${DIM}$GUARD_BRANCH, and the claim record for #$guard_issue confirms $GUARD_BRANCH, so${OFF}" >&2
|
|
895
|
+
echo "${DIM}it is a superseded predecessor rather than a rival claim (#1112).${OFF}" >&2
|
|
896
|
+
done
|
|
802
897
|
fi
|
|
803
898
|
|
|
804
899
|
if [ "$conflict" -eq 1 ]; then
|
|
@@ -1156,13 +1251,28 @@ gh_label create "$LABEL" \
|
|
|
1156
1251
|
# which is why a claim could be written with nothing to identify it. That branch
|
|
1157
1252
|
# is now unreachable (the requirement above exits 2 first), so the conditional
|
|
1158
1253
|
# would only be a place for the old behaviour to come back.
|
|
1254
|
+
#
|
|
1255
|
+
# `claim-branch` (#1698) is the fact `--guard` reads back via
|
|
1256
|
+
# `claim_guard_branch` to confirm a discount is safe -- this comment is that
|
|
1257
|
+
# mechanism's one durable, non-worktree record, so nothing new is invented,
|
|
1258
|
+
# only this field added to it. AGENTS.md's own sequence puts worktree-and-
|
|
1259
|
+
# branch creation before this claim runs, so the branch normally already
|
|
1260
|
+
# exists; when it genuinely does not (detached HEAD, or `HEAD` itself, which
|
|
1261
|
+
# `--abbrev-ref` returns rather than a real name), the field is OMITTED
|
|
1262
|
+
# entirely rather than written empty -- an empty `claim-branch:` would match
|
|
1263
|
+
# any `case *"$BRANCH_MARK"*` probe as "present" while naming nothing, the
|
|
1264
|
+
# same empty-token trap `claim_held_by` already guards against for $2 (#826).
|
|
1265
|
+
_claim_branch=$(git rev-parse --abbrev-ref HEAD 2>/dev/null || echo '')
|
|
1266
|
+
case "$_claim_branch" in
|
|
1267
|
+
'' | HEAD) _claim_branch='' ;;
|
|
1268
|
+
esac
|
|
1159
1269
|
gh_issue edit "$ISSUE" --add-label "$LABEL" >/dev/null 2>&1 || {
|
|
1160
1270
|
echo "${RED}claim: could not apply the '$LABEL' label.${OFF}" >&2
|
|
1161
1271
|
echo "${DIM} Not claimed. Do not start work on the assumption that it worked.${OFF}" >&2
|
|
1162
1272
|
exit 2
|
|
1163
1273
|
}
|
|
1164
1274
|
gh_issue comment "$ISSUE" \
|
|
1165
|
-
--body "Claimed at $(date -u +%FT%TZ) by \`$(git config user.name 2>/dev/null || echo agent)\`. ${HOLDER_MARK}${HOLDER} Release it — remove the label — on merge, or if you stop." \
|
|
1275
|
+
--body "Claimed at $(date -u +%FT%TZ) by \`$(git config user.name 2>/dev/null || echo agent)\`. ${HOLDER_MARK}${HOLDER}${_claim_branch:+ ${BRANCH_MARK}${_claim_branch}} Release it — remove the label — on merge, or if you stop." \
|
|
1166
1276
|
>/dev/null 2>&1
|
|
1167
1277
|
|
|
1168
1278
|
echo "${GREEN}Claimed.${OFF}"
|