@bongos/core 1.19.924 → 1.19.925
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 +12 -12
- package/docs/module-api-changelog.md +2 -0
- package/package-lock.json +2 -2
- package/package.json +1 -1
- package/scripts/gds/provision-core-upgrade.js +39 -9
- package/scripts/gds/provision-repo.js +26 -0
- package/src/module-api.js +1 -1
- package/tests/core_upgrade_runner.mjs +94 -0
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.925",
|
|
6
|
+
"core_contract": "1.19.925",
|
|
7
|
+
"source_commit": "930b42e46feac9ae2474f05b89fc6f34e34411f9",
|
|
8
8
|
"source_ref": "HEAD",
|
|
9
|
-
"built_at": "2026-09-20T22:
|
|
9
|
+
"built_at": "2026-09-20T22:35:59.969Z",
|
|
10
10
|
"redaction": {
|
|
11
11
|
"model": "docs-redacted+functional-verbatim",
|
|
12
12
|
"docs_redacted": 511,
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
"gate": "passed"
|
|
18
18
|
},
|
|
19
19
|
"file_count": 2849,
|
|
20
|
-
"tree_sha256": "
|
|
20
|
+
"tree_sha256": "10cb3a93d6b8e5f9825e7c3be73d087762b6fd943ed12823c7289f19939249f8",
|
|
21
21
|
"files": [
|
|
22
22
|
{
|
|
23
23
|
"path": ".claude/skills/ask-for-help/SKILL.md",
|
|
@@ -2677,7 +2677,7 @@
|
|
|
2677
2677
|
{
|
|
2678
2678
|
"path": "docs/module-api-changelog.md",
|
|
2679
2679
|
"mode": "0000644",
|
|
2680
|
-
"sha256": "
|
|
2680
|
+
"sha256": "7ada2ca77bedfca9dd58ce88e09d04a5449c08198adb55619285a95239a88481"
|
|
2681
2681
|
},
|
|
2682
2682
|
{
|
|
2683
2683
|
"path": "docs/modules-contract.md",
|
|
@@ -7917,12 +7917,12 @@
|
|
|
7917
7917
|
{
|
|
7918
7918
|
"path": "package-lock.json",
|
|
7919
7919
|
"mode": "0000644",
|
|
7920
|
-
"sha256": "
|
|
7920
|
+
"sha256": "37fbca7af4b1aa573094974c42fa895a9eec301c3af4fe8c2ce168753ce47ccc"
|
|
7921
7921
|
},
|
|
7922
7922
|
{
|
|
7923
7923
|
"path": "package.json",
|
|
7924
7924
|
"mode": "0000644",
|
|
7925
|
-
"sha256": "
|
|
7925
|
+
"sha256": "d667850fddfe0681b9e05312d9558a616f99be4f3eb399a8c6558d7e5745fc5f"
|
|
7926
7926
|
},
|
|
7927
7927
|
{
|
|
7928
7928
|
"path": "public-docs/index.html",
|
|
@@ -8867,7 +8867,7 @@
|
|
|
8867
8867
|
{
|
|
8868
8868
|
"path": "scripts/gds/provision-core-upgrade.js",
|
|
8869
8869
|
"mode": "0000644",
|
|
8870
|
-
"sha256": "
|
|
8870
|
+
"sha256": "05292d556595bb3ce88a3e0b76c10c7cab2fef7f9d7e1af1e0b3610d48830692"
|
|
8871
8871
|
},
|
|
8872
8872
|
{
|
|
8873
8873
|
"path": "scripts/gds/provision-disconnect.js",
|
|
@@ -8892,7 +8892,7 @@
|
|
|
8892
8892
|
{
|
|
8893
8893
|
"path": "scripts/gds/provision-repo.js",
|
|
8894
8894
|
"mode": "0000644",
|
|
8895
|
-
"sha256": "
|
|
8895
|
+
"sha256": "ef448bfb12437b2a3b83d7d726daff6a38b2a5a2db52096cc0150171d73547ce"
|
|
8896
8896
|
},
|
|
8897
8897
|
{
|
|
8898
8898
|
"path": "scripts/gds/provision-teardown.js",
|
|
@@ -9922,7 +9922,7 @@
|
|
|
9922
9922
|
{
|
|
9923
9923
|
"path": "src/module-api.js",
|
|
9924
9924
|
"mode": "0000644",
|
|
9925
|
-
"sha256": "
|
|
9925
|
+
"sha256": "d365390e6290c57d21c149731cc048b379887d6f3146ba02dafdf89ee869739d"
|
|
9926
9926
|
},
|
|
9927
9927
|
{
|
|
9928
9928
|
"path": "src/module-loader/catalog.js",
|
|
@@ -10837,7 +10837,7 @@
|
|
|
10837
10837
|
{
|
|
10838
10838
|
"path": "tests/core_upgrade_runner.mjs",
|
|
10839
10839
|
"mode": "0000644",
|
|
10840
|
-
"sha256": "
|
|
10840
|
+
"sha256": "994af91fda0df0ceb8b6e3eb80dd8bd23595a127a0e1d2e0381cb55e97beb557"
|
|
10841
10841
|
},
|
|
10842
10842
|
{
|
|
10843
10843
|
"path": "tests/cosmos_token_contrast.mjs",
|
|
@@ -2322,5 +2322,7 @@ is load-bearing: the script throws rather than guess if it is missing, and
|
|
|
2322
2322
|
landed since 1.19.922 with no explicit bump. run 35541278656. (task 1002620)
|
|
2323
2323
|
1.19.924 — CI auto-patch (publish-on-merge, ADR 0161): carrier for merges
|
|
2324
2324
|
landed since 1.19.923 with no explicit bump. run 35541674047. (task 1002620)
|
|
2325
|
+
1.19.925 — CI auto-patch (publish-on-merge, ADR 0161): carrier for merges
|
|
2326
|
+
landed since 1.19.924 with no explicit bump. run 35542154729. (task 1002620)
|
|
2325
2327
|
---------------------------------------------------------------------------
|
|
2326
2328
|
```
|
package/package-lock.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bongos/core",
|
|
3
|
-
"version": "1.19.
|
|
3
|
+
"version": "1.19.925",
|
|
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.925",
|
|
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.925",
|
|
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",
|
|
@@ -30,7 +30,7 @@
|
|
|
30
30
|
|
|
31
31
|
const path = require('path');
|
|
32
32
|
const { CONFIG, MANIFEST_VERIFY_INTERVAL_MS, MANIFEST_VERIFY_TRIES, REPO_ROOT } = require('./provision-config.js');
|
|
33
|
-
const { APP_USER, dbName, instanceUser, standaloneRoot } = require('./provision-repo.js');
|
|
33
|
+
const { APP_USER, dbName, instanceUser, selfInstanceRoot, standaloneRoot } = require('./provision-repo.js');
|
|
34
34
|
const { isValidSlug } = require('../../modules/provisioning/provisioning.js');
|
|
35
35
|
|
|
36
36
|
// The shapes whose box this runner can actually reach. Mirrors UPGRADE_SHAPES in the
|
|
@@ -96,7 +96,19 @@ function targetFault(to, available, installed, channel, channelAllows) {
|
|
|
96
96
|
if (!available.includes(to)) {
|
|
97
97
|
return `core ${to} is not published yet — nothing was changed. A release tag appears several minutes before the package itself does; try again once it lands.`;
|
|
98
98
|
}
|
|
99
|
-
|
|
99
|
+
// AN UNREADABLE `installed` IS A FAULT, NOT A FREE PASS (task 1004123).
|
|
100
|
+
//
|
|
101
|
+
// This guard used to be written `if (installed && …)`, so a null installed version
|
|
102
|
+
// skipped the channel check entirely rather than failing it. That is backwards: not
|
|
103
|
+
// knowing where an instance is today is the one state in which "is this jump
|
|
104
|
+
// permitted" cannot be answered, and the channel exists precisely to refuse jumps
|
|
105
|
+
// nobody has looked at. It also hid the defect that motivated this: the control
|
|
106
|
+
// plane read its installed version from the wrong directory for weeks and got null
|
|
107
|
+
// every time, and every press sailed past this line.
|
|
108
|
+
if (!installed) {
|
|
109
|
+
return `could not read the core version installed at this instance — nothing was changed. An upgrade that cannot say where it is starting from cannot be bounded by the '${channel}' channel.`;
|
|
110
|
+
}
|
|
111
|
+
if (channelAllows && !channelAllows(installed, to, channel)) {
|
|
100
112
|
return `core ${to} is outside this project's '${channel}' update channel — nothing was changed. Change the channel first if this is deliberate.`;
|
|
101
113
|
}
|
|
102
114
|
return null;
|
|
@@ -156,7 +168,8 @@ async function coreUpgradeInstance(inst, deps, intent) {
|
|
|
156
168
|
|
|
157
169
|
// ---- 2. is the target real, and permitted -----------------------------------------
|
|
158
170
|
const channelMod = deps.updateChannel || require('./update-channel.js');
|
|
159
|
-
const
|
|
171
|
+
const selfRoot = deps.selfInstanceRoot || selfInstanceRoot;
|
|
172
|
+
const instanceDir = inst.hosting_shape === 'standalone' ? standaloneRoot(inst) : selfRoot();
|
|
160
173
|
const readInstalled = deps.readInstalledCoreVersion || require('./upgrade.js').readInstalledCoreVersion;
|
|
161
174
|
const installed = readInstalled(instanceDir);
|
|
162
175
|
// THE PLATFORM DEFAULT, NOT A PER-INSTANCE CHANNEL — and saying so beats implying
|
|
@@ -190,7 +203,7 @@ async function coreUpgradeInstance(inst, deps, intent) {
|
|
|
190
203
|
: '';
|
|
191
204
|
// Built by the SAME function the preview uses, so "what you were shown" and "what runs"
|
|
192
205
|
// cannot drift apart into two hand-maintained flag lists (task 1004121).
|
|
193
|
-
const { cmd, runDir } = upgradeInvocation(inst, to, { privileged, requestedBy });
|
|
206
|
+
const { cmd, runDir } = upgradeInvocation(inst, to, { privileged, requestedBy, selfRoot });
|
|
194
207
|
|
|
195
208
|
if (!apply) {
|
|
196
209
|
// A --dry-run runner tick used to only PRINT this command. It now runs the CLI's own
|
|
@@ -269,18 +282,31 @@ function parsePreflight(out) {
|
|
|
269
282
|
// HERE rather than left to instanceUser()'s own throw: that throw is correct today, but
|
|
270
283
|
// a call site whose safety depends on a helper's internals is one refactor away from a
|
|
271
284
|
// shell-injection path (and the apply leg's own guard sits at the route, not here).
|
|
272
|
-
function upgradeInvocation(inst, to, { privileged = false, dryRun = false, requestedBy = '' } = {}) {
|
|
285
|
+
function upgradeInvocation(inst, to, { privileged = false, dryRun = false, requestedBy = '', selfRoot = selfInstanceRoot } = {}) {
|
|
273
286
|
if (!isValidSlug(inst && inst.slug)) {
|
|
274
287
|
throw new Error(`upgradeInvocation: refusing to build a shell command from an invalid slug ${JSON.stringify(inst && inst.slug)}`);
|
|
275
288
|
}
|
|
276
|
-
|
|
289
|
+
// WHERE THE CLI LIVES AND WHERE THE TARGET LIVES ARE DIFFERENT PLACES, TWICE OVER.
|
|
290
|
+
//
|
|
291
|
+
// `upgrade.js` ships in the CORE, and a provisioned instance root holds no scripts/
|
|
292
|
+
// directory at all — so the CLI runs from REPO_ROOT and NAMES its target (task
|
|
293
|
+
// 1004053, which fixed every standalone tenant).
|
|
294
|
+
//
|
|
295
|
+
// The second half is that REPO_ROOT is not the instance either (task 1004123). It
|
|
296
|
+
// names the copy of the core that is EXECUTING, which coincides with the instance
|
|
297
|
+
// only while the core is a checkout run in place. Once the control plane's runner
|
|
298
|
+
// moved into its vendored core the two diverged silently — REPO_ROOT still resolved,
|
|
299
|
+
// just to node_modules/@bongos/core — and the door aimed at the package instead of
|
|
300
|
+
// the project. So ask, rather than assume; in the legacy shape the answer is
|
|
301
|
+
// REPO_ROOT and the command below is byte-identical to what it always was.
|
|
302
|
+
const instanceDir = inst.hosting_shape === 'standalone' ? standaloneRoot(inst) : selfRoot();
|
|
277
303
|
// The control plane is not a provisioned tenant and has no `bongos-` account; every
|
|
278
304
|
// other shape runs as its own, which is also its Postgres login role (task 1004119).
|
|
279
305
|
const runAs = inst.hosting_shape === 'control-plane' ? APP_USER : instanceUser(inst);
|
|
280
306
|
const sudoP = privileged ? `sudo -u ${runAs} env PGDATABASE=${dbName(inst)} ` : '';
|
|
281
307
|
const cli = path.posix.join('scripts', 'gds', 'upgrade.js');
|
|
282
308
|
const shared = [`${sudoP}node ${cli}`, '--registry', `--to ${to}`,
|
|
283
|
-
instanceDir === REPO_ROOT ? '' : `--instance ${instanceDir}`];
|
|
309
|
+
instanceDir === REPO_ROOT ? '' : `--instance ${instanceDir}`]; // REPO_ROOT is where the CLI runs (runDir below)
|
|
284
310
|
const tail = dryRun
|
|
285
311
|
? ['--dry-run']
|
|
286
312
|
: [`--service ${inst.slug}`,
|
|
@@ -297,7 +323,10 @@ function upgradeInvocation(inst, to, { privileged = false, dryRun = false, reque
|
|
|
297
323
|
function preflightUpgrade(inst, to, deps) {
|
|
298
324
|
const { exec, privileged } = deps;
|
|
299
325
|
const boxExec = inst.hosting_shape === 'standalone' ? (deps.controlExec || exec) : exec;
|
|
300
|
-
|
|
326
|
+
// A distinct local name: `deps.selfInstanceRoot || selfInstanceRoot` with a const of
|
|
327
|
+
// the same name would TDZ-shadow the import and crash every uninjected call.
|
|
328
|
+
const selfRoot = deps.selfInstanceRoot || selfInstanceRoot;
|
|
329
|
+
const { cmd, runDir } = upgradeInvocation(inst, to, { privileged, dryRun: true, selfRoot });
|
|
301
330
|
const r = boxExec(cmd, { cwd: runDir, allowFail: true });
|
|
302
331
|
const out = `${(r && r.stdout) || ''}\n${(r && r.stderr) || ''}`;
|
|
303
332
|
const { checks, error } = parsePreflight(out);
|
|
@@ -353,7 +382,8 @@ async function snapshotCoreVersions(inst, deps) {
|
|
|
353
382
|
if (!UPGRADE_SHAPES.has(inst.hosting_shape) || inst.status !== 'active') return { ok: true, skipped: true };
|
|
354
383
|
const boxExec = inst.hosting_shape === 'standalone' ? (deps.controlExec || exec) : exec;
|
|
355
384
|
const channelMod = deps.updateChannel || require('./update-channel.js');
|
|
356
|
-
const
|
|
385
|
+
const selfRoot = deps.selfInstanceRoot || selfInstanceRoot;
|
|
386
|
+
const instanceDir = inst.hosting_shape === 'standalone' ? standaloneRoot(inst) : selfRoot();
|
|
357
387
|
try {
|
|
358
388
|
const probe = boxExec(versionCmd(inst), { allowFail: true });
|
|
359
389
|
const served = parseServedVersion(probe && !probe.softFailed ? (probe.stdout || '') : null);
|
|
@@ -91,6 +91,31 @@ const CORE_PKG_DIR = 'node_modules/@bongos/core';
|
|
|
91
91
|
// one constant so the backup dir owner + the backup unit's User= can't drift from it.
|
|
92
92
|
const APP_USER = 'lars';
|
|
93
93
|
function standaloneRoot(inst, { base = CONFIG.standaloneBase } = {}) { return path.posix.join(base, inst.slug); }
|
|
94
|
+
// WHERE THE INSTANCE THIS CORE SERVES ACTUALLY LIVES (task 1004123).
|
|
95
|
+
//
|
|
96
|
+
// REPO_ROOT is `path.resolve(__dirname, '..', '..')` — it points at whatever copy of
|
|
97
|
+
// the core is EXECUTING, which is not the same place as the instance being upgraded.
|
|
98
|
+
// Two shapes, and the code had only ever met the first:
|
|
99
|
+
//
|
|
100
|
+
// • a core CHECKOUT run in place (`/home/lars/cloudbongos`) — the instance root IS
|
|
101
|
+
// the checkout, so REPO_ROOT was right by coincidence;
|
|
102
|
+
// • a VENDORED core (`<instance>/node_modules/@bongos/core`) — REPO_ROOT is the
|
|
103
|
+
// package, and the instance is three directories up.
|
|
104
|
+
//
|
|
105
|
+
// The live control plane became the second shape when its runner unit was re-homed
|
|
106
|
+
// (`WorkingDirectory=/home/lars/cloudbongos-instance`, ExecStart into the vendored
|
|
107
|
+
// core), and nothing noticed: REPO_ROOT still resolved, just to the wrong tree. So
|
|
108
|
+
// `readInstalledCoreVersion` answered null instead of 1.19.917, every guard that
|
|
109
|
+
// reads `installed` silently stopped guarding, and an upgrade would have pinned and
|
|
110
|
+
// npm-installed INSIDE the core package while the instance's own pin stayed put.
|
|
111
|
+
//
|
|
112
|
+
// This is a string question, not a filesystem one: the vendored layout is the only
|
|
113
|
+
// way a core ends up under `node_modules/`, so the suffix IS the answer. Keeping it
|
|
114
|
+
// pure means the control-plane path is testable without a box.
|
|
115
|
+
function selfInstanceRoot(repoRoot = REPO_ROOT) {
|
|
116
|
+
const suffix = path.sep + CORE_PKG_DIR.split('/').join(path.sep);
|
|
117
|
+
return repoRoot.endsWith(suffix) ? repoRoot.slice(0, -suffix.length) : repoRoot;
|
|
118
|
+
}
|
|
94
119
|
// Sync the instance's own repo (host content + the core pin). Mirrors deploy-instance.sh
|
|
95
120
|
// step 1 — assumes the repo is already cloned at standaloneRoot (initial clone is a
|
|
96
121
|
// precondition, like the runbook's `git pull`). Run with cwd = standaloneRoot.
|
|
@@ -1107,6 +1132,7 @@ module.exports = {
|
|
|
1107
1132
|
safeVersionLabel,
|
|
1108
1133
|
scaffoldStandaloneRepo,
|
|
1109
1134
|
seedFirstVersionCmd,
|
|
1135
|
+
selfInstanceRoot,
|
|
1110
1136
|
standaloneInstallCmd,
|
|
1111
1137
|
standaloneMigrateCmd,
|
|
1112
1138
|
standalonePullCmd,
|
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.925'; // 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');
|
|
@@ -36,6 +36,7 @@ const R = require('../scripts/gds/provision-core-upgrade.js');
|
|
|
36
36
|
const U = require('../scripts/gds/upgrade.js');
|
|
37
37
|
const P = require('../modules/provisioning/provisioning.js');
|
|
38
38
|
const G = require('../modules/provisioning/control-plane-guard.js');
|
|
39
|
+
const RP = require('../scripts/gds/provision-repo.js');
|
|
39
40
|
|
|
40
41
|
let passed = 0;
|
|
41
42
|
let failed = 0;
|
|
@@ -553,5 +554,98 @@ await t('a preview of nothing is refused rather than cached as a pass', async ()
|
|
|
553
554
|
assert.equal(d.queries.filter((q) => /INSERT INTO provisioning_core_version_snapshot/.test(q.sql)).length, 0);
|
|
554
555
|
});
|
|
555
556
|
|
|
557
|
+
console.log('\nwhere the instance actually is (task 1004123):');
|
|
558
|
+
|
|
559
|
+
// The vendored layout the live control plane runs in: the core is a PACKAGE inside the
|
|
560
|
+
// instance, so the directory the core executes from is three levels below the directory
|
|
561
|
+
// that must be upgraded.
|
|
562
|
+
const VENDORED = '/home/lars/cloudbongos-instance/node_modules/@bongos/core';
|
|
563
|
+
const INSTANCE = '/home/lars/cloudbongos-instance';
|
|
564
|
+
const LEGACY = '/home/lars/cloudbongos';
|
|
565
|
+
|
|
566
|
+
await t('selfInstanceRoot walks out of a vendored core, and leaves a plain checkout alone', () => {
|
|
567
|
+
assert.equal(RP.selfInstanceRoot(VENDORED), INSTANCE, 'the package is not the instance');
|
|
568
|
+
assert.equal(RP.selfInstanceRoot(LEGACY), LEGACY, 'a checkout run in place IS the instance');
|
|
569
|
+
});
|
|
570
|
+
|
|
571
|
+
await t('a path that merely CONTAINS the package name is not mistaken for one', () => {
|
|
572
|
+
// Guarding the string test itself: only a trailing segment means "I am the vendored
|
|
573
|
+
// core", and an instance that happens to live under a similarly-named directory must
|
|
574
|
+
// not be truncated into nonsense.
|
|
575
|
+
const decoy = '/srv/node_modules/@bongos/core-tools';
|
|
576
|
+
assert.equal(RP.selfInstanceRoot(decoy), decoy);
|
|
577
|
+
assert.equal(RP.selfInstanceRoot('/srv/node_modules/@bongos/core/sub'), '/srv/node_modules/@bongos/core/sub');
|
|
578
|
+
});
|
|
579
|
+
|
|
580
|
+
await t('the runner reads the installed version from the INSTANCE, not from the core package', async () => {
|
|
581
|
+
// The defect this replaces: readInstalledCoreVersion was handed REPO_ROOT, which in
|
|
582
|
+
// the vendored shape holds no node_modules/@bongos/core at all — so it answered null
|
|
583
|
+
// on the live control plane for as long as the shape has existed.
|
|
584
|
+
const asked = [];
|
|
585
|
+
const d = { ...makeDeps({ served: '1.19.762' }), selfInstanceRoot: () => INSTANCE };
|
|
586
|
+
d.readInstalledCoreVersion = (dir) => { asked.push(dir); return '1.19.760'; };
|
|
587
|
+
await R.coreUpgradeInstance(inst({ hosting_shape: 'control-plane', slug: 'cloudbongos', port: 3002 }), d, intent());
|
|
588
|
+
assert.deepEqual(asked, [INSTANCE], 'asked the instance root exactly once');
|
|
589
|
+
});
|
|
590
|
+
|
|
591
|
+
await t('the CLI runs where it lives and NAMES the instance it is aimed at', async () => {
|
|
592
|
+
const d = { ...makeDeps({ served: '1.19.762' }), selfInstanceRoot: () => INSTANCE };
|
|
593
|
+
await R.coreUpgradeInstance(inst({ hosting_shape: 'control-plane', slug: 'cloudbongos', port: 3002 }), d, intent());
|
|
594
|
+
const cmd = upgradeCall(d);
|
|
595
|
+
assert.ok(cmd.includes(`--instance ${INSTANCE} `),
|
|
596
|
+
`without this the upgrade pins and installs inside the core package and the real pin never moves — got: ${cmd}`);
|
|
597
|
+
});
|
|
598
|
+
|
|
599
|
+
await t('the legacy shape still omits --instance, because there is nothing to disambiguate', async () => {
|
|
600
|
+
const d = { ...makeDeps({ served: '1.19.762' }), selfInstanceRoot: () => require('../scripts/gds/provision-config.js').REPO_ROOT };
|
|
601
|
+
await R.coreUpgradeInstance(inst({ hosting_shape: 'control-plane', slug: 'cloudbongos', port: 3002 }), d, intent());
|
|
602
|
+
assert.doesNotMatch(upgradeCall(d), /--instance /, 'a checkout run in place is its own instance');
|
|
603
|
+
});
|
|
604
|
+
|
|
605
|
+
await t('the version snapshot asks the registry from the instance, not from inside the package', async () => {
|
|
606
|
+
// Weakest of the three sites — `recommended` is derived from the box's own /version,
|
|
607
|
+
// so aiming this one wrongly changes no answer today. It is guarded anyway: npm reads
|
|
608
|
+
// its configuration from the directory it runs in, and an unguarded line is a line
|
|
609
|
+
// that regresses quietly. Found by mutating each of the three sites in turn; this was
|
|
610
|
+
// the one no test noticed.
|
|
611
|
+
const cwds = [];
|
|
612
|
+
const d = makeDeps({ served: '1.19.760' });
|
|
613
|
+
d.selfInstanceRoot = () => INSTANCE;
|
|
614
|
+
d.updateChannel = { ...d.updateChannel, listAvailableVersions: ({ cwd }) => { cwds.push(cwd); return { ok: true, versions: ['1.19.760', '1.19.762'] }; } };
|
|
615
|
+
await R.snapshotCoreVersions(inst({ hosting_shape: 'control-plane', slug: 'cloudbongos', port: 3002 }), d);
|
|
616
|
+
assert.deepEqual(cwds, [INSTANCE]);
|
|
617
|
+
});
|
|
618
|
+
|
|
619
|
+
await t('the PREVIEW aims at the instance too — a dry run of the wrong directory is the lie', async () => {
|
|
620
|
+
// This is the shape that made the defect dangerous rather than merely broken: dry-run
|
|
621
|
+
// against the core package and upgrade.js reports `(none) → <target>` with green
|
|
622
|
+
// pre-flight ticks, so the owner is shown a clean bill of health for a run that will
|
|
623
|
+
// restart production and roll back. Both legs share one builder; both must aim.
|
|
624
|
+
const box = { id: 41, slug: 'cloudbongos', port: 3002, hosting_shape: 'control-plane', db_name: 'cloudbongos' };
|
|
625
|
+
const selfRoot = () => INSTANCE;
|
|
626
|
+
const move = R.upgradeInvocation(box, '1.19.922', { privileged: true, selfRoot });
|
|
627
|
+
const peek = R.upgradeInvocation(box, '1.19.922', { privileged: true, dryRun: true, selfRoot });
|
|
628
|
+
assert.ok(peek.cmd.includes(`--instance ${INSTANCE} `), `the preview names its target — got: ${peek.cmd}`);
|
|
629
|
+
assert.equal(move.instanceDir, peek.instanceDir, 'and it is the same target the move would take');
|
|
630
|
+
assert.equal(peek.instanceDir, INSTANCE);
|
|
631
|
+
});
|
|
632
|
+
|
|
633
|
+
await t('an unreadable installed version is a REFUSAL, not a skipped channel check', () => {
|
|
634
|
+
// Fail-closed. This is the guard that would have caught the directory bug on the
|
|
635
|
+
// first press instead of after four live restarts.
|
|
636
|
+
const f = R.targetFault('1.19.760', ['1.19.758', '1.19.760'], null, 'patch', () => true);
|
|
637
|
+
assert.match(f, /could not read the core version installed/);
|
|
638
|
+
assert.match(f, /cannot be bounded by the 'patch' channel/);
|
|
639
|
+
});
|
|
640
|
+
|
|
641
|
+
await t('the runner refuses, and runs NOTHING, when it cannot see where the instance is', async () => {
|
|
642
|
+
const d = makeDeps({ served: '1.19.762' });
|
|
643
|
+
d.readInstalledCoreVersion = () => null;
|
|
644
|
+
const r = await R.coreUpgradeInstance(inst({ hosting_shape: 'control-plane', slug: 'cloudbongos', port: 3002 }), d, intent());
|
|
645
|
+
assert.equal(r.ok, false);
|
|
646
|
+
assert.match(r.error, /could not read the core version installed/);
|
|
647
|
+
assert.equal(d.ranWith.filter((c) => /upgrade\.js/.test(c.cmd)).length, 0, 'the box was not touched');
|
|
648
|
+
});
|
|
649
|
+
|
|
556
650
|
console.log(`\ncore_upgrade_runner: ${passed} passed, ${failed} failed`);
|
|
557
651
|
if (failed) process.exit(1);
|