autonomous-sdlc-harness 0.6.2 → 0.6.4
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/dist/commands/init.js +3 -3
- package/dist/commands/init.js.map +1 -1
- package/dist/config/check.js +3 -2
- package/dist/config/check.js.map +1 -1
- package/dist/config/model.js +1 -1
- package/dist/config/model.js.map +1 -1
- package/dist/doctor/checks.js +30 -12
- package/dist/doctor/checks.js.map +1 -1
- package/dist/remote/githubActions.js +45 -3
- package/dist/remote/githubActions.js.map +1 -1
- package/package.json +1 -1
- package/templates/github/workflows/harness-control.yml +114 -16
- package/templates/github/workflows/harness-resume.yml +6 -5
- package/templates/github/workflows/harness-run.yml +39 -8
- package/templates/scripts/autonomous-watcher.sh +125 -22
- package/templates/scripts/lib/harness-run-lib.sh +140 -13
- package/templates/scripts/push-branch.sh +113 -14
- package/templates/scripts/remote-run.sh +1972 -301
- package/templates/state-dir/user_reviews/README.md +1 -1
|
@@ -28,7 +28,28 @@
|
|
|
28
28
|
# must NEVER abort the calling run — a stale remote is recoverable and a halted
|
|
29
29
|
# run is not. That is why this script uses `set -uo pipefail` deliberately
|
|
30
30
|
# WITHOUT `set -e`, and why even the fail-closed arm below exits 0: it refuses
|
|
31
|
-
# to push, which is the closed outcome here, and says so.
|
|
31
|
+
# to push, which is the closed outcome here, and says so. A push the remote
|
|
32
|
+
# refused for any reason but a `[rejected]` ref is attempted up to
|
|
33
|
+
# PUSH_ATTEMPTS times in all — waiting PUSH_RETRY_DELAY_SECS before the second
|
|
34
|
+
# attempt and three times that before the third — and is still never fatal once
|
|
35
|
+
# given up. PUSH_RETRY_DELAY_SECS is read from the environment as a test and
|
|
36
|
+
# tuning seam: default 5, and anything but a non-negative integer falls back to
|
|
37
|
+
# 5 with one line.
|
|
38
|
+
#
|
|
39
|
+
# A BRANCH ITS REMOTE DELETED STAYS DELETED. A branch this checkout tracks on
|
|
40
|
+
# its remote is never pushed when that remote no longer lists it. It tracks one
|
|
41
|
+
# when `branch.<branch>.merge` is set, or when `refs/remotes/origin/<branch>`
|
|
42
|
+
# resolves; a branch with neither is a first push and is not checked. The remote
|
|
43
|
+
# asked is `branch.<branch>.remote` (else `origin`), for `branch.<branch>.merge`
|
|
44
|
+
# (else `refs/heads/<branch>`), once with `ls-remote --exit-code` before any
|
|
45
|
+
# push attempt. Exit 2 (not listed) skips the push with one line naming the hand
|
|
46
|
+
# push that publishes it again; any other failure is not evidence of a deletion,
|
|
47
|
+
# so the push goes ahead and reports its own result. Reason: Gate 12 round 9,
|
|
48
|
+
# finding 1 — a run stopped because its branch was deleted had that branch
|
|
49
|
+
# re-created by the job's `always()` push step. Accepted costs: a deletion that
|
|
50
|
+
# lands between the `ls-remote` and the push is still pushed back; and a local
|
|
51
|
+
# branch reusing the name of one deleted on origin is not pushed while its stale
|
|
52
|
+
# `refs/remotes/origin/<branch>` survives — the skip line names the hand push.
|
|
32
53
|
#
|
|
33
54
|
# DEFENCE IN DEPTH, NOT THE SOLE GUARD. The caller-agnostic backstop is the
|
|
34
55
|
# committed pre-push hook `init` writes into the configured `githooksDir`, which
|
|
@@ -52,7 +73,12 @@
|
|
|
52
73
|
#
|
|
53
74
|
# WHAT IT NEVER DOES. It performs only a fast-forward push of already-committed
|
|
54
75
|
# work: no `--force`, no `--force-with-lease`, no history-rewriting flag, no
|
|
55
|
-
# commit of its own, and no non-zero exit
|
|
76
|
+
# commit of its own, and no non-zero exit, and never pushes back a branch it
|
|
77
|
+
# tracks after its remote deleted it. It never retries a push git reports
|
|
78
|
+
# as `! [rejected]` (non-fast-forward, fetch first), and never fetches or
|
|
79
|
+
# rebases: a push that lost a race fails loudly, which is the run-control rule
|
|
80
|
+
# of record (docs/github-run-control.md -> "A push that loses a race fails
|
|
81
|
+
# loudly, and is never fetched, rebased or retried").
|
|
56
82
|
#
|
|
57
83
|
# Usage: push-branch.sh [<repo-dir>]
|
|
58
84
|
# <repo-dir> worktree/repository to operate in (default: $PWD)
|
|
@@ -67,15 +93,44 @@
|
|
|
67
93
|
# no upstream yet push-branch.sh "$d" -> exit 0; `git -C "$b" branch` lists
|
|
68
94
|
# feat_x and the branch now has an upstream
|
|
69
95
|
# nothing new push-branch.sh "$d" -> exit 0 ("everything up to date")
|
|
96
|
+
# deleted on origin git -C "$b" update-ref -d refs/heads/feat_x; push-branch.sh "$d"
|
|
97
|
+
# -> exit 0, a "no longer has feat_x" line, and
|
|
98
|
+
# `git -C "$b" branch` lists no feat_x
|
|
70
99
|
# protected branch git -C "$d" checkout -q -b trunk; push-branch.sh "$d"
|
|
71
100
|
# -> exit 0, refusal message, "$b" unchanged
|
|
72
101
|
# unresolvable printf 'x' > "$d/harness.config.json"; push-branch.sh "$d"
|
|
73
102
|
# -> exit 0, refusal message, nothing pushed
|
|
74
103
|
# push fails git -C "$d" remote set-url origin /nonexistent.git
|
|
75
104
|
# -> exit 0 with a visible failure line
|
|
105
|
+
# refused once printf '%s\n' '#!/bin/sh' 'c="$GIT_DIR/refusals"' \
|
|
106
|
+
# '[ -e "$c" ] && exit 0' ': > "$c"; exit 1' > "$b/hooks/pre-receive"
|
|
107
|
+
# chmod +x "$b/hooks/pre-receive"
|
|
108
|
+
# git -C "$d" commit -qm x --allow-empty
|
|
109
|
+
# PUSH_RETRY_DELAY_SECS=0 push-branch.sh "$d"
|
|
110
|
+
# -> exit 0, one "retrying" line, and `git -C "$b" rev-parse
|
|
111
|
+
# feat_x` equals `git -C "$d" rev-parse HEAD`
|
|
112
|
+
# remote moved c=$(mktemp -d); git clone -q -b feat_x "$b" "$c"
|
|
113
|
+
# git -C "$c" commit -qm other --allow-empty; git -C "$c" push -q
|
|
114
|
+
# git -C "$d" commit -qm mine --allow-empty; push-branch.sh "$d"
|
|
115
|
+
# -> exit 0, a "(not retried)" line, one push attempt
|
|
76
116
|
|
|
77
117
|
set -uo pipefail
|
|
78
118
|
|
|
119
|
+
# Attempts in all, and the wait before the second; the third waits three times
|
|
120
|
+
# that (EVERY FAILURE PATH IS NON-FATAL above).
|
|
121
|
+
PUSH_ATTEMPTS=3
|
|
122
|
+
PUSH_RETRY_DELAY_SECS="${PUSH_RETRY_DELAY_SECS:-5}"
|
|
123
|
+
case "$PUSH_RETRY_DELAY_SECS" in
|
|
124
|
+
'' | *[!0-9]*)
|
|
125
|
+
echo "push-branch.sh: PUSH_RETRY_DELAY_SECS='$PUSH_RETRY_DELAY_SECS' is not a non-negative integer — using 5"
|
|
126
|
+
PUSH_RETRY_DELAY_SECS=5
|
|
127
|
+
;;
|
|
128
|
+
*)
|
|
129
|
+
# Base 10, so a leading zero is not read as octal.
|
|
130
|
+
PUSH_RETRY_DELAY_SECS=$((10#$PUSH_RETRY_DELAY_SECS))
|
|
131
|
+
;;
|
|
132
|
+
esac
|
|
133
|
+
|
|
79
134
|
# The library is reached by a path computed from this script's own location — no
|
|
80
135
|
# session root, and no runtime-substituted token, is assumed. Not finding it is
|
|
81
136
|
# non-fatal like everything else here.
|
|
@@ -121,20 +176,64 @@ case "$protected" in
|
|
|
121
176
|
;;
|
|
122
177
|
esac
|
|
123
178
|
|
|
124
|
-
#
|
|
125
|
-
#
|
|
126
|
-
|
|
127
|
-
if
|
|
128
|
-
git -C "$top"
|
|
129
|
-
|
|
130
|
-
|
|
179
|
+
# A BRANCH ITS REMOTE DELETED STAYS DELETED (header). Once, before the retry
|
|
180
|
+
# loop; a branch tracking nothing is a first push and skips the check.
|
|
181
|
+
track_merge="$(git -C "$top" config --get "branch.$branch.merge" 2>/dev/null)"
|
|
182
|
+
if [ -n "$track_merge" ] ||
|
|
183
|
+
git -C "$top" rev-parse --verify --quiet "refs/remotes/origin/$branch" >/dev/null 2>&1; then
|
|
184
|
+
track_remote="$(git -C "$top" config --get "branch.$branch.remote" 2>/dev/null)"
|
|
185
|
+
[ -n "$track_remote" ] || track_remote=origin
|
|
186
|
+
track_ref="${track_merge:-refs/heads/$branch}"
|
|
187
|
+
git -C "$top" ls-remote --exit-code --heads "$track_remote" "$track_ref" >/dev/null 2>&1
|
|
188
|
+
ls_status=$?
|
|
189
|
+
case "$ls_status" in
|
|
190
|
+
0) ;;
|
|
191
|
+
2)
|
|
192
|
+
echo "push-branch.sh: $track_remote no longer has $branch, which this checkout tracks; not pushing it back (a branch deleted on its remote stays deleted). To publish it again on purpose: git push --set-upstream $track_remote $branch"
|
|
193
|
+
exit 0
|
|
194
|
+
;;
|
|
195
|
+
*)
|
|
196
|
+
echo "push-branch.sh: could not ask $track_remote whether it still has $branch (ls-remote exited $ls_status); pushing as before"
|
|
197
|
+
;;
|
|
198
|
+
esac
|
|
131
199
|
fi
|
|
132
|
-
push_status=$?
|
|
133
200
|
|
|
134
|
-
|
|
135
|
-
|
|
201
|
+
# Push the branch; the deleted-on-its-remote check above has already run. Use
|
|
202
|
+
# the existing upstream when one is configured; otherwise set it on the fly, so
|
|
203
|
+
# a branch with no upstream is still pushed. Fast-forward only.
|
|
204
|
+
if git -C "$top" rev-parse --abbrev-ref --symbolic-full-name '@{u}' >/dev/null 2>&1; then
|
|
205
|
+
push_cmd=(git -C "$top" push)
|
|
136
206
|
else
|
|
137
|
-
|
|
138
|
-
echo "push-branch.sh: push failed for $branch (see output above)"
|
|
207
|
+
push_cmd=(git -C "$top" push --set-upstream origin "$branch")
|
|
139
208
|
fi
|
|
209
|
+
|
|
210
|
+
# Every failure line keeps the `push failed for <branch>` prefix: logs and
|
|
211
|
+
# Gate 12 records quote it.
|
|
212
|
+
attempt=1
|
|
213
|
+
delay="$PUSH_RETRY_DELAY_SECS"
|
|
214
|
+
while :; do
|
|
215
|
+
push_out="$("${push_cmd[@]}" 2>&1)"
|
|
216
|
+
push_status=$?
|
|
217
|
+
# git writes its ref-status lines to stderr; keep them there.
|
|
218
|
+
[ -n "$push_out" ] && printf '%s\n' "$push_out" >&2
|
|
219
|
+
|
|
220
|
+
if [ "$push_status" -eq 0 ]; then
|
|
221
|
+
echo "push-branch.sh: pushed $branch to origin"
|
|
222
|
+
break
|
|
223
|
+
fi
|
|
224
|
+
# A here-string, not a pipe: `grep -q` exiting early would fail a pipeline
|
|
225
|
+
# under pipefail.
|
|
226
|
+
if grep -q '^ ! \[rejected\]' <<<"$push_out"; then
|
|
227
|
+
echo "push-branch.sh: push failed for $branch: origin has commits this branch does not (not retried)"
|
|
228
|
+
break
|
|
229
|
+
fi
|
|
230
|
+
if [ "$attempt" -ge "$PUSH_ATTEMPTS" ]; then
|
|
231
|
+
echo "push-branch.sh: push failed for $branch after $PUSH_ATTEMPTS attempts (see output above)"
|
|
232
|
+
break
|
|
233
|
+
fi
|
|
234
|
+
echo "push-branch.sh: push failed for $branch (attempt $attempt of $PUSH_ATTEMPTS); retrying in ${delay}s"
|
|
235
|
+
sleep "$delay"
|
|
236
|
+
attempt=$((attempt + 1))
|
|
237
|
+
delay=$((delay * 3))
|
|
238
|
+
done
|
|
140
239
|
exit 0
|