@bongos/core 1.19.650 → 1.19.651
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/.bongos-core.json +13 -13
- package/docs/module-api-changelog.md +2 -0
- package/package-lock.json +2 -2
- package/package.json +1 -1
- package/scripts/gds/ship-land.js +34 -1
- package/scripts/gds/ship-merge.js +76 -10
- package/scripts/gds/ship.js +13 -1
- package/src/module-api.js +1 -1
- package/tests/ship_ci_deploy.mjs +106 -1
package/.bongos-core.json
CHANGED
|
@@ -2,11 +2,11 @@
|
|
|
2
2
|
"artifact": "bongos-core",
|
|
3
3
|
"manifest_schema": 1,
|
|
4
4
|
"generator": "scripts/gds/package-core.js",
|
|
5
|
-
"core_version": "1.19.
|
|
6
|
-
"core_contract": "1.19.
|
|
7
|
-
"source_commit": "
|
|
5
|
+
"core_version": "1.19.651",
|
|
6
|
+
"core_contract": "1.19.651",
|
|
7
|
+
"source_commit": "c13a88d638588dedd2454579a62cb76e4415656a",
|
|
8
8
|
"source_ref": "HEAD",
|
|
9
|
-
"built_at": "2026-09-10T20:
|
|
9
|
+
"built_at": "2026-09-10T20:32:32.558Z",
|
|
10
10
|
"redaction": {
|
|
11
11
|
"model": "docs-redacted+functional-verbatim",
|
|
12
12
|
"docs_redacted": 471,
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
"gate": "passed"
|
|
18
18
|
},
|
|
19
19
|
"file_count": 2604,
|
|
20
|
-
"tree_sha256": "
|
|
20
|
+
"tree_sha256": "3e626f458ea19a08ab773ec27d6efe07ab6901a906091aa433ac27aa7789173e",
|
|
21
21
|
"files": [
|
|
22
22
|
{
|
|
23
23
|
"path": ".claude/skills/backlog-review/SKILL.md",
|
|
@@ -2777,7 +2777,7 @@
|
|
|
2777
2777
|
{
|
|
2778
2778
|
"path": "docs/module-api-changelog.md",
|
|
2779
2779
|
"mode": "0000644",
|
|
2780
|
-
"sha256": "
|
|
2780
|
+
"sha256": "b19f014bb424a4974164c08f892cb0103af216bebd411615b1d997f01d6054a7"
|
|
2781
2781
|
},
|
|
2782
2782
|
{
|
|
2783
2783
|
"path": "docs/modules-contract.md",
|
|
@@ -7732,12 +7732,12 @@
|
|
|
7732
7732
|
{
|
|
7733
7733
|
"path": "package-lock.json",
|
|
7734
7734
|
"mode": "0000644",
|
|
7735
|
-
"sha256": "
|
|
7735
|
+
"sha256": "e70fe3cda6fcf43f7416f03c0232be6be8ff84fca2aaac504dac9268b33d8f69"
|
|
7736
7736
|
},
|
|
7737
7737
|
{
|
|
7738
7738
|
"path": "package.json",
|
|
7739
7739
|
"mode": "0000644",
|
|
7740
|
-
"sha256": "
|
|
7740
|
+
"sha256": "17a361e96b81d29aaddc6b2f73d2d3cdf007914df8e14d6f3fcd0ea7c12378c8"
|
|
7741
7741
|
},
|
|
7742
7742
|
{
|
|
7743
7743
|
"path": "public-docs/index.html",
|
|
@@ -8902,7 +8902,7 @@
|
|
|
8902
8902
|
{
|
|
8903
8903
|
"path": "scripts/gds/ship-land.js",
|
|
8904
8904
|
"mode": "0000644",
|
|
8905
|
-
"sha256": "
|
|
8905
|
+
"sha256": "d8cd184816230b7b246ec7afaa2ea285b7ba0e24bb8f47f79d2bc7fa5b423429"
|
|
8906
8906
|
},
|
|
8907
8907
|
{
|
|
8908
8908
|
"path": "scripts/gds/ship-memory-sync.js",
|
|
@@ -8912,7 +8912,7 @@
|
|
|
8912
8912
|
{
|
|
8913
8913
|
"path": "scripts/gds/ship-merge.js",
|
|
8914
8914
|
"mode": "0000644",
|
|
8915
|
-
"sha256": "
|
|
8915
|
+
"sha256": "b602b4cf8f0e66610991237e1cb7ade9cd5c14659b155ce3512268d40ae670eb"
|
|
8916
8916
|
},
|
|
8917
8917
|
{
|
|
8918
8918
|
"path": "scripts/gds/ship-preflight-steps.js",
|
|
@@ -8952,7 +8952,7 @@
|
|
|
8952
8952
|
{
|
|
8953
8953
|
"path": "scripts/gds/ship.js",
|
|
8954
8954
|
"mode": "0000644",
|
|
8955
|
-
"sha256": "
|
|
8955
|
+
"sha256": "6fd0329b8ad7d45fbb93deda02142c2b766c9e01b669d2d94eeafcb1caa225b0"
|
|
8956
8956
|
},
|
|
8957
8957
|
{
|
|
8958
8958
|
"path": "scripts/gds/skill-lint.js",
|
|
@@ -9492,7 +9492,7 @@
|
|
|
9492
9492
|
{
|
|
9493
9493
|
"path": "src/module-api.js",
|
|
9494
9494
|
"mode": "0000644",
|
|
9495
|
-
"sha256": "
|
|
9495
|
+
"sha256": "de52ed5ea35350aff9ade88ecc06393252ef5294ea7b76aca8222f956367472a"
|
|
9496
9496
|
},
|
|
9497
9497
|
{
|
|
9498
9498
|
"path": "src/module-loader/catalog.js",
|
|
@@ -12552,7 +12552,7 @@
|
|
|
12552
12552
|
{
|
|
12553
12553
|
"path": "tests/ship_ci_deploy.mjs",
|
|
12554
12554
|
"mode": "0000644",
|
|
12555
|
-
"sha256": "
|
|
12555
|
+
"sha256": "83f8bfb0979b8950a8895eae7710981c19e30878b89fe648eb2dc2e8098c9de1"
|
|
12556
12556
|
},
|
|
12557
12557
|
{
|
|
12558
12558
|
"path": "tests/ship_error_shape.mjs",
|
|
@@ -1749,5 +1749,7 @@ is load-bearing: the script throws rather than guess if it is missing, and
|
|
|
1749
1749
|
landed since 1.19.648 with no explicit bump. run 34523111338. (task 1002620)
|
|
1750
1750
|
1.19.650 — CI auto-patch (publish-on-merge, ADR 0161): carrier for merges
|
|
1751
1751
|
landed since 1.19.649 with no explicit bump. run 34523753119. (task 1002620)
|
|
1752
|
+
1.19.651 — CI auto-patch (publish-on-merge, ADR 0161): carrier for merges
|
|
1753
|
+
landed since 1.19.650 with no explicit bump. run 34526936330. (task 1002620)
|
|
1752
1754
|
---------------------------------------------------------------------------
|
|
1753
1755
|
```
|
package/package-lock.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bongos/core",
|
|
3
|
-
"version": "1.19.
|
|
3
|
+
"version": "1.19.651",
|
|
4
4
|
"lockfileVersion": 3,
|
|
5
5
|
"requires": true,
|
|
6
6
|
"packages": {
|
|
7
7
|
"": {
|
|
8
8
|
"name": "@bongos/core",
|
|
9
|
-
"version": "1.19.
|
|
9
|
+
"version": "1.19.651",
|
|
10
10
|
"license": "AGPL-3.0-or-later",
|
|
11
11
|
"dependencies": {
|
|
12
12
|
"express": "^4.21.2",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bongos/core",
|
|
3
|
-
"version": "1.19.
|
|
3
|
+
"version": "1.19.651",
|
|
4
4
|
"description": "Cloud Bongos — the AI-first build platform core (GDS + platform surfaces + module system), installed as a versioned dependency (ADR 0108).",
|
|
5
5
|
"license": "AGPL-3.0-or-later",
|
|
6
6
|
"main": "src/platform-server.js",
|
package/scripts/gds/ship-land.js
CHANGED
|
@@ -35,9 +35,37 @@ const { normalizeApiError } = require('./ship-io.js');
|
|
|
35
35
|
// Only landBailed makes the card say NOT landed. Purely additive: no bail site
|
|
36
36
|
// changes what it DOES, only what it now reports back.
|
|
37
37
|
// ADR 0184: an EMPTY ship has no commit to prove, so it must DECLARE itself.
|
|
38
|
+
// task 1002572 (PART 2): a bail also carries an optional `bailId` — the stable
|
|
39
|
+
// name of WHICH refusal fired, so a caller can route the recoverable ones to the
|
|
40
|
+
// push+PR path instead of stranding. `null` for every un-tagged bail, which keeps
|
|
41
|
+
// them terminal (fail-safe: an untagged bail never gains a silent fallback).
|
|
38
42
|
function landed(o = {}) { return { ok: true, pending: false, reason: null, nextStep: null, noArtifact: o.noArtifact === true, noArtifactReason: o.noArtifactReason || null }; }
|
|
39
43
|
function landPending() { return { ok: false, pending: true, reason: null, nextStep: null }; }
|
|
40
|
-
function landBailed(reason, nextStep = null) { return { ok: false, pending: false, reason, nextStep }; }
|
|
44
|
+
function landBailed(reason, nextStep = null, bailId = null) { return { ok: false, pending: false, reason, nextStep, bailId }; }
|
|
45
|
+
|
|
46
|
+
// ---------- which bails a PR can still land (task 1002572, part 2) ----------
|
|
47
|
+
//
|
|
48
|
+
// A builder in a SINGLE main checkout can never complete a local merge: the tree
|
|
49
|
+
// that would RECEIVE the merge is the very tree holding the feature branch. That
|
|
50
|
+
// is a property of the machine, not of the work — the branch itself is perfectly
|
|
51
|
+
// landable, just not from here. So that one bail must fall back to the push+PR
|
|
52
|
+
// path (ciLand) rather than parking the task at 'confirmed' unpaid (ADR 0120).
|
|
53
|
+
//
|
|
54
|
+
// Everything else stays LOUD. A merge conflict, a red post-merge smoke, a failed
|
|
55
|
+
// push, an unreachable origin: those are real failures, and a PR would carry the
|
|
56
|
+
// same breakage to main. Membership is opt-in by id for exactly that reason —
|
|
57
|
+
// blanket-converting bails into PRs is the way this fix goes wrong.
|
|
58
|
+
const LAND_BAIL_MAIN_CHECKOUT_ON_BRANCH = 'main-checkout-on-branch';
|
|
59
|
+
const LAND_BAIL_NO_TASK_BRANCH = 'no-task-branch';
|
|
60
|
+
const PR_RECOVERABLE_BAILS = new Set([LAND_BAIL_MAIN_CHECKOUT_ON_BRANCH]);
|
|
61
|
+
|
|
62
|
+
// landBailRecoverableByPr — PURE. Can this bail still be landed by opening a PR?
|
|
63
|
+
// `no-task-branch` is deliberately NOT in the set: standing on main there is no
|
|
64
|
+
// head branch to open a PR FROM, so the PR path has nothing to offer it (its cure
|
|
65
|
+
// is to go back to the task branch and re-ship, which now works).
|
|
66
|
+
function landBailRecoverableByPr(bailId) {
|
|
67
|
+
return PR_RECOVERABLE_BAILS.has(bailId);
|
|
68
|
+
}
|
|
41
69
|
|
|
42
70
|
// The short COMMAND form of strandNextStep (which is prose, sized for the log).
|
|
43
71
|
// The card has room for one command, not a sentence.
|
|
@@ -639,6 +667,11 @@ module.exports = {
|
|
|
639
667
|
ciLand,
|
|
640
668
|
classifyLandOutcome,
|
|
641
669
|
formatPublishFailure,
|
|
670
|
+
// task 1002572 (PART 2): the bail ids + the pure "can a PR still land this?"
|
|
671
|
+
// decision, so the single-checkout fallback is opt-in per bail and unit-tested.
|
|
672
|
+
LAND_BAIL_MAIN_CHECKOUT_ON_BRANCH,
|
|
673
|
+
LAND_BAIL_NO_TASK_BRANCH,
|
|
674
|
+
landBailRecoverableByPr,
|
|
642
675
|
landBailed,
|
|
643
676
|
landPending,
|
|
644
677
|
landPendingLines,
|
|
@@ -1,10 +1,14 @@
|
|
|
1
1
|
// scripts/gds/ship-merge.js — autoMerge — the merge/regen/push/land chain that puts the branch on main.
|
|
2
2
|
//
|
|
3
3
|
// Carved out of scripts/gds/ship.js by task 1003205 (goal 1000079, the R03
|
|
4
|
-
// de-monolith plan) — a STRUCTURAL move, not a rewrite
|
|
5
|
-
//
|
|
6
|
-
// re-exports every name and owns the CLI entry point, so the ship contract and
|
|
4
|
+
// de-monolith plan) — a STRUCTURAL move, not a rewrite. ship.js remains the facade
|
|
5
|
+
// that re-exports every name and owns the CLI entry point, so the ship contract and
|
|
7
6
|
// every flag are untouched, and the other scripts that import from it are too.
|
|
7
|
+
//
|
|
8
|
+
// task 1002572 (PART 2) split the entry point in two: `mergeLocally` is the original
|
|
9
|
+
// merge/regen/push/deploy chain, and `autoMerge` is a thin wrapper that routes the one
|
|
10
|
+
// bail a PR can still land — a single checkout whose main worktree holds the feature
|
|
11
|
+
// branch — into the push+PR path instead of stranding the task at 'confirmed'.
|
|
8
12
|
|
|
9
13
|
'use strict';
|
|
10
14
|
|
|
@@ -13,7 +17,14 @@ const { cleanupMacFinderDupRefs } = require('../../modules/lifecycle/ship-prefli
|
|
|
13
17
|
const mergeLock = require('../../modules/lifecycle/merge-lock');
|
|
14
18
|
const { buildMergeArgv, gitArgvOk, gitOk, shellOk } = require('./ship-git.js');
|
|
15
19
|
const { mainIsProtected, pushVia, readDeployConfigRaw, resolveDeployMode, resolveDeployTarget, safeBranding, shouldBailOnFetchFailure } = require('./ship-deploy-target.js');
|
|
16
|
-
const {
|
|
20
|
+
const {
|
|
21
|
+
LAND_BAIL_MAIN_CHECKOUT_ON_BRANCH,
|
|
22
|
+
LAND_BAIL_NO_TASK_BRANCH,
|
|
23
|
+
ciLand,
|
|
24
|
+
landBailRecoverableByPr,
|
|
25
|
+
landBailed,
|
|
26
|
+
landed,
|
|
27
|
+
} = require('./ship-land.js');
|
|
17
28
|
const { runSmoke } = require('./ship-grade.js');
|
|
18
29
|
const { currentBranch, mainWorktreeBusy, regenerateApiClient, regenerateApiDocs, regenerateDiagrams, regenerateFileMap, regenerateRepoMap, regenerateSessionIndex } = require('./ship-regen.js');
|
|
19
30
|
const { REPO_ROOT } = require('./ship-state.js');
|
|
@@ -23,7 +34,49 @@ function zeroCommitVerdict(commitsAhead, dbOnly) {
|
|
|
23
34
|
return dbOnly ? 'db-only' : 'refuse';
|
|
24
35
|
}
|
|
25
36
|
|
|
26
|
-
|
|
37
|
+
// onMainNextStep — PURE. The recovery command for the other half of the
|
|
38
|
+
// single-checkout catch-22 (task 1002572): the builder is standing ON main, so
|
|
39
|
+
// there is no head branch to merge FROM and none to open a PR from either. Since
|
|
40
|
+
// mergeLocally's sibling bail now falls back to the PR path, shipping from the
|
|
41
|
+
// task branch WORKS even in a single checkout — so the cure is "go back to the
|
|
42
|
+
// branch and re-ship", not the circular /merge-mode this used to advise. Only
|
|
43
|
+
// name the branch when it actually exists locally; otherwise fall back to
|
|
44
|
+
// /merge-mode rather than inventing a checkout target.
|
|
45
|
+
function onMainNextStep(taskId, taskBranchExists) {
|
|
46
|
+
return taskBranchExists
|
|
47
|
+
? `git checkout task-${taskId} && node scripts/gds/ship.js ${taskId}`
|
|
48
|
+
: '/merge-mode';
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// localTaskBranchExists — does this checkout still hold the task's own branch? The
|
|
52
|
+
// discriminator onMainNextStep needs, so the advice only names a branch that is
|
|
53
|
+
// really there (a builder who deleted it gets /merge-mode, not a failing checkout).
|
|
54
|
+
function localTaskBranchExists(taskId) {
|
|
55
|
+
return gitArgvOk(['rev-parse', '--verify', '--quiet', `refs/heads/task-${taskId}`], { stdio: 'ignore' });
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// autoMerge — the land entry point. Runs the local merge chain, then (task 1002572
|
|
59
|
+
// PART 2) routes the ONE bail a PR can still land into the push+PR path instead of
|
|
60
|
+
// leaving the task at 'confirmed'. The fallback deliberately sits OUTSIDE
|
|
61
|
+
// mergeLocally: its `finally` releases the shared merge lock and normalizes the main
|
|
62
|
+
// worktree, and the PR poll that follows can run for minutes — holding the lock
|
|
63
|
+
// across it would wedge every other ship on this machine.
|
|
64
|
+
// `deps` is a test seam ONLY — the routing is the whole deliverable and it cannot
|
|
65
|
+
// be reached from a unit test otherwise (mergeLocally needs a real second checkout
|
|
66
|
+
// and an authed gh). Callers pass nothing; the defaults are the real functions.
|
|
67
|
+
async function autoMerge(taskId, valueSummary, opts = {}, deps = {}) {
|
|
68
|
+
const merge = deps.mergeLocally || mergeLocally;
|
|
69
|
+
const land = deps.ciLand || ciLand;
|
|
70
|
+
const branchOf = deps.currentBranch || currentBranch;
|
|
71
|
+
const outcome = await merge(taskId, valueSummary, opts);
|
|
72
|
+
if (!landBailRecoverableByPr(outcome && outcome.bailId)) return outcome;
|
|
73
|
+
const branch = branchOf();
|
|
74
|
+
console.log(' auto-merge: a local merge is impossible from this checkout, but the branch is still landable —');
|
|
75
|
+
console.log(' falling back to the push+PR land path. Nothing is stranded; the server lands the PR.');
|
|
76
|
+
return await land(taskId, branch, valueSummary);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
async function mergeLocally(taskId, valueSummary, { dbOnly = false } = {}) {
|
|
27
80
|
const branch = currentBranch();
|
|
28
81
|
if (!branch || branch === 'main' || branch === 'HEAD') {
|
|
29
82
|
// task 1002723: an --allow-empty / --db-only ship legitimately runs FROM the
|
|
@@ -40,8 +93,15 @@ async function autoMerge(taskId, valueSummary, { dbOnly = false } = {}) {
|
|
|
40
93
|
console.log(` auto-merge: skipped (current branch is ${branch})`);
|
|
41
94
|
// The merge source IS the current branch, so on main there is nothing to
|
|
42
95
|
// merge. Half of the single-checkout catch-22 this task documents: the other
|
|
43
|
-
// half is the "main worktree is on <branch>, not main" bail below
|
|
44
|
-
|
|
96
|
+
// half is the "main worktree is on <branch>, not main" bail below, which now
|
|
97
|
+
// falls back to a PR. This half CANNOT — a PR needs a head branch and there
|
|
98
|
+
// is none here — so it stays a bail, but with a next step that actually
|
|
99
|
+
// recovers instead of pointing back at the same dead end (task 1002572).
|
|
100
|
+
return landBailed(
|
|
101
|
+
`nothing to merge — this checkout is on ${branch}, not a task branch`,
|
|
102
|
+
onMainNextStep(taskId, localTaskBranchExists(taskId)),
|
|
103
|
+
LAND_BAIL_NO_TASK_BRANCH,
|
|
104
|
+
);
|
|
45
105
|
}
|
|
46
106
|
|
|
47
107
|
// Refresh the data-driven diagrams before computing commits-ahead, so a
|
|
@@ -153,14 +213,17 @@ async function autoMerge(taskId, valueSummary, { dbOnly = false } = {}) {
|
|
|
153
213
|
// The main worktree must currently have main checked out.
|
|
154
214
|
const onMain = gitOk('git rev-parse --abbrev-ref HEAD', { cwd: REPO_ROOT });
|
|
155
215
|
if (onMain !== 'main') {
|
|
156
|
-
console.log(` auto-merge: main worktree is on ${onMain}, not main
|
|
216
|
+
console.log(` auto-merge: main worktree is on ${onMain}, not main — a local merge is impossible here`);
|
|
157
217
|
// The other half of the single-checkout catch-22: the merge target tree is
|
|
158
218
|
// the one holding the feature branch, so there is nowhere to merge INTO.
|
|
159
|
-
//
|
|
160
|
-
//
|
|
219
|
+
// task 1002572 (PART 2): this is a property of the MACHINE, not of the work
|
|
220
|
+
// — the branch lands fine via a PR. Tagged PR-recoverable so autoMerge falls
|
|
221
|
+
// back instead of parking the task at 'confirmed' unpaid. The reason +
|
|
222
|
+
// nextStep are kept for the case where the fallback itself bails.
|
|
161
223
|
return landBailed(
|
|
162
224
|
`the main checkout is on ${onMain}, not main — work a task in its own worktree so the main checkout stays on main`,
|
|
163
225
|
'/merge-mode',
|
|
226
|
+
LAND_BAIL_MAIN_CHECKOUT_ON_BRANCH,
|
|
164
227
|
);
|
|
165
228
|
}
|
|
166
229
|
|
|
@@ -293,5 +356,8 @@ async function autoMerge(taskId, valueSummary, { dbOnly = false } = {}) {
|
|
|
293
356
|
|
|
294
357
|
module.exports = {
|
|
295
358
|
autoMerge,
|
|
359
|
+
// task 1002572 (PART 2): the pure recovery command for a ship run from main —
|
|
360
|
+
// exported so the "don't point back at the dead end" wording is pinned.
|
|
361
|
+
onMainNextStep,
|
|
296
362
|
zeroCommitVerdict,
|
|
297
363
|
};
|
package/scripts/gds/ship.js
CHANGED
|
@@ -105,6 +105,9 @@ const {
|
|
|
105
105
|
const {
|
|
106
106
|
classifyLandOutcome,
|
|
107
107
|
formatPublishFailure,
|
|
108
|
+
LAND_BAIL_MAIN_CHECKOUT_ON_BRANCH,
|
|
109
|
+
LAND_BAIL_NO_TASK_BRANCH,
|
|
110
|
+
landBailRecoverableByPr,
|
|
108
111
|
landBailed,
|
|
109
112
|
landPending,
|
|
110
113
|
landPendingLines,
|
|
@@ -140,7 +143,7 @@ const {
|
|
|
140
143
|
isLinkifiableDocPath,
|
|
141
144
|
parseDeletedUpstreamPaths,
|
|
142
145
|
} = require('./ship-preflight-steps.js');
|
|
143
|
-
const { zeroCommitVerdict } = require('./ship-merge.js');
|
|
146
|
+
const { onMainNextStep, zeroCommitVerdict } = require('./ship-merge.js');
|
|
144
147
|
const {
|
|
145
148
|
buildGradePayload,
|
|
146
149
|
buildPriorRoundContext,
|
|
@@ -303,6 +306,15 @@ module.exports = {
|
|
|
303
306
|
// ship unless --db-only is explicitly passed. Exported pure so the gate is
|
|
304
307
|
// unit-tested (has-commits / db-only / refuse) without git.
|
|
305
308
|
zeroCommitVerdict,
|
|
309
|
+
// task 1002572 (PART 2): the single-checkout land fallback, pure halves only.
|
|
310
|
+
// landBailRecoverableByPr is the whole decision — which auto-merge bail a PR can
|
|
311
|
+
// still land (the main checkout holding the branch) versus which are REAL
|
|
312
|
+
// failures a PR would only carry to main. onMainNextStep is the recovery command
|
|
313
|
+
// for the other half of the catch-22, where no head branch exists to PR from.
|
|
314
|
+
LAND_BAIL_MAIN_CHECKOUT_ON_BRANCH,
|
|
315
|
+
LAND_BAIL_NO_TASK_BRANCH,
|
|
316
|
+
landBailRecoverableByPr,
|
|
317
|
+
onMainNextStep,
|
|
306
318
|
// touches[] cleanup [3/8] (task 878): folder-tag derivation, now over the
|
|
307
319
|
// committed diff — exported pure so the regression test can assert it.
|
|
308
320
|
deriveTagsFromPaths,
|
package/src/module-api.js
CHANGED
|
@@ -71,7 +71,7 @@ const { responsibilityFor, ROLE_RESPONSIBILITIES } = require('./role-responsibil
|
|
|
71
71
|
// there. scripts/gds/bump-version.js still rewrites the literal below; it appends
|
|
72
72
|
// the entry to that file. Look for a version's history there, not here.
|
|
73
73
|
// ---------------------------------------------------------------------------
|
|
74
|
-
const CORE_VERSION = '1.19.
|
|
74
|
+
const CORE_VERSION = '1.19.651'; // CI auto-patch carrier (ADR 0161); changelog: docs/module-api-changelog.md
|
|
75
75
|
|
|
76
76
|
// A namespaced logger so a module's log lines are attributable + consistent.
|
|
77
77
|
// Usage: const log = api.logger('dev-box'); log.info('mounted');
|
package/tests/ship_ci_deploy.mjs
CHANGED
|
@@ -44,7 +44,7 @@ function test(name, fn) {
|
|
|
44
44
|
}
|
|
45
45
|
}
|
|
46
46
|
|
|
47
|
-
const { parseDeployMode, effectiveDeployMode, buildPrCreateArgv, buildPrMergeArgv, deployRunSucceeded, shouldBailOnFetchFailure, classifyLandOutcome, strandNextStep, pickStaleGateReviewRun, landPollAfterAutoMerge, reconciledCardStatus, landed, landPending, landBailed, strandCommand, landPendingLines } = ship;
|
|
47
|
+
const { parseDeployMode, effectiveDeployMode, buildPrCreateArgv, buildPrMergeArgv, deployRunSucceeded, shouldBailOnFetchFailure, classifyLandOutcome, strandNextStep, pickStaleGateReviewRun, landPollAfterAutoMerge, reconciledCardStatus, landed, landPending, landBailed, strandCommand, landPendingLines, landBailRecoverableByPr, onMainNextStep, LAND_BAIL_MAIN_CHECKOUT_ON_BRANCH, LAND_BAIL_NO_TASK_BRANCH } = ship;
|
|
48
48
|
|
|
49
49
|
// ---- 1. parseDeployMode precedence + safe default ----
|
|
50
50
|
|
|
@@ -628,6 +628,111 @@ test('1002645 reconcileBlockedLines: never reads as success (no "NOT a failure",
|
|
|
628
628
|
assert.ok(!/Prod is live/.test(lines), 'nor the success line');
|
|
629
629
|
});
|
|
630
630
|
|
|
631
|
+
// ---- task 1002572 (PART 2): the single-checkout land fallback ----
|
|
632
|
+
// A builder with ONE checkout can never complete a local merge — the tree that
|
|
633
|
+
// would RECEIVE the merge is the tree holding the feature branch. That used to
|
|
634
|
+
// park the task at 'confirmed', unpaid under ADR 0120, with nothing landing. The
|
|
635
|
+
// branch itself is landable, so that bail now falls back to the push+PR path.
|
|
636
|
+
// What must NOT happen is a blanket conversion: a merge conflict, a red
|
|
637
|
+
// post-merge smoke or a failed push are real failures, and a PR would carry the
|
|
638
|
+
// same breakage to main. The decision is therefore opt-in PER BAIL, and these
|
|
639
|
+
// tests pin both halves of it.
|
|
640
|
+
|
|
641
|
+
test('1002572 landBailed: carries the bail id, and defaults to none', () => {
|
|
642
|
+
assert.equal(landBailed('r', 'n', LAND_BAIL_MAIN_CHECKOUT_ON_BRANCH).bailId, LAND_BAIL_MAIN_CHECKOUT_ON_BRANCH);
|
|
643
|
+
// Fail-safe: every pre-existing bail site passes no id, so it stays terminal.
|
|
644
|
+
assert.equal(landBailed('r', 'n').bailId, null);
|
|
645
|
+
// Still a bail — the fallback must not have turned it into a success.
|
|
646
|
+
assert.equal(landBailed('r', 'n', LAND_BAIL_MAIN_CHECKOUT_ON_BRANCH).ok, false);
|
|
647
|
+
assert.equal(landBailed('r', 'n', LAND_BAIL_MAIN_CHECKOUT_ON_BRANCH).pending, false);
|
|
648
|
+
});
|
|
649
|
+
|
|
650
|
+
test('1002572 landBailRecoverableByPr: only the main-checkout-holds-the-branch bail falls back', () => {
|
|
651
|
+
assert.equal(landBailRecoverableByPr(LAND_BAIL_MAIN_CHECKOUT_ON_BRANCH), true);
|
|
652
|
+
// On main there is no head branch, so a PR has nothing to open FROM.
|
|
653
|
+
assert.equal(landBailRecoverableByPr(LAND_BAIL_NO_TASK_BRANCH), false);
|
|
654
|
+
});
|
|
655
|
+
|
|
656
|
+
test('1002572 landBailRecoverableByPr: an untagged bail is terminal (no blanket conversion)', () => {
|
|
657
|
+
// The real failures — conflict, red smoke, failed push, unreachable origin —
|
|
658
|
+
// all reach the caller as untagged bails. Each must stay loud.
|
|
659
|
+
for (const v of [null, undefined, '', 'merge-conflict', 'post-merge-smoke-failed', 'push-failed', 'fetch-failed']) {
|
|
660
|
+
assert.equal(landBailRecoverableByPr(v), false, `${JSON.stringify(v)} must not be PR-recoverable`);
|
|
661
|
+
}
|
|
662
|
+
});
|
|
663
|
+
|
|
664
|
+
test('1002572 onMainNextStep: names the task branch to re-ship from when it exists', () => {
|
|
665
|
+
const step = onMainNextStep(1002572, true);
|
|
666
|
+
assert.match(step, /task-1002572/, 'names the branch to go back to');
|
|
667
|
+
assert.match(step, /node scripts\/gds\/ship\.js 1002572/, 'and the re-ship command for THIS task');
|
|
668
|
+
// The old advice pointed back at the same dead end; that is the regression.
|
|
669
|
+
assert.notEqual(step, '/merge-mode');
|
|
670
|
+
});
|
|
671
|
+
|
|
672
|
+
test('1002572 onMainNextStep: falls back to /merge-mode rather than inventing a branch', () => {
|
|
673
|
+
assert.equal(onMainNextStep(1002572, false), '/merge-mode');
|
|
674
|
+
});
|
|
675
|
+
|
|
676
|
+
// The ROUTING — the actual deliverable. Driven through autoMerge with the merge
|
|
677
|
+
// chain and the PR land stubbed (a real mergeLocally needs a second checkout and
|
|
678
|
+
// an authed gh), so this proves behaviour rather than matching the source.
|
|
679
|
+
const shipMerge = require('../scripts/gds/ship-merge.js');
|
|
680
|
+
|
|
681
|
+
function driveAutoMerge(mergeOutcome) {
|
|
682
|
+
const calls = [];
|
|
683
|
+
const deps = {
|
|
684
|
+
mergeLocally: async () => mergeOutcome,
|
|
685
|
+
ciLand: async (taskId, branch, summary) => {
|
|
686
|
+
calls.push({ taskId, branch, summary });
|
|
687
|
+
return landPending();
|
|
688
|
+
},
|
|
689
|
+
currentBranch: () => 'task-1002572',
|
|
690
|
+
};
|
|
691
|
+
return shipMerge.autoMerge(1002572, 'a summary', {}, deps).then((outcome) => ({ outcome, calls }));
|
|
692
|
+
}
|
|
693
|
+
|
|
694
|
+
await (async () => {
|
|
695
|
+
const recoverable = await driveAutoMerge(landBailed('main checkout is on a branch', '/merge-mode', LAND_BAIL_MAIN_CHECKOUT_ON_BRANCH));
|
|
696
|
+
test('1002572 routing: the single-checkout bail lands via the PR path instead of stranding', () => {
|
|
697
|
+
assert.equal(recoverable.calls.length, 1, 'ciLand was called exactly once');
|
|
698
|
+
assert.deepEqual(recoverable.calls[0], { taskId: 1002572, branch: 'task-1002572', summary: 'a summary' });
|
|
699
|
+
// Pending, not bailed: the card may honestly say "landing automatically" now.
|
|
700
|
+
assert.equal(recoverable.outcome.pending, true);
|
|
701
|
+
assert.equal(recoverable.outcome.reason, null);
|
|
702
|
+
});
|
|
703
|
+
|
|
704
|
+
const terminal = await driveAutoMerge(landBailed('merging task-1002572 into main failed (conflicts?)', '/merge-mode'));
|
|
705
|
+
test('1002572 routing: a real failure stays loud — no PR is opened for a conflict', () => {
|
|
706
|
+
assert.equal(terminal.calls.length, 0, 'a conflict must NOT be converted into a PR');
|
|
707
|
+
assert.equal(terminal.outcome.pending, false);
|
|
708
|
+
assert.match(terminal.outcome.reason, /conflicts/);
|
|
709
|
+
});
|
|
710
|
+
|
|
711
|
+
const onMain = await driveAutoMerge(landBailed('nothing to merge — this checkout is on main, not a task branch', onMainNextStep(1002572, true), LAND_BAIL_NO_TASK_BRANCH));
|
|
712
|
+
test('1002572 routing: standing on main opens no PR (there is no head branch), but names the cure', () => {
|
|
713
|
+
assert.equal(onMain.calls.length, 0);
|
|
714
|
+
assert.match(onMain.outcome.nextStep, /task-1002572/);
|
|
715
|
+
});
|
|
716
|
+
|
|
717
|
+
const success = await driveAutoMerge(landed());
|
|
718
|
+
test('1002572 routing: a successful local merge is untouched (the worktree path does not regress)', () => {
|
|
719
|
+
assert.equal(success.calls.length, 0, 'a clean local merge must not also open a PR');
|
|
720
|
+
assert.equal(success.outcome.ok, true);
|
|
721
|
+
});
|
|
722
|
+
})();
|
|
723
|
+
|
|
724
|
+
// Only the two documented catch-22 bails may carry an id. Behaviour can't see
|
|
725
|
+
// this — a third tagged bail would silently gain a fallback it was never granted.
|
|
726
|
+
test('1002572 wiring: exactly the two catch-22 bails are tagged', () => {
|
|
727
|
+
const src = fs.readFileSync(path.join(__dirname, '..', 'scripts', 'gds', 'ship-merge.js'), 'utf8');
|
|
728
|
+
// Each id appears exactly twice: the require destructure, and its ONE bail site.
|
|
729
|
+
for (const id of [LAND_BAIL_MAIN_CHECKOUT_ON_BRANCH, LAND_BAIL_NO_TASK_BRANCH]) {
|
|
730
|
+
const name = `LAND_BAIL_${id.toUpperCase().replace(/-/g, '_')}`;
|
|
731
|
+
const hits = src.split(name).length - 1;
|
|
732
|
+
assert.equal(hits, 2, `${name}: imported once + used at exactly one bail site (saw ${hits})`);
|
|
733
|
+
}
|
|
734
|
+
});
|
|
735
|
+
|
|
631
736
|
console.log('');
|
|
632
737
|
console.log(`ship_ci_deploy: ${passed} passed, ${failed} failed`);
|
|
633
738
|
process.exit(failed === 0 ? 0 : 1);
|