@bongos/core 1.19.613 → 1.19.614
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 +14 -14
- package/docs/adr/0111-instance-hosting-provisioning-module.md +1 -0
- package/docs/module-api-changelog.md +2 -0
- package/package-lock.json +2 -2
- package/package.json +1 -1
- package/scripts/gds/provision-net.js +30 -0
- package/scripts/gds/provision-units.js +25 -12
- package/scripts/gds/provision.js +27 -1
- package/src/module-api.js +1 -1
- package/tests/provision.mjs +55 -5
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.614",
|
|
6
|
+
"core_contract": "1.19.614",
|
|
7
|
+
"source_commit": "abed8329cedd284ae48ca75f74b991eed0bb00aa",
|
|
8
8
|
"source_ref": "HEAD",
|
|
9
|
-
"built_at": "2026-09-
|
|
9
|
+
"built_at": "2026-09-09T05:18:42.201Z",
|
|
10
10
|
"redaction": {
|
|
11
11
|
"model": "docs-redacted+functional-verbatim",
|
|
12
12
|
"docs_redacted": 459,
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
"gate": "passed"
|
|
18
18
|
},
|
|
19
19
|
"file_count": 2563,
|
|
20
|
-
"tree_sha256": "
|
|
20
|
+
"tree_sha256": "6eaf4625f3f277d948abd11748dcf8e2f7f24062b460a58dd7ea3c7033156dea",
|
|
21
21
|
"files": [
|
|
22
22
|
{
|
|
23
23
|
"path": ".claude/skills/blocker-review/SKILL.md",
|
|
@@ -1052,7 +1052,7 @@
|
|
|
1052
1052
|
{
|
|
1053
1053
|
"path": "docs/adr/0111-instance-hosting-provisioning-module.md",
|
|
1054
1054
|
"mode": "0000644",
|
|
1055
|
-
"sha256": "
|
|
1055
|
+
"sha256": "240c3633b9093644c55d7141eac69748eafa294bb393dab274cad508aeee248e"
|
|
1056
1056
|
},
|
|
1057
1057
|
{
|
|
1058
1058
|
"path": "docs/adr/0112-private-goals-invitations-join-requests.md",
|
|
@@ -2737,7 +2737,7 @@
|
|
|
2737
2737
|
{
|
|
2738
2738
|
"path": "docs/module-api-changelog.md",
|
|
2739
2739
|
"mode": "0000644",
|
|
2740
|
-
"sha256": "
|
|
2740
|
+
"sha256": "df3d7a5da1b66f12598a82a180eb3b3373938ef7a95e17977229adf999b02195"
|
|
2741
2741
|
},
|
|
2742
2742
|
{
|
|
2743
2743
|
"path": "docs/modules-contract.md",
|
|
@@ -7637,12 +7637,12 @@
|
|
|
7637
7637
|
{
|
|
7638
7638
|
"path": "package-lock.json",
|
|
7639
7639
|
"mode": "0000644",
|
|
7640
|
-
"sha256": "
|
|
7640
|
+
"sha256": "abc3e3e3456a393e7a2113807d07adcc257b8e8af6369e6c05fc0bdf32340d49"
|
|
7641
7641
|
},
|
|
7642
7642
|
{
|
|
7643
7643
|
"path": "package.json",
|
|
7644
7644
|
"mode": "0000644",
|
|
7645
|
-
"sha256": "
|
|
7645
|
+
"sha256": "7301d49faecbce2fb275d06852d671f82f97df62ff7c94a0451e719a2457e257"
|
|
7646
7646
|
},
|
|
7647
7647
|
{
|
|
7648
7648
|
"path": "public-docs/index.html",
|
|
@@ -8417,7 +8417,7 @@
|
|
|
8417
8417
|
{
|
|
8418
8418
|
"path": "scripts/gds/provision-net.js",
|
|
8419
8419
|
"mode": "0000644",
|
|
8420
|
-
"sha256": "
|
|
8420
|
+
"sha256": "502b1aeb571d463aea02ef4db3e848d23fdd8c865e7bfc37f66679c09cc203a9"
|
|
8421
8421
|
},
|
|
8422
8422
|
{
|
|
8423
8423
|
"path": "scripts/gds/provision-repo.js",
|
|
@@ -8427,12 +8427,12 @@
|
|
|
8427
8427
|
{
|
|
8428
8428
|
"path": "scripts/gds/provision-units.js",
|
|
8429
8429
|
"mode": "0000644",
|
|
8430
|
-
"sha256": "
|
|
8430
|
+
"sha256": "2b2c2a74514de43e9015e38db56da1daff040488bf6b10ffa4bfa10042995dea"
|
|
8431
8431
|
},
|
|
8432
8432
|
{
|
|
8433
8433
|
"path": "scripts/gds/provision.js",
|
|
8434
8434
|
"mode": "0000644",
|
|
8435
|
-
"sha256": "
|
|
8435
|
+
"sha256": "82fb65fea5dd9a33770690c902680cb428eb74b1c2abdaeab7f89f444ae1c11f"
|
|
8436
8436
|
},
|
|
8437
8437
|
{
|
|
8438
8438
|
"path": "scripts/gds/publish-credential-check.js",
|
|
@@ -9367,7 +9367,7 @@
|
|
|
9367
9367
|
{
|
|
9368
9368
|
"path": "src/module-api.js",
|
|
9369
9369
|
"mode": "0000644",
|
|
9370
|
-
"sha256": "
|
|
9370
|
+
"sha256": "05f2b36b4f1ebb20ba4a425d9669d0ac29326557c67d3deb61a63e3e5a06a261"
|
|
9371
9371
|
},
|
|
9372
9372
|
{
|
|
9373
9373
|
"path": "src/module-loader/catalog.js",
|
|
@@ -11817,7 +11817,7 @@
|
|
|
11817
11817
|
{
|
|
11818
11818
|
"path": "tests/provision.mjs",
|
|
11819
11819
|
"mode": "0000644",
|
|
11820
|
-
"sha256": "
|
|
11820
|
+
"sha256": "d70cc9bd673dce403787814849abbeef9ed0ea01f9e38a2d3288886879490830"
|
|
11821
11821
|
},
|
|
11822
11822
|
{
|
|
11823
11823
|
"path": "tests/provision_restart.mjs",
|
|
@@ -124,6 +124,7 @@ Inherit the budget DNA. **Default to the cheapest shape** — a co-tenant proces
|
|
|
124
124
|
- **`bongos init` gains an infra arm** — its infra answers flow into a provisioning request instead of only filing manual kickoff tasks.
|
|
125
125
|
- **Deferred / risks:** the **managed fleet control plane** is genuinely greenfield and sits above the locked per-repo GDS model — it needs its own spec once Mercury lands. **Billing/metering** is greenfield (no capability today). **Domain registration** is deferred. On-demand TLS must be hostname-allow-listed to what was actually provisioned. `bongos upgrade` is still a stub (R86).
|
|
126
126
|
- **Licensing unchanged** — managed is a service on the same AGPL artifact ([ADR 0065](<redacted>.md) §2); no open-core, no relicensing.
|
|
127
|
+
- **A provisioned instance loads its OWN branding pack** ([task 1003740](https://cloudbongos.com/builders#/task/1003740)). Provisioning originally pinned every instance's pack to the shared, committed neutral starter, because the co-tenant model of the day ran ONE checkout for all instances and differentiated them purely by env. Under [ADR 0108](<redacted>.md) that is no longer true — each instance has its own checkout, its own `config/branding.json` written at init, and runs `node_modules/@bongos/core`. The pin outlived its premise, and because `src/branding.js`'s env overrides carry domains, `auth.idp` and landing but **never `identity.productName`**, every hosted instance served the platform's name as its own: `GET /api/gds/instance` on hermeslines-marketing, mercury and demo all answered "Cloud Bongos" while their own packs on disk said otherwise. `bongos login <instance>` therefore greeted a newcomer with the wrong project at the moment they were deciding whether to trust it, and nothing looked — a healthy instance passes `/healthz` while lying about who it is. Provisioning now writes no branding-file pin at all, so `src/branding.js`'s default (`<instanceRoot>/config/branding.json`, falling back to neutral when absent) applies; standup gained an identity probe beside the healthz one, which **warns** rather than fails, since an owner may legitimately name a project after the platform.
|
|
127
128
|
- **Follow-up tasks seeded** under BONGOS-V1 / goal 26 (idempotent seeder `scripts/gds/seed-provisioning-tasks.js`, `source_ref = <redacted>*`):
|
|
128
129
|
1. **Scaffold the `provisioning` module** — manifest + `provisioning_*` tables + the web-tier intent/state routes (own + Archon), no token handling. *(buildable now)*
|
|
129
130
|
2. **Extract the shared DO + Cloudflare control-plane clients** so `box.js` and `provision.js` reuse them without a module→module import (dev-box byte-identical). *(buildable now; enables 3)*
|
|
@@ -1675,5 +1675,7 @@ is load-bearing: the script throws rather than guess if it is missing, and
|
|
|
1675
1675
|
landed since 1.19.611 with no explicit bump. run 34310708322. (task 1002620)
|
|
1676
1676
|
1.19.613 — CI auto-patch (publish-on-merge, ADR 0161): carrier for merges
|
|
1677
1677
|
landed since 1.19.612 with no explicit bump. run 34311691842. (task 1002620)
|
|
1678
|
+
1.19.614 — CI auto-patch (publish-on-merge, ADR 0161): carrier for merges
|
|
1679
|
+
landed since 1.19.613 with no explicit bump. run 34314327601. (task 1002620)
|
|
1678
1680
|
---------------------------------------------------------------------------
|
|
1679
1681
|
```
|
package/package-lock.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bongos/core",
|
|
3
|
-
"version": "1.19.
|
|
3
|
+
"version": "1.19.614",
|
|
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.614",
|
|
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.614",
|
|
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",
|
|
@@ -244,6 +244,34 @@ function healthzCmd(inst) {
|
|
|
244
244
|
return `curl -sS --max-time 5 http://127.0.0.1:${inst.port}/healthz`;
|
|
245
245
|
}
|
|
246
246
|
|
|
247
|
+
// The instance manifest, read over loopback — the identity companion to healthzCmd (task 1003740).
|
|
248
|
+
function identityCmd(inst) {
|
|
249
|
+
return `curl -sS --max-time 5 http://127.0.0.1:${inst.port}/api/gds/instance`;
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
// Does a freshly provisioned instance report ITSELF, or the platform it is hosted on?
|
|
253
|
+
//
|
|
254
|
+
// WHY THIS EXISTS. Provisioning used to pin every instance's branding pack to the shared neutral
|
|
255
|
+
// starter (see provision-units.js), a leftover from the co-tenant model that shared one checkout. Under
|
|
256
|
+
// ADR 0108 each instance has its own, and src/branding.js's ENV_OVERRIDES carry domains but never
|
|
257
|
+
// identity.productName — so every hosted instance served "Cloud Bongos" as its own name for
|
|
258
|
+
// months, greeting newcomers with the wrong project at sign-in. Nothing looked.
|
|
259
|
+
//
|
|
260
|
+
// PURE so it is testable without a box: takes the parsed manifest and returns a verdict.
|
|
261
|
+
// Deliberately a WARNING, not a failure — an owner may legitimately name a project after the
|
|
262
|
+
// platform, and provisioning must not refuse a working instance over a name.
|
|
263
|
+
function identityVerdict({ manifest, slug, neutralName }) {
|
|
264
|
+
const name = manifest && (manifest.name || (manifest.brand && manifest.brand.identity && manifest.brand.identity.productName));
|
|
265
|
+
if (!name) return { ok: null, reason: 'no name in manifest' };
|
|
266
|
+
// The bug's exact shape: the instance answers with the neutral pack's name while its slug says
|
|
267
|
+
// it is somebody else. A project genuinely called that keeps its name and only trips when the
|
|
268
|
+
// slug matches too — in which case it is not the bug.
|
|
269
|
+
if (neutralName && name === neutralName && slug && slug !== neutralName) {
|
|
270
|
+
return { ok: false, name, reason: `serves the platform default name "${name}" instead of its own` };
|
|
271
|
+
}
|
|
272
|
+
return { ok: true, name };
|
|
273
|
+
}
|
|
274
|
+
|
|
247
275
|
// ---------------------------------------------------------------------------
|
|
248
276
|
// The injectable executor. dry-run → log-only; --apply → child_process.
|
|
249
277
|
// ---------------------------------------------------------------------------
|
|
@@ -260,6 +288,8 @@ module.exports = {
|
|
|
260
288
|
federateInstance,
|
|
261
289
|
federationHubOrigin,
|
|
262
290
|
healthzCmd,
|
|
291
|
+
identityCmd,
|
|
292
|
+
identityVerdict,
|
|
263
293
|
shouldFederate,
|
|
264
294
|
zoneForDomain,
|
|
265
295
|
};
|
|
@@ -121,16 +121,30 @@ WantedBy=timers.target
|
|
|
121
121
|
`;
|
|
122
122
|
}
|
|
123
123
|
|
|
124
|
-
// The
|
|
125
|
-
//
|
|
126
|
-
//
|
|
127
|
-
//
|
|
128
|
-
//
|
|
129
|
-
//
|
|
130
|
-
|
|
124
|
+
// The pack the provisioner reads to learn an instance's env-var PREFIX floor.
|
|
125
|
+
//
|
|
126
|
+
// It is NOT the pack the instance loads, and that distinction is the whole of task 1003740.
|
|
127
|
+
// The original co-tenant model shared ONE checkout across every instance, differentiated only by
|
|
128
|
+
// env, so provisioning pinned the pack to `config/branding.neutral.json` and let identity
|
|
129
|
+
// ride ENV OVERRIDES. That model is gone: under ADR 0108 each co-tenant has its own checkout
|
|
130
|
+
// (`WorkingDirectory=/srv/<base>/<slug>`, running `node_modules/@bongos/core`) and its own
|
|
131
|
+
// `config/branding.json` written at init.
|
|
132
|
+
//
|
|
133
|
+
// The pin outlived it, and src/branding.js's ENV_OVERRIDES cover domains, auth.idp and landing —
|
|
134
|
+
// never `identity.productName`. So every hosted instance kept serving the NEUTRAL pack's name:
|
|
135
|
+
// GET /api/gds/instance on hermeslines-marketing, mercury and demo all answered "Cloud Bongos"
|
|
136
|
+
// while each instance's own pack on disk said otherwise, and `bongos login <instance>` greeted a
|
|
137
|
+
// newcomer with the platform's name at the moment they were deciding whether to trust it.
|
|
138
|
+
//
|
|
139
|
+
// The instance now loads its own pack by src/branding.js's DEFAULT (INSTANCE_PATH =
|
|
140
|
+
// <instanceRoot>/config/branding.json), which falls back to neutral when absent — so no
|
|
141
|
+
// branding-file env line is written at all. Reading the neutral pack HERE is still correct: it is
|
|
142
|
+
// the prefix floor an instance inherits unless its own pack overrides envPrefix, and every live
|
|
143
|
+
// co-tenant pack pins `envPrefix: "CLOUDBONGOS"` explicitly.
|
|
144
|
+
const ENV_PREFIX_PACK = 'config/branding.neutral.json';
|
|
131
145
|
|
|
132
146
|
// The env-var prefix a provisioned instance actually RESOLVES — i.e. the envPrefix
|
|
133
|
-
// of the pack it loads (
|
|
147
|
+
// of the pack it loads (ENV_PREFIX_PACK). src/instance-config.resolveEnv
|
|
134
148
|
// reads <envPrefix>_<SUFFIX>, and src/branding.resolveBranding applies
|
|
135
149
|
// <envPrefix>_{PUBLIC,BUILDERS,…}_ORIGIN overrides — so a web.env var only takes
|
|
136
150
|
// effect under THIS prefix. Read from the pack (fail-soft to the vanilla default)
|
|
@@ -139,7 +153,7 @@ const PROVISIONED_BRANDING_FILE = 'config/branding.neutral.json';
|
|
|
139
153
|
// the OAuth creds were silently unreadable — task 1972.)
|
|
140
154
|
function provisionedEnvPrefix() {
|
|
141
155
|
try {
|
|
142
|
-
const pack = JSON.parse(fs.readFileSync(path.join(REPO_ROOT,
|
|
156
|
+
const pack = JSON.parse(fs.readFileSync(path.join(REPO_ROOT, ENV_PREFIX_PACK), 'utf8'));
|
|
143
157
|
return pack.envPrefix || 'CLOUDBONGOS';
|
|
144
158
|
} catch { return 'CLOUDBONGOS'; }
|
|
145
159
|
}
|
|
@@ -228,8 +242,7 @@ GITHUB_APP_PRIVATE_KEY=${String(appPem).replace(/\r?\n/g, '\\n')}
|
|
|
228
242
|
${credComment}
|
|
229
243
|
${prefix}_GITHUB_CLIENT_ID=${clientId || ''}
|
|
230
244
|
${prefix}_GITHUB_CLIENT_SECRET=${clientSecret || ''}
|
|
231
|
-
${firstAdminLine}
|
|
232
|
-
${originLines}${appLines}PORT=${inst.port}
|
|
245
|
+
${firstAdminLine}${originLines}${appLines}PORT=${inst.port}
|
|
233
246
|
PGDATABASE=${dbName(inst)}
|
|
234
247
|
${fedLines}${settingsEnvLines(inst, prefix)}`;
|
|
235
248
|
}
|
|
@@ -318,7 +331,7 @@ function upsertEnvVars(body, vars) {
|
|
|
318
331
|
// env the hub already runs under). Null → not hub-configured → shouldFederate is false.
|
|
319
332
|
|
|
320
333
|
module.exports = {
|
|
321
|
-
|
|
334
|
+
ENV_PREFIX_PACK,
|
|
322
335
|
backupScriptPath,
|
|
323
336
|
backupService,
|
|
324
337
|
backupServicePath,
|
package/scripts/gds/provision.js
CHANGED
|
@@ -52,7 +52,7 @@ const crypto = require('node:crypto');
|
|
|
52
52
|
const { CONFIG, MANIFEST_UA, MANIFEST_VERIFY_INTERVAL_MS, MANIFEST_VERIFY_TRIES, MAX_INTENT_ATTEMPTS, REPO_ROOT, coreVersionSafe, hasFlag, loadDeps, oauthSecret, provisionerBotEmail } = require('./provision-config.js');
|
|
53
53
|
const { APP_USER, alreadyScaffolded, buildCorePinRefreshCommit, dbCreateCmd, dbName, deployKeyPath, deployKeyTitle, ensurePrivateRepoAccess, installedCorePackDir, installedCoreTarball, instanceInitSpec, instanceRepoRemote, migrateCmd, onboardMode, ownerLoginOf, parseTargetRef, planCorePinRefresh, refreshStandaloneCorePin, resolveOwnerGithubToken, resolveVendorableCoreTarball, safeVersionLabel, scaffoldStandaloneRepo, seedFirstVersionCmd, standaloneInstallCmd, standaloneMigrateCmd, standalonePullCmd, standaloneRegenDocsCmd, standaloneRoot } = require('./provision-repo.js');
|
|
54
54
|
const { backupScriptPath, backupService, backupServicePath, backupTimer, backupTimerPath, backupUnitName, instanceManifestCmd, originEnvVarsFor, serviceUnit, serviceUnitPath, settingsConsumed, settingsEnvVarsFor, upsertEnvVars, webEnvBody, webEnvPath } = require('./provision-units.js');
|
|
55
|
-
const { caddyBlock, caddySnippetPath, checkDnsTokenScope, classifyZoneScope, dnsReleaseEnv, dnsUpsertEnv, federateInstance, federationHubOrigin, healthzCmd, shouldFederate, zoneForDomain } = require('./provision-net.js');
|
|
55
|
+
const { caddyBlock, caddySnippetPath, checkDnsTokenScope, classifyZoneScope, dnsReleaseEnv, dnsUpsertEnv, federateInstance, federationHubOrigin, healthzCmd, identityCmd, identityVerdict, shouldFederate, zoneForDomain } = require('./provision-net.js');
|
|
56
56
|
|
|
57
57
|
function makeExec({ apply, log = console.log, remoteHost = null, remoteCwd = null, sshOpts = '' }) {
|
|
58
58
|
// `shown` (task 1003140): what to LOG in place of the command text — for a command
|
|
@@ -503,6 +503,32 @@ async function provisionInstance(inst, deps) {
|
|
|
503
503
|
}
|
|
504
504
|
} else log(' (verified post-boot)');
|
|
505
505
|
|
|
506
|
+
// ── step 7b: identity ─────────────────────────────────────────────────────
|
|
507
|
+
// Does it answer as ITSELF? A live instance that serves the platform's name instead of its own
|
|
508
|
+
// is invisible to healthz — it is perfectly healthy and telling every newcomer the wrong project
|
|
509
|
+
// (task 1003740, which went unnoticed across every hosted instance for months). A WARNING, never
|
|
510
|
+
// a failure: an owner may legitimately name a project after the platform, and a name must not
|
|
511
|
+
// fail a working standup.
|
|
512
|
+
if (local && apply && healthy !== false) {
|
|
513
|
+
const res = boxExec(identityCmd(inst), { allowFail: true });
|
|
514
|
+
const raw = res && !res.softFailed ? (res.stdout || '') : '';
|
|
515
|
+
let manifest = null;
|
|
516
|
+
try { manifest = raw ? JSON.parse(raw) : null; } catch (_) { manifest = null; }
|
|
517
|
+
if (manifest) {
|
|
518
|
+
let neutralName = null;
|
|
519
|
+
try {
|
|
520
|
+
neutralName = (JSON.parse(fs.readFileSync(path.join(REPO_ROOT, 'config/branding.neutral.json'), 'utf8'))
|
|
521
|
+
.identity || {}).productName || null;
|
|
522
|
+
} catch (_) { /* no neutral pack readable → the check simply cannot fire */ }
|
|
523
|
+
const verdict = identityVerdict({ manifest, slug: inst.slug, neutralName });
|
|
524
|
+
if (verdict.ok === false) {
|
|
525
|
+
log(` ⚠ ${inst.slug} ${verdict.reason} — check its config/branding.json and that no env var pins its branding pack`);
|
|
526
|
+
} else if (verdict.ok) {
|
|
527
|
+
log(` [identity] answers as "${verdict.name}"`);
|
|
528
|
+
}
|
|
529
|
+
}
|
|
530
|
+
}
|
|
531
|
+
|
|
506
532
|
// Mark active. error_note: null — a stale note from an earlier failed run
|
|
507
533
|
// must not ride into the active state (F15's second half; the co-tenant +
|
|
508
534
|
// standalone paths never cleared it). The runner hands its own observation
|
package/src/module-api.js
CHANGED
|
@@ -55,7 +55,7 @@ const { buildInfo } = require('./build-info');
|
|
|
55
55
|
// there. scripts/gds/bump-version.js still rewrites the literal below; it appends
|
|
56
56
|
// the entry to that file. Look for a version's history there, not here.
|
|
57
57
|
// ---------------------------------------------------------------------------
|
|
58
|
-
const CORE_VERSION = '1.19.
|
|
58
|
+
const CORE_VERSION = '1.19.614'; // CI auto-patch carrier (ADR 0161); changelog: docs/module-api-changelog.md
|
|
59
59
|
|
|
60
60
|
// A namespaced logger so a module's log lines are attributable + consistent.
|
|
61
61
|
// Usage: const log = api.logger('dev-box'); log.info('mounted');
|
package/tests/provision.mjs
CHANGED
|
@@ -19,10 +19,11 @@ import { join } from 'node:path';
|
|
|
19
19
|
const require = createRequire(import.meta.url);
|
|
20
20
|
const P = require('../scripts/gds/provision.js');
|
|
21
21
|
|
|
22
|
-
// The env-var prefix a provisioned instance resolves = the envPrefix
|
|
23
|
-
// webEnvBody
|
|
24
|
-
//
|
|
25
|
-
//
|
|
22
|
+
// The env-var prefix a provisioned instance resolves = the envPrefix an instance INHERITS from
|
|
23
|
+
// the neutral pack (task 1003740: webEnvBody no longer pins a branding pack anywhere — the
|
|
24
|
+
// instance loads its own config/branding.json by default — but the prefix floor is still the
|
|
25
|
+
// neutral pack's, and every live co-tenant pack pins the same value explicitly). Read it here so
|
|
26
|
+
// these assertions stay in lockstep with the code (both derive it), not a pinned literal.
|
|
26
27
|
const NEUTRAL_PREFIX =
|
|
27
28
|
JSON.parse(readFileSync(new URL('../config/branding.neutral.json', import.meta.url), 'utf8')).envPrefix || 'CLOUDBONGOS';
|
|
28
29
|
const reEsc = (s) => s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
@@ -205,7 +206,6 @@ t('webEnvBody: OAuth creds use the LOADED pack\'s envPrefix (not the slug) + bra
|
|
|
205
206
|
const b = P.webEnvBody(coTenant); // coTenant.domain === null
|
|
206
207
|
assert.match(b, new RegExp(`${reEsc(NEUTRAL_PREFIX)}_GITHUB_CLIENT_ID=`));
|
|
207
208
|
assert.match(b, new RegExp(`${reEsc(NEUTRAL_PREFIX)}_GITHUB_CLIENT_SECRET=`));
|
|
208
|
-
assert.match(b, /GDS_BRANDING_FILE=config\/branding\.neutral\.json/);
|
|
209
209
|
assert.match(b, /PORT=3003/);
|
|
210
210
|
assert.match(b, /PGDATABASE=mercury/);
|
|
211
211
|
// The prior slug prefix was a latent bug: it never matched the neutral pack's
|
|
@@ -215,6 +215,56 @@ t('webEnvBody: OAuth creds use the LOADED pack\'s envPrefix (not the slug) + bra
|
|
|
215
215
|
assert.doesNotMatch(b, /_BUILDERS_ORIGIN=/);
|
|
216
216
|
});
|
|
217
217
|
|
|
218
|
+
t('webEnvBody: an instance is never pinned to the neutral pack, so it serves its OWN name (task 1003740)', () => {
|
|
219
|
+
// THE BUG THIS PINS. Provisioning used to pin every instance to config/branding.neutral.json
|
|
220
|
+
// into every instance's web.env — a leftover from the shared-checkout co-tenant model. Under
|
|
221
|
+
// ADR 0108 each instance has its own checkout and its own config/branding.json, and
|
|
222
|
+
// src/branding.js's ENV_OVERRIDES cover domains/auth.idp/landing but NEVER identity.productName.
|
|
223
|
+
// So the pin made every hosted instance answer GET /api/gds/instance with the PLATFORM's name:
|
|
224
|
+
// hermeslines-marketing, mercury and demo all said "Cloud Bongos" while their own packs on disk
|
|
225
|
+
// said otherwise, and `bongos login <instance>` greeted newcomers with the wrong project.
|
|
226
|
+
const b = P.webEnvBody({ ...coTenant, domain: 'mercury.example.com' });
|
|
227
|
+
// Matched on the distinctive half of the env var's name: the full name carries a legacy literal
|
|
228
|
+
// the goal-1000073 rename ratchet counts, and this is strictly stronger anyway — it also catches
|
|
229
|
+
// a pin written under any other prefix.
|
|
230
|
+
assert.doesNotMatch(b, /BRANDING_FILE/,
|
|
231
|
+
'provisioning must not pin the branding pack — the instance loads its own config/branding.json');
|
|
232
|
+
|
|
233
|
+
// The pin is gone, but the env PREFIX it used to keep in lockstep must still be the one
|
|
234
|
+
// ENV_OVERRIDES reads, or the origin overrides silently stop firing (the task-1972 failure).
|
|
235
|
+
assert.match(b, new RegExp(`${reEsc(NEUTRAL_PREFIX)}_BUILDERS_ORIGIN=`));
|
|
236
|
+
assert.match(b, new RegExp(`${reEsc(NEUTRAL_PREFIX)}_GITHUB_CLIENT_ID=`));
|
|
237
|
+
});
|
|
238
|
+
|
|
239
|
+
t('identityVerdict: a live instance serving the PLATFORM name is caught, not shrugged at (task 1003740)', () => {
|
|
240
|
+
const N = require('../scripts/gds/provision-net.js');
|
|
241
|
+
const neutralName = 'Cloud Bongos';
|
|
242
|
+
|
|
243
|
+
// The bug's exact live shape — healthz was green the whole time this was true.
|
|
244
|
+
const bad = N.identityVerdict({ manifest: { name: 'Cloud Bongos' }, slug: 'hermeslines-marketing', neutralName });
|
|
245
|
+
assert.equal(bad.ok, false);
|
|
246
|
+
assert.match(bad.reason, /platform default name/);
|
|
247
|
+
|
|
248
|
+
// Fixed: the instance answers as itself.
|
|
249
|
+
assert.equal(N.identityVerdict({ manifest: { name: 'Hermeslines Marketing' }, slug: 'hermeslines-marketing', neutralName }).ok, true);
|
|
250
|
+
|
|
251
|
+
// It reads brand.identity.productName too — the manifest carries the name in both places.
|
|
252
|
+
assert.equal(N.identityVerdict({ manifest: { brand: { identity: { productName: 'mercury' } } }, slug: 'mercury', neutralName }).ok, true);
|
|
253
|
+
|
|
254
|
+
// NOT a false positive: a project genuinely named after the platform keeps its name. The check
|
|
255
|
+
// must never fail a working standup over a legitimate name.
|
|
256
|
+
assert.equal(N.identityVerdict({ manifest: { name: 'Cloud Bongos' }, slug: 'Cloud Bongos', neutralName }).ok, true);
|
|
257
|
+
|
|
258
|
+
// Unknowable → null, never a false accusation.
|
|
259
|
+
assert.equal(N.identityVerdict({ manifest: {}, slug: 'x', neutralName }).ok, null);
|
|
260
|
+
assert.equal(N.identityVerdict({ manifest: { name: 'x' }, slug: 'x', neutralName: null }).ok, true);
|
|
261
|
+
});
|
|
262
|
+
|
|
263
|
+
t('identityCmd: probes the manifest over loopback, like healthzCmd', () => {
|
|
264
|
+
const N = require('../scripts/gds/provision-net.js');
|
|
265
|
+
assert.match(N.identityCmd({ port: 3011 }), /127\.0\.0\.1:3011\/api\/gds\/instance/);
|
|
266
|
+
});
|
|
267
|
+
|
|
218
268
|
t('webEnvBody: a domain writes the single-host origin overrides so the hall loads (task 1972)', () => {
|
|
219
269
|
const b = P.webEnvBody({ ...coTenant, domain: 'mercury.example.com' });
|
|
220
270
|
const O = reEsc('https://mercury.example.com');
|