@bongos/core 1.19.650 → 1.19.652
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 +35 -25
- package/clients/bongos-client/index.d.ts +1 -1
- package/docs/adr/0277-a-box-is-in-use-only-while-a-human-is-attached.md +53 -0
- package/docs/adr/README.md +1 -0
- package/docs/api/openapi.json +4 -1
- package/docs/api-reference.md +1 -1
- package/docs/module-api-changelog.md +4 -0
- package/migrations/core_238_box_unattended_cap.sql +57 -0
- package/modules/dev-box/boxes.js +112 -8
- package/modules/dev-box/routes/box.js +14 -6
- package/package-lock.json +2 -2
- package/package.json +1 -1
- package/scripts/gds/box-infra.js +8 -0
- package/scripts/gds/box.js +35 -9
- 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/boxes.mjs +160 -3
- package/tests/ship_ci_deploy.mjs +106 -1
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);
|