@biffo/cli 0.236.0 → 0.238.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.
- package/_skeletons/plugin-template/.githooks/pre-push +8 -5
- package/_skeletons/plugin-template/AGENTS.md +22 -4
- package/_skeletons/plugin-template/scripts/verify.sh +6 -2
- package/_skeletons/sibling-template/.githooks/pre-push +8 -5
- package/_skeletons/sibling-template/AGENTS.md +20 -2
- package/_skeletons/sibling-template/scripts/verify.sh +6 -2
- package/dist/index.js +65 -16
- package/package.json +7 -2
- package/_skeletons/sibling-template/scripts/branch-health.sh +0 -306
- package/_skeletons/sibling-template/scripts/claim.sh +0 -208
- package/_skeletons/sibling-template/scripts/hook-audit.sh +0 -145
- package/_skeletons/sibling-template/scripts/pg-test-db.sh +0 -275
- package/_skeletons/sibling-template/scripts/rewrite-scope-check.sh +0 -189
- /package/{_skeletons/plugin-template/scripts → scripts}/branch-health.sh +0 -0
- /package/{_skeletons/plugin-template/scripts → scripts}/claim.sh +0 -0
- /package/{_skeletons/plugin-template/scripts → scripts}/hook-audit.sh +0 -0
- /package/{_skeletons/plugin-template/scripts → scripts}/pg-test-db.sh +0 -0
- /package/{_skeletons/plugin-template/scripts → scripts}/rewrite-scope-check.sh +0 -0
|
@@ -1,189 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env sh
|
|
2
|
-
# Refuse a force-push that has quietly started touching a file somebody else
|
|
3
|
-
# just changed.
|
|
4
|
-
#
|
|
5
|
-
# ## The incident (tabsii-platform#446, 2026-07-31)
|
|
6
|
-
#
|
|
7
|
-
# A branch had to be rebuilt to purge a Secret Scan finding from its history.
|
|
8
|
-
# The rebuild used:
|
|
9
|
-
#
|
|
10
|
-
# git reset --soft origin/dev && git add -A
|
|
11
|
-
#
|
|
12
|
-
# `origin/dev` had advanced by two merged PRs since the branch was cut. So
|
|
13
|
-
# `reset --soft` moved HEAD onto the NEWER dev while the working tree still
|
|
14
|
-
# held the OLDER copy of a doc those PRs had edited — and `git add -A`
|
|
15
|
-
# faithfully staged the difference. The resulting commit, whose subject was
|
|
16
|
-
# about RLS policies, silently REVERTED four lines of somebody else's merged
|
|
17
|
-
# work.
|
|
18
|
-
#
|
|
19
|
-
# Nothing local saw it: `verify.sh` passed, 2884 tests passed, the real-Postgres
|
|
20
|
-
# lane passed twice. It surfaced only because GitHub reported the PR as
|
|
21
|
-
# CONFLICTING and somebody went looking for why. **Had those two PRs touched any
|
|
22
|
-
# other file, there would have been no conflict and the revert would have
|
|
23
|
-
# merged.** A conflict is not a detector; it is the lucky case.
|
|
24
|
-
#
|
|
25
|
-
# ## What this checks, and why it is narrow
|
|
26
|
-
#
|
|
27
|
-
# It fires only on the intersection of two conditions, because either alone is
|
|
28
|
-
# far too noisy to be a gate:
|
|
29
|
-
#
|
|
30
|
-
# 1. The push is a REWRITE — the remote tip is not an ancestor of what you are
|
|
31
|
-
# pushing. An ordinary fast-forward push cannot have this problem.
|
|
32
|
-
# 2. A file the branch changes would be written back to content the
|
|
33
|
-
# integration branch ALREADY HAD earlier in its history. Not new work: an
|
|
34
|
-
# older copy.
|
|
35
|
-
#
|
|
36
|
-
# Condition 2 is the accidental-revert signature exactly, and it is the second
|
|
37
|
-
# attempt. The first asked "has the integration branch moved this file since my
|
|
38
|
-
# base?", which sounds equivalent and is not — `git reset --soft origin/dev`
|
|
39
|
-
# makes your base the integration tip, so the answer is always no. **That
|
|
40
|
-
# version scored a clean pass on a faithful reconstruction of the incident
|
|
41
|
-
# above.** `cli/src/lib/rewrite-scope-check.test.ts` reinstates it to prove the
|
|
42
|
-
# tests can tell the two apart.
|
|
43
|
-
#
|
|
44
|
-
# What stays silent, deliberately: amending to add a file nobody else has
|
|
45
|
-
# touched; rebasing onto a newer integration branch; two branches making
|
|
46
|
-
# genuinely different edits to one shared file. Only a STALE copy is a bug.
|
|
47
|
-
#
|
|
48
|
-
# ## What it deliberately does NOT do
|
|
49
|
-
#
|
|
50
|
-
# It does not try to decide whether a change is "intended". That needs intent,
|
|
51
|
-
# which no gate has. It answers a narrower question answerable from history
|
|
52
|
-
# alone: *is the content I am about to push actually older content?*
|
|
53
|
-
#
|
|
54
|
-
# It also never fails the push because it could not run. A guard that blocks
|
|
55
|
-
# work when its own inputs are missing gets disabled, and then it protects
|
|
56
|
-
# nothing. When it cannot answer it says so, loudly, and exits 0 — the same
|
|
57
|
-
# contract `verify.sh` uses for a check whose tool is absent. Absence is
|
|
58
|
-
# reported, never mistaken for a clean result.
|
|
59
|
-
|
|
60
|
-
set -u
|
|
61
|
-
|
|
62
|
-
ZERO=0000000000000000000000000000000000000000
|
|
63
|
-
ESCAPE_HATCH=BIFFO_ALLOW_SCOPE_CHANGE
|
|
64
|
-
|
|
65
|
-
note() { printf 'rewrite-scope: %s\n' "$1" >&2; }
|
|
66
|
-
|
|
67
|
-
# The integration branch every Biffo repo uses (AGENTS.md §2). Resolved rather
|
|
68
|
-
# than assumed: a repo whose remote HEAD says otherwise is a repo this check
|
|
69
|
-
# should not guess about.
|
|
70
|
-
integration=""
|
|
71
|
-
for candidate in dev main master; do
|
|
72
|
-
if git rev-parse --verify --quiet "refs/remotes/origin/$candidate" >/dev/null 2>&1; then
|
|
73
|
-
integration="origin/$candidate"
|
|
74
|
-
break
|
|
75
|
-
fi
|
|
76
|
-
done
|
|
77
|
-
|
|
78
|
-
if [ -z "$integration" ]; then
|
|
79
|
-
note "no origin/dev, origin/main or origin/master — check did NOT run"
|
|
80
|
-
exit 0
|
|
81
|
-
fi
|
|
82
|
-
|
|
83
|
-
# The inner filter below runs in a subshell (it is on the right of a pipe), so
|
|
84
|
-
# it cannot assign to a variable in this shell. Collect through a file instead.
|
|
85
|
-
FINDINGS_FILE=$(mktemp) || {
|
|
86
|
-
note "could not create a temp file — check did NOT run"
|
|
87
|
-
exit 0
|
|
88
|
-
}
|
|
89
|
-
trap 'rm -f "$FINDINGS_FILE"' EXIT HUP INT TERM
|
|
90
|
-
|
|
91
|
-
# git feeds pre-push one line per ref: <local ref> <local sha> <remote ref> <remote sha>
|
|
92
|
-
while read -r _local_ref local_sha _remote_ref remote_sha; do
|
|
93
|
-
# Branch deletion, or nothing to push.
|
|
94
|
-
[ "$local_sha" = "$ZERO" ] && continue
|
|
95
|
-
# Brand-new remote branch: there is no previous scope to compare against.
|
|
96
|
-
[ "$remote_sha" = "$ZERO" ] && continue
|
|
97
|
-
# The remote may not have the old object locally (shallow clone, pruned).
|
|
98
|
-
git cat-file -e "$remote_sha^{commit}" 2>/dev/null || {
|
|
99
|
-
note "remote tip $remote_sha not available locally — check did NOT run for this ref"
|
|
100
|
-
continue
|
|
101
|
-
}
|
|
102
|
-
# Fast-forward: not a rewrite, cannot exhibit this defect.
|
|
103
|
-
if git merge-base --is-ancestor "$remote_sha" "$local_sha" 2>/dev/null; then
|
|
104
|
-
continue
|
|
105
|
-
fi
|
|
106
|
-
|
|
107
|
-
new_base=$(git merge-base "$local_sha" "$integration" 2>/dev/null) || continue
|
|
108
|
-
old_base=$(git merge-base "$remote_sha" "$integration" 2>/dev/null) || continue
|
|
109
|
-
|
|
110
|
-
# Every file this branch claims to change, relative to where it forked.
|
|
111
|
-
#
|
|
112
|
-
# Iterated with `while read`, not `for file in $scope`: word splitting would
|
|
113
|
-
# break any path containing a space, and a guard that silently skips the file
|
|
114
|
-
# it should have flagged is worse than no guard. Process substitution
|
|
115
|
-
# (`grep -f <(...)`) is avoided for the same class of reason — it is a
|
|
116
|
-
# bashism, and this runs under `sh` from the pre-push hook.
|
|
117
|
-
git diff --name-only "$new_base" "$local_sha" 2>/dev/null | while IFS= read -r file; do
|
|
118
|
-
[ -z "$file" ] && continue
|
|
119
|
-
|
|
120
|
-
mine=$(git rev-parse --quiet --verify "$local_sha:$file" 2>/dev/null) || continue
|
|
121
|
-
theirs=$(git rev-parse --quiet --verify "$integration:$file" 2>/dev/null) || continue
|
|
122
|
-
# Agreeing with the integration branch is the normal, safe case.
|
|
123
|
-
[ "$mine" = "$theirs" ] && continue
|
|
124
|
-
|
|
125
|
-
# The signature of an accidental revert: the content you are about to push
|
|
126
|
-
# is not new work, it is a version this file ALREADY HAD earlier in the
|
|
127
|
-
# integration branch's history.
|
|
128
|
-
#
|
|
129
|
-
# This is what makes the check work at all. The first version asked "has
|
|
130
|
-
# the integration branch moved this file since my base?", which sounds
|
|
131
|
-
# equivalent and is not: `git reset --soft origin/dev` makes your base the
|
|
132
|
-
# integration tip, so the answer is always no and the guard never fires.
|
|
133
|
-
# Verified against a reconstruction of tabsii-platform#446 — the earlier
|
|
134
|
-
# condition scored a clean pass on the exact commit that reverted two
|
|
135
|
-
# merged PRs.
|
|
136
|
-
#
|
|
137
|
-
# Bounded to recent history: an unbounded walk on a long-lived file costs
|
|
138
|
-
# more than a pre-push gate should, and a stale copy older than this is not
|
|
139
|
-
# the accident this catches.
|
|
140
|
-
git log --format=%H -n 200 "$integration" -- "$file" 2>/dev/null | while IFS= read -r commit; do
|
|
141
|
-
[ -z "$commit" ] && continue
|
|
142
|
-
past=$(git rev-parse --quiet --verify "$commit:$file" 2>/dev/null) || continue
|
|
143
|
-
if [ "$past" = "$mine" ]; then
|
|
144
|
-
printf '%s\n' "$file"
|
|
145
|
-
break
|
|
146
|
-
fi
|
|
147
|
-
done
|
|
148
|
-
done >>"$FINDINGS_FILE"
|
|
149
|
-
done
|
|
150
|
-
|
|
151
|
-
findings=$(cat "$FINDINGS_FILE" 2>/dev/null)
|
|
152
|
-
|
|
153
|
-
[ -z "$findings" ] && exit 0
|
|
154
|
-
|
|
155
|
-
if [ -n "${BIFFO_ALLOW_SCOPE_CHANGE:-}" ]; then
|
|
156
|
-
note "scope change allowed via $ESCAPE_HATCH:"
|
|
157
|
-
printf '%s' "$findings" | sed 's/^/ /' >&2
|
|
158
|
-
exit 0
|
|
159
|
-
fi
|
|
160
|
-
|
|
161
|
-
cat >&2 <<EOF
|
|
162
|
-
|
|
163
|
-
This force-push would write a version of these file(s) that $integration
|
|
164
|
-
ALREADY HAD earlier in its history — not new work, an older copy:
|
|
165
|
-
|
|
166
|
-
$(printf '%s' "$findings" | sed 's/^/ /')
|
|
167
|
-
|
|
168
|
-
That is the signature of an accidental revert. A rewrite (rebase, amend,
|
|
169
|
-
reset --soft) picked up a stale copy of somebody else's merged work and
|
|
170
|
-
recorded it as your change. It merges silently unless it happens to
|
|
171
|
-
conflict, and no test can see it — the content is valid, just old.
|
|
172
|
-
|
|
173
|
-
Check what you are actually about to write:
|
|
174
|
-
|
|
175
|
-
git diff $integration...HEAD -- <file>
|
|
176
|
-
|
|
177
|
-
If it is intended, push again with:
|
|
178
|
-
|
|
179
|
-
$ESCAPE_HATCH=1 git push ...
|
|
180
|
-
|
|
181
|
-
If it is not: restore the file and amend —
|
|
182
|
-
|
|
183
|
-
git checkout $integration -- <file> && git commit --amend --no-edit
|
|
184
|
-
|
|
185
|
-
Rebuilding a branch? Reset to a FIXED SHA (the branch point), never to a
|
|
186
|
-
moving ref like $integration.
|
|
187
|
-
|
|
188
|
-
EOF
|
|
189
|
-
exit 1
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|