autonomous-sdlc-harness 0.6.3 → 0.6.5

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.
@@ -18,7 +18,7 @@
18
18
  * `LEGACY_TRIGGER_LABEL`)
19
19
  * and the run-control names (`WORKFLOW_CONTROL_*`, `COMMAND_*`, `COMMENT_MARKER`,
20
20
  * `REVIEW_ROUND_STATE`, `STATE_LABEL_PREFIX`, `RUN_STATES`, `STATE_LABELS`, `PR_CREATE_SETTING`,
21
- * `PR_CREATE_SETTING_PATH`) are gated tighter
21
+ * `PR_CREATE_SETTING_PATH`, `MENTION_*`) are gated tighter
22
22
  * still: they are consulted only where `forgeTriggerApplies(config)` is true.
23
23
  *
24
24
  * **Shell and YAML mirrors that must agree byte for byte.** The compiler cannot reach them, so each
@@ -41,6 +41,7 @@
41
41
  * It reads no configuration and writes nothing.
42
42
  */
43
43
  import { spawnSync } from 'node:child_process';
44
+ import { PLUGIN_NAME } from '../core/pluginIdentity.js';
44
45
  import { unquoteYamlScalar } from '../core/yamlScalar.js';
45
46
  /** The adopter-side directory GitHub reads workflows from. */
46
47
  export const WORKFLOWS_DIR = '.github/workflows';
@@ -61,6 +62,38 @@ export const COMMAND_HANDLE = '@sdlc-harness';
61
62
  * replies with the run's state and changes no label and dispatches nothing.
62
63
  */
63
64
  export const COMMAND_VERBS = ['answer', 'pause', 'resume', 'stop', 'clear', 'status'];
65
+ /**
66
+ * The `action` values a mention's decision may carry: `command` names one of {@link COMMAND_VERBS};
67
+ * `reply` and `clarify` carry the agent's text; `fixes` is a request for fixes, answered with the
68
+ * script's own text; `none` means the mention was not addressed to the harness.
69
+ */
70
+ export const MENTION_ACTIONS = ['command', 'reply', 'clarify', 'fixes', 'none'];
71
+ /**
72
+ * How a mention's `command` decision treats each verb; total, so a new verb fails to compile until it
73
+ * is classified. `act` is carried out through that verb's own arm. `confirm` is answered with a
74
+ * request to comment the command itself: `stop` because it is destructive, `clear` because on GitHub
75
+ * typing it is the confirmation `branch-resume` asks for.
76
+ */
77
+ export const MENTION_VERB_HANDLING = {
78
+ answer: 'act',
79
+ pause: 'act',
80
+ resume: 'act',
81
+ stop: 'confirm',
82
+ clear: 'confirm',
83
+ status: 'act',
84
+ };
85
+ /** The verbs {@link MENTION_VERB_HANDLING} marks `act`, in {@link COMMAND_VERBS} order. */
86
+ export const MENTION_ACT_VERBS = COMMAND_VERBS.filter((verb) => MENTION_VERB_HANDLING[verb] === 'act');
87
+ /** The verbs {@link MENTION_VERB_HANDLING} marks `confirm`, in {@link COMMAND_VERBS} order. */
88
+ export const MENTION_CONFIRM_VERBS = COMMAND_VERBS.filter((verb) => MENTION_VERB_HANDLING[verb] === 'confirm');
89
+ /** The basename of the plugin slash command `plugin/commands/harness-read-mention.md`, the slash-command name verbatim. */
90
+ export const MENTION_COMMAND_NAME = 'harness-read-mention';
91
+ /**
92
+ * The plugin-qualified slash command `remote-run.sh control` passes as the session's `-p` prompt, the
93
+ * way `autonomous-watcher.sh` launches the plugin's commands as a session's first message. Built from
94
+ * {@link PLUGIN_NAME} rather than spelled; the CLI never prints it.
95
+ */
96
+ export const MENTION_COMMAND = `/${PLUGIN_NAME}:${MENTION_COMMAND_NAME}`;
64
97
  /**
65
98
  * The prefix of the hidden line every harness comment carries; the whole line is
66
99
  * `<!-- sdlc-harness event=<event> branch=<branch>[ question=<n>][ engine=<engine>] -->`, where
@@ -173,6 +206,14 @@ export function runActorAdmitted(actors, login) {
173
206
  export function carriesRunActors(workflowText) {
174
207
  return workflowText.includes(`vars.${RUN_ACTORS_VARIABLE}`);
175
208
  }
209
+ /**
210
+ * Whether a workflow's text reads `secrets.CLAUDE_CODE_OAUTH_TOKEN` or `secrets.ANTHROPIC_API_KEY`;
211
+ * `false` marks a `harness-control.yml` written before a mention could be read. Pure — the caller
212
+ * reads the file.
213
+ */
214
+ export function carriesMentionCredential(workflowText) {
215
+ return workflowText.includes(`secrets.${OAUTH_TOKEN_SECRET}`) || workflowText.includes(`secrets.${API_KEY_SECRET}`);
216
+ }
176
217
  /** The `repository_dispatch` `event_type` the trigger workflow listens to. */
177
218
  export const TRIGGER_DISPATCH_EVENT_TYPE = 'harness-task';
178
219
  /** Repository secret for subscription billing. */
@@ -1 +1 @@
1
- {"version":3,"file":"githubActions.js","sourceRoot":"","sources":["../../src/remote/githubActions.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"githubActions.js","sourceRoot":"","sources":["../../src/remote/githubActions.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "autonomous-sdlc-harness",
3
- "version": "0.6.3",
3
+ "version": "0.6.5",
4
4
  "description": "The outer loop of the autonomous SDLC harness: init, doctor, config and daemon management for the harness Claude Code plugin, plus opt-in local docs retrieval.",
5
5
  "license": "Apache-2.0",
6
6
  "author": "firu-daniel",
@@ -19,9 +19,16 @@
19
19
  # WHAT IT DOES. One job runs `remote-run.sh control`, which does everything
20
20
  # else: it authorises the actor — their write access, then the allow-list
21
21
  # HARNESS_RUN_ACTORS — finds the run's branch, carries out the
22
- # command or places the review round, and replies. Every decision lives in that
23
- # script; this file carries only the events, the permissions, a prefilter and
24
- # the concurrency group.
22
+ # command or places the review round, and replies. A comment that mentions
23
+ # `@sdlc-harness` anywhere, not in the exact command form, is read by one
24
+ # read-only agent session running the plugin's mention-reading slash command,
25
+ # which `remote-run.sh control` launches and whose decision it validates.
26
+ # A comment job first asks `remote-run.sh control --needs-agent` whether that
27
+ # session would start (THE AGENT CHECK below); the act step re-checks
28
+ # everything.
29
+ # Every decision lives in that script; this file carries only the events, the
30
+ # permissions, a prefilter, the concurrency group, and on a comment job the
31
+ # credential, the agent CLI and the pinned plugin that session needs.
25
32
  # The `run-name` below is not harness-run.yml's `harness <action> <branch>`
26
33
  # title, so no run lookup `remote-run.sh` makes by that title can match it.
27
34
  #
@@ -73,8 +80,18 @@
73
80
  # issues: write reply on the issue or pull request, and label it
74
81
  # pull-requests: write read the pull request, its reviews and comments,
75
82
  # and label it
76
- # The job reads no repository secret: it needs only its own token, so the
77
- # credential secrets never reach the job that reads untrusted comment text.
83
+ # Only an `issue_comment` event's act step receives a credential: its `env:`
84
+ # passes CLAUDE_CODE_OAUTH_TOKEN and ANTHROPIC_API_KEY as IN_OAUTH and IN_API,
85
+ # each through an expression that is the empty string unless the event is
86
+ # `issue_comment`, and nothing at job level reads a secret. A
87
+ # `pull_request_review`, `issues`, `pull_request` or `delete` event gets empty
88
+ # strings. A `pull_request_review` job runs the pull request's merge-commit
89
+ # copy of this file, which a same-repository head can edit to read the secret;
90
+ # that writer could already read it through any workflow of their own
91
+ # (docs/remote-execution.md, section 9, Credentials and billing), and a fork's
92
+ # review job gets no secret (C2). The decision of record, its exposure and why
93
+ # a comment's text cannot steer the session outside its closed set of actions:
94
+ # docs/github-run-control.md, section 6.
78
95
  # Every reply is posted as github-actions[bot] and starts no workflow (S3).
79
96
  #
80
97
  # FORK PULL REQUESTS. Never `pull_request_target`, which would run with this
@@ -113,6 +130,33 @@
113
130
  # harness-run.yml's `collect` job is the one such job today (DECLARED
114
131
  # MIRRORS below).
115
132
  #
133
+ # THE PLUGIN. This file carries no pin of its own. A comment job reads the
134
+ # first HARNESS_CLI_VERSION in the checkout's harness-run.yml, clones the
135
+ # release tag `autonomous-sdlc-harness--v<that version>` outside the checkout
136
+ # as harness-run.yml's `Install the pinned plugin` step does, checks the
137
+ # plugin manifest's version against it, and hands the plugin's directory to
138
+ # the act step as HARNESS_MENTION_PLUGIN_DIR; the session loads it with
139
+ # `--plugin-dir`, so nothing is installed. The plugin a mention runs under is
140
+ # therefore the release the run job installs. A pinned release older than
141
+ # mentions carries no mention-reading command and gets `control`'s no-plugin
142
+ # reply; the route is `init --upgrade-workflows` (docs/remote-execution.md,
143
+ # `### Upgrading`). The three comment-only steps run only when THE AGENT CHECK
144
+ # below did not answer `no`, so an exact-form command now skips them. They stay
145
+ # `continue-on-error`, so a failed install still never blocks one: `control`
146
+ # then finds no agent binary or plugin and answers a mention with its exit-3
147
+ # reply.
148
+ #
149
+ # THE AGENT CHECK. A comment job runs `remote-run.sh control --needs-agent`
150
+ # before the three comment-only steps. It tells the exact command form apart
151
+ # from a mention, then runs `control`'s gates 1 to 3 on a mention, and posts,
152
+ # dispatches and writes nothing. Only its exit 2 (no session would start: an
153
+ # ignored comment, an exact-form command or a refused commenter) writes
154
+ # `agent=no`, which skips those steps. Every other outcome installs, as
155
+ # before: an older script that does not know the flag, a failed permission
156
+ # call, a failed step. A mention by an admitted commenter costs one extra
157
+ # permission call. It is a saving, not the authority, as THE PREFILTER says of
158
+ # the `if:`: the act step re-checks every rule.
159
+ #
116
160
  # A REFUSAL IS A SUCCESS. `remote-run.sh control` exits 2 for a refusal it has
117
161
  # already replied to; that exit code is the script's contract with every other
118
162
  # caller, so the step, not the script, maps it: `|| [ $? -eq 2 ]` turns a
@@ -141,23 +185,40 @@
141
185
  # (`changes_requested`), HARNESS_RUNNER,
142
186
  # HARNESS_REMOTE_STOP,
143
187
  # HARNESS_TRIGGER_ALLOWED_BOTS,
144
- # HARNESS_RUN_ACTORS
188
+ # HARNESS_RUN_ACTORS,
189
+ # OAUTH_TOKEN_SECRET
190
+ # (`CLAUDE_CODE_OAUTH_TOKEN`),
191
+ # API_KEY_SECRET (`ANTHROPIC_API_KEY`),
192
+ # CLI_VERSION_VARIABLE
193
+ # (`HARNESS_CLI_VERSION`, read from
194
+ # harness-run.yml)
195
+ # cli/src/generators/projectSettings.ts MARKETPLACE_NAME
196
+ # (`autonomous-sdlc-harness`)
145
197
  # cli/src/config/model.ts DEFAULTS.scriptsDir
146
198
  # harness-run.yml the concurrency group
147
199
  # `harness-review-<branch>`, held by its
148
- # `collect` job
200
+ # `collect` job; the release-tag shape
201
+ # `autonomous-sdlc-harness--v<version>`
202
+ # of its `Install the pinned plugin` step
203
+ # remote-run.sh (the scriptsDir copy) IN_OAUTH, IN_API and
204
+ # HARNESS_MENTION_PLUGIN_DIR, the env
205
+ # interface `control` reads (its MENTION
206
+ # paragraph); `control --needs-agent`,
207
+ # whose exit 2 alone means no session
208
+ # would start
149
209
  #
150
210
  # ACTION PINS.
151
211
  # actions/checkout@v5
152
- # The lowest major whose own action.yml declares `runs.using: node24`, per a
153
- # maintainer's lookups of its action.yml and release notes on
154
- # 2026-09-29T08:21Z (UTC). That major's release notes require Actions Runner
155
- # 2.327.1 or newer; a GitHub-hosted runner already has it, and a self-hosted
156
- # one must run it (docs/remote-execution.md, section 8). A major tag, not a
157
- # commit sha: it is GitHub's own `actions/` organisation, the owner of the
158
- # runner and GITHUB_TOKEN this job already trusts, and a major tag takes the
159
- # action's own patch and security releases where a sha would freeze your copy.
160
- # Pin a sha in your copy if you want one.
212
+ # actions/setup-node@v5
213
+ # Each is the lowest major whose own action.yml declares `runs.using: node24`,
214
+ # per a maintainer's lookups of each action's action.yml and release notes on
215
+ # 2026-09-29T08:21Z (UTC). Each of those majors' release notes requires
216
+ # Actions Runner 2.327.1 or newer; a GitHub-hosted runner already has it, and
217
+ # a self-hosted one must run it (docs/remote-execution.md, section 8). A major
218
+ # tag, not a commit sha: every action here is GitHub's own `actions/`
219
+ # organisation, the owner of the runner and GITHUB_TOKEN this job already
220
+ # trusts, and a major tag takes the action's own patch and security releases
221
+ # where a sha would freeze your copy. Pin shas in your copy if you want them.
161
222
 
162
223
  name: harness-control
163
224
  run-name: harness control ${{ github.event.issue.number || github.event.pull_request.number || github.event.ref }}
@@ -226,5 +287,73 @@ jobs:
226
287
  git config user.name "github-actions[bot]"
227
288
  git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
228
289
 
290
+ - name: Decide whether the comment needs the agent
291
+ id: needs
292
+ if: github.event_name == 'issue_comment'
293
+ continue-on-error: true
294
+ run: |
295
+ status=0
296
+ bash "$SCRIPTS_DIR/remote-run.sh" control --needs-agent || status=$?
297
+ if [ "$status" -eq 2 ]; then
298
+ echo "agent=no" >> "$GITHUB_OUTPUT"
299
+ else
300
+ echo "agent=yes" >> "$GITHUB_OUTPUT"
301
+ fi
302
+
303
+ - name: Set up Node
304
+ if: github.event_name == 'issue_comment' && steps.needs.outputs.agent != 'no'
305
+ continue-on-error: true
306
+ uses: actions/setup-node@v5
307
+ with:
308
+ node-version: '22'
309
+ # As in harness-run.yml: no npm cache is used, and a lockfile-less repository must not fail this step.
310
+ package-manager-cache: false
311
+
312
+ - name: Install the claude CLI when absent
313
+ if: github.event_name == 'issue_comment' && steps.needs.outputs.agent != 'no'
314
+ continue-on-error: true
315
+ run: |
316
+ if ! command -v claude >/dev/null 2>&1; then
317
+ npm install -g @anthropic-ai/claude-code
318
+ fi
319
+ claude --version
320
+
321
+ - name: Fetch the pinned plugin
322
+ if: github.event_name == 'issue_comment' && steps.needs.outputs.agent != 'no'
323
+ continue-on-error: true
324
+ run: |
325
+ pin=$(awk '/^[[:space:]]*HARNESS_CLI_VERSION:/ { sub(/^[[:space:]]*HARNESS_CLI_VERSION:[[:space:]]*/, ""); sub(/[[:space:]]+#.*$/, ""); sub(/[[:space:]]+$/, ""); gsub(/["\047]/, ""); print; exit }' .github/workflows/harness-run.yml 2>/dev/null || true)
326
+ if [ -z "$pin" ]; then
327
+ echo "::warning::.github/workflows/harness-run.yml carries no HARNESS_CLI_VERSION, so no plugin was fetched and a mention gets the no-plugin reply."
328
+ exit 1
329
+ fi
330
+ source_repo=$(jq -r '.extraKnownMarketplaces["autonomous-sdlc-harness"].source.repo // empty' .claude/settings.json 2>/dev/null || true)
331
+ if [ -z "$source_repo" ]; then
332
+ echo "::warning::.claude/settings.json names no marketplace source for autonomous-sdlc-harness, so the plugin at $pin was not fetched."
333
+ exit 1
334
+ fi
335
+ tag="autonomous-sdlc-harness--v$pin"
336
+ marketplace_dir="$RUNNER_TEMP/harness-marketplace"
337
+ # --quiet does not cover the detached-HEAD advice a tag checkout prints; only advice.detachedHead gates it.
338
+ if ! git -c advice.detachedHead=false clone --quiet --depth 1 --branch "$tag" "https://github.com/$source_repo.git" "$marketplace_dir"; then
339
+ echo "::warning::$source_repo has no release tag $tag, so the plugin at $pin was not fetched."
340
+ exit 1
341
+ fi
342
+ plugin_source=$(jq -r '[.plugins[]? | select(.name == "autonomous-sdlc-harness") | .source | strings][0] // empty' "$marketplace_dir/.claude-plugin/marketplace.json" 2>/dev/null || true)
343
+ if [ -z "$plugin_source" ]; then
344
+ echo "::warning::$tag's .claude-plugin/marketplace.json has no readable autonomous-sdlc-harness entry, so the plugin at $pin was not handed to the mention session."
345
+ exit 1
346
+ fi
347
+ plugin_dir="$marketplace_dir/${plugin_source#./}"
348
+ version=$(jq -r '.version // empty' "$plugin_dir/.claude-plugin/plugin.json" 2>/dev/null || true)
349
+ if [ "$version" != "$pin" ]; then
350
+ echo "::warning::the plugin cloned at $tag is version '${version:-none}', not $pin, so it was not handed to the mention session."
351
+ exit 1
352
+ fi
353
+ echo "HARNESS_MENTION_PLUGIN_DIR=$plugin_dir" >> "$GITHUB_ENV"
354
+
229
355
  - name: Act on the comment, review, close or deletion
356
+ env:
357
+ IN_OAUTH: ${{ github.event_name == 'issue_comment' && secrets.CLAUDE_CODE_OAUTH_TOKEN || '' }}
358
+ IN_API: ${{ github.event_name == 'issue_comment' && secrets.ANTHROPIC_API_KEY || '' }}
230
359
  run: bash "$SCRIPTS_DIR/remote-run.sh" control || [ $? -eq 2 ]
@@ -49,7 +49,13 @@
49
49
  # job's status by that name
50
50
  # harness-control.yml the concurrency group
51
51
  # `harness-review-<branch>`, shared by its
52
- # review jobs and the `collect` job here
52
+ # review jobs and the `collect` job here;
53
+ # and, read by its `Fetch the pinned
54
+ # plugin` step, the first
55
+ # `HARNESS_CLI_VERSION:` env line (key, then
56
+ # the quoted version) and the release-tag
57
+ # shape `autonomous-sdlc-harness--v<version>`
58
+ # of the `Install the pinned plugin` step
53
59
  #
54
60
  # THE COLLECT JOB. When a run ends, `remote-run.sh collect` starts the next
55
61
  # user-review round from every review requesting changes that `control`
@@ -563,6 +569,9 @@ jobs:
563
569
  unset IN_OAUTH IN_API
564
570
  bash "$SCRIPTS_DIR/autonomous-watcher.sh" job "$HARNESS_INPUT_BRANCH" "$HARNESS_INPUT_ENGINE" "$HARNESS_INPUT_RESUME"
565
571
 
572
+ # Runs under always(), so a cancelled or failed job still pushes its commits. push-branch.sh
573
+ # pushes nothing back for a branch origin no longer has, so a run stopped by its branch's
574
+ # deletion does not re-create it (Gate 12 round 9, finding 1).
566
575
  - name: Push the branch
567
576
  if: always() && env.SCRIPTS_DIR != ''
568
577
  run: bash "$SCRIPTS_DIR/push-branch.sh"
@@ -1357,7 +1357,10 @@ job_report() {
1357
1357
  # API-overload self-pause. `user` and `budget` are written
1358
1358
  # before the PAUSE is dropped, while still `running`;
1359
1359
  # classify_run_exit settles the reason as the run pauses,
1360
- # `user` first, then `usage`, then `budget`. Cleared when
1360
+ # `user` first, then `usage`, then `budget`. A `user` still
1361
+ # set when the run exits `parked` or `park_loop` is read by
1362
+ # `remote-run.sh report` for that park's comment, and
1363
+ # run_job clears it at the job's end. Cleared when
1361
1364
  # job mode relaunches the run — plus `killed`, a registry-only
1362
1365
  # value `remote-run.sh sync` derives when a finished run's
1363
1366
  # bundle still says `running`, or a finished run left no
@@ -36,6 +36,21 @@
36
36
  # tuning seam: default 5, and anything but a non-negative integer falls back to
37
37
  # 5 with one line.
38
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.
53
+ #
39
54
  # DEFENCE IN DEPTH, NOT THE SOLE GUARD. The caller-agnostic backstop is the
40
55
  # committed pre-push hook `init` writes into the configured `githooksDir`, which
41
56
  # git runs on every push GIT PERFORMS, however it was started. That backstop is
@@ -58,7 +73,8 @@
58
73
  #
59
74
  # WHAT IT NEVER DOES. It performs only a fast-forward push of already-committed
60
75
  # work: no `--force`, no `--force-with-lease`, no history-rewriting flag, no
61
- # commit of its own, and no non-zero exit. It never retries a push git reports
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
62
78
  # as `! [rejected]` (non-fast-forward, fetch first), and never fetches or
63
79
  # rebases: a push that lost a race fails loudly, which is the run-control rule
64
80
  # of record (docs/github-run-control.md -> "A push that loses a race fails
@@ -77,6 +93,9 @@
77
93
  # no upstream yet push-branch.sh "$d" -> exit 0; `git -C "$b" branch` lists
78
94
  # feat_x and the branch now has an upstream
79
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
80
99
  # protected branch git -C "$d" checkout -q -b trunk; push-branch.sh "$d"
81
100
  # -> exit 0, refusal message, "$b" unchanged
82
101
  # unresolvable printf 'x' > "$d/harness.config.json"; push-branch.sh "$d"
@@ -157,9 +176,31 @@ case "$protected" in
157
176
  ;;
158
177
  esac
159
178
 
160
- # Push the branch. Use the existing upstream when one is configured; otherwise
161
- # set it on the fly, so a branch with no upstream is still pushed. Fast-forward
162
- # only.
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
199
+ fi
200
+
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.
163
204
  if git -C "$top" rev-parse --abbrev-ref --symbolic-full-name '@{u}' >/dev/null 2>&1; then
164
205
  push_cmd=(git -C "$top" push)
165
206
  else