@bongos/core 1.17.3 → 1.18.0
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 +161 -91
- package/.devcontainer/healthcheck.sh +0 -0
- package/.devcontainer/init-firewall.sh +0 -0
- package/.devcontainer/post-create.sh +0 -0
- package/GOVERNANCE.md +1 -1
- package/README.md +26 -22
- package/bin/bongos.js +0 -0
- package/docker-entrypoint.sh +0 -0
- package/docs/adr/0139-platform-history-migration-to-cloudbongos.md +94 -0
- package/docs/adr/0140-cloudbongos-standalone-reprovision.md +43 -0
- package/docs/adr/README.md +3 -0
- package/docs/api/openapi.json +4 -345
- package/docs/api-reference.md +5 -17
- package/docs/file-map.md +7 -25
- package/docs/onboarding/diagrams/05-versioning.mmd +4 -6
- package/docs/onboarding/diagrams/assertions.json +5 -9
- package/modules/hall-ui/public/builders.js +37 -0
- package/modules/hall-ui/public/goals-page.js +98 -176
- package/modules/hall-ui/public/goals.css +183 -0
- package/modules/hall-ui/public/goals.html +35 -261
- package/modules/hall-ui/public/goals.js +7 -2
- package/modules/hall-ui/public/hall-widgets.js +12 -4
- package/modules/hall-ui/public/settings.css +355 -0
- package/modules/hall-ui/public/settings.html +86 -414
- package/modules/hall-ui/public/settings.js +96 -35
- package/modules/hall-ui/public/style.css +31 -0
- package/modules/hall-ui/public/watch.css +184 -0
- package/modules/hall-ui/public/watch.html +111 -285
- package/modules/hall-ui/public/watch.js +59 -21
- package/package-lock.json +2 -2
- package/package.json +1 -1
- package/public-landing/create.html +83 -7
- package/public-landing/index.html +11 -76
- package/scripts/cron/style_guide_audit.sh +0 -0
- package/scripts/gds/api.js +0 -0
- package/scripts/gds/architect-audit.js +0 -0
- package/scripts/gds/audit-authorship.sh +0 -0
- package/scripts/gds/audit-replay.sh +0 -0
- package/scripts/gds/box.js +0 -0
- package/scripts/gds/capture.js +0 -0
- package/scripts/gds/conductor-dispatch.js +0 -0
- package/scripts/gds/coreB-carve-proof.sh +0 -0
- package/scripts/gds/db-backup-nightly.sh +0 -0
- package/scripts/gds/db-backup-offsite.sh +0 -0
- package/scripts/gds/doctor.js +0 -0
- package/scripts/gds/grade-smoke.js +0 -0
- package/scripts/gds/hooks/pre-push.js +0 -0
- package/scripts/gds/install-git-hooks.js +0 -0
- package/scripts/gds/main-audit.js +0 -0
- package/scripts/gds/oauth-secret.js +0 -0
- package/scripts/gds/onboarding-analyze.js +0 -0
- package/scripts/gds/onboarding-ship.js +0 -0
- package/scripts/gds/paste-token.js +0 -0
- package/scripts/gds/portability-smoke-art.sh +0 -0
- package/scripts/gds/portability-smoke-memory.sh +0 -0
- package/scripts/gds/provision.js +125 -4
- package/scripts/gds/push-main.js +0 -0
- package/scripts/gds/render-ideas.js +0 -0
- package/scripts/gds/security-baseline-check.sh +0 -0
- package/scripts/gds/session.js +0 -0
- package/scripts/gds/ship.js +11 -1
- package/scripts/gds/smoke-batch-claim.sh +0 -0
- package/scripts/gds/smoke-batch-release.sh +0 -0
- package/scripts/gds/smoke-box.sh +0 -0
- package/scripts/gds/smoke-claim-consistency.sh +0 -0
- package/scripts/gds/smoke-dependencies.sh +0 -0
- package/scripts/gds/smoke-write.sh +0 -0
- package/scripts/hall-preview/fixtures/access-requests.json +20 -1
- package/scripts/hall-preview/fixtures/audit-log.json +68 -1
- package/scripts/hall-preview/fixtures/blockers__detail.json +12 -0
- package/scripts/hall-preview/fixtures/box__me.json +5 -0
- package/scripts/hall-preview/fixtures/boxes__cost-ledger.json +69 -1
- package/scripts/hall-preview/fixtures/builders__roster.json +48 -1
- package/scripts/hall-preview/fixtures/grades__by-builder.json +32 -1
- package/scripts/hall-preview/fixtures/inbox__detail.json +15 -0
- package/scripts/hall-preview/fixtures/me__art-key__own.json +6 -0
- package/scripts/hall-preview/fixtures/me__event-sound-prefs.json +209 -0
- package/scripts/hall-preview/fixtures/me__render.json +5 -0
- package/scripts/hall-preview/fixtures/me__skill-prefs.json +143 -0
- package/scripts/hall-preview/fixtures/me__sound-prefs.json +67 -0
- package/scripts/hall-preview/fixtures/me__wandering.json +43 -0
- package/scripts/hall-preview/fixtures/override-requests.json +15 -1
- package/scripts/hall-preview/fixtures/security__docs.json +10 -1
- package/scripts/hall-preview/fixtures/security__reports.json +31 -1
- package/scripts/hall-preview/fixtures/sessions__search.json +9 -1
- package/scripts/hall-preview/server.js +18 -1
- package/scripts/migrate.sh +0 -0
- package/scripts/migrate_cost_ledger.sh +0 -0
- package/scripts/render-diagrams.sh +0 -0
- package/scripts/security/dep-audit.js +0 -0
- package/scripts/setup-builder.js +0 -0
- package/scripts/setup-builder.sh +0 -0
- package/scripts/snapshots/maybe-render-on-deploy.sh +0 -0
- package/src/module-api.js +20 -1
- package/tests/provision.mjs +69 -0
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
{
|
|
2
|
+
"prefs": {},
|
|
3
|
+
"effective": [
|
|
4
|
+
{
|
|
5
|
+
"sound": "chime",
|
|
6
|
+
"label": "Chime",
|
|
7
|
+
"desc": "When Claude finishes a turn",
|
|
8
|
+
"default_enabled": true,
|
|
9
|
+
"override_enabled": null,
|
|
10
|
+
"enabled": true
|
|
11
|
+
},
|
|
12
|
+
{
|
|
13
|
+
"sound": "alert",
|
|
14
|
+
"label": "Alert",
|
|
15
|
+
"desc": "When Claude needs your input",
|
|
16
|
+
"default_enabled": true,
|
|
17
|
+
"override_enabled": null,
|
|
18
|
+
"enabled": true
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
"sound": "ship",
|
|
22
|
+
"label": "Ship",
|
|
23
|
+
"desc": "When a task ships",
|
|
24
|
+
"default_enabled": true,
|
|
25
|
+
"override_enabled": null,
|
|
26
|
+
"enabled": true
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
"sound": "triumph",
|
|
30
|
+
"label": "Triumph",
|
|
31
|
+
"desc": "When a big task ships",
|
|
32
|
+
"default_enabled": true,
|
|
33
|
+
"override_enabled": null,
|
|
34
|
+
"enabled": true
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
"sound": "fanfare",
|
|
38
|
+
"label": "Fanfare",
|
|
39
|
+
"desc": "When you earn a rank promotion",
|
|
40
|
+
"default_enabled": true,
|
|
41
|
+
"override_enabled": null,
|
|
42
|
+
"enabled": true
|
|
43
|
+
}
|
|
44
|
+
],
|
|
45
|
+
"effective_map": {
|
|
46
|
+
"chime": true,
|
|
47
|
+
"alert": true,
|
|
48
|
+
"ship": true,
|
|
49
|
+
"triumph": true,
|
|
50
|
+
"fanfare": true
|
|
51
|
+
},
|
|
52
|
+
"volume": 100,
|
|
53
|
+
"defaults": {
|
|
54
|
+
"chime": true,
|
|
55
|
+
"alert": true,
|
|
56
|
+
"ship": true,
|
|
57
|
+
"triumph": true,
|
|
58
|
+
"fanfare": true
|
|
59
|
+
},
|
|
60
|
+
"known_sounds": [
|
|
61
|
+
"chime",
|
|
62
|
+
"alert",
|
|
63
|
+
"ship",
|
|
64
|
+
"triumph",
|
|
65
|
+
"fanfare"
|
|
66
|
+
]
|
|
67
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
{
|
|
2
|
+
"rank": "metic",
|
|
3
|
+
"levels": [
|
|
4
|
+
{
|
|
5
|
+
"n": 0,
|
|
6
|
+
"key": "locked",
|
|
7
|
+
"label": "Locked",
|
|
8
|
+
"blurb": "Strictly the claimed task. Off-task notes are filed silently, never raised.",
|
|
9
|
+
"allowed": true
|
|
10
|
+
},
|
|
11
|
+
{
|
|
12
|
+
"n": 1,
|
|
13
|
+
"key": "focused",
|
|
14
|
+
"label": "Focused",
|
|
15
|
+
"blurb": "On task, full in-task initiative; at most one captured follow-up at the end.",
|
|
16
|
+
"allowed": true
|
|
17
|
+
},
|
|
18
|
+
{
|
|
19
|
+
"n": 2,
|
|
20
|
+
"key": "balanced",
|
|
21
|
+
"label": "Balanced",
|
|
22
|
+
"blurb": "May surface a high-value tangent mid-task as \"capture or pursue?\" \u2014 your call.",
|
|
23
|
+
"allowed": true
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
"n": 3,
|
|
27
|
+
"key": "exploratory",
|
|
28
|
+
"label": "Exploratory",
|
|
29
|
+
"blurb": "A thinking partner \u2014 actively surfaces adjacent work and investigations.",
|
|
30
|
+
"allowed": true
|
|
31
|
+
}
|
|
32
|
+
],
|
|
33
|
+
"default_level": 2,
|
|
34
|
+
"default_key": "balanced",
|
|
35
|
+
"max_level": 3,
|
|
36
|
+
"max_key": "exploratory",
|
|
37
|
+
"override_level": null,
|
|
38
|
+
"effective_level": 2,
|
|
39
|
+
"effective_key": "balanced",
|
|
40
|
+
"effective_label": "Balanced",
|
|
41
|
+
"contract": "Wandering: Balanced. Stay anchored to your claim, but if you hit a genuinely high-value or blocking tangent you may surface it briefly and ask \"capture or pursue?\" \u2014 leave the call to the user. Note anomalies as observations; don't rabbit-hole.",
|
|
42
|
+
"inject": false
|
|
43
|
+
}
|
|
@@ -1 +1,15 @@
|
|
|
1
|
-
{
|
|
1
|
+
{
|
|
2
|
+
"requests": [
|
|
3
|
+
{
|
|
4
|
+
"id": "12",
|
|
5
|
+
"task_id": "777",
|
|
6
|
+
"task_title": "Synthetic: grader bypass ask for preview",
|
|
7
|
+
"requester_display_name": "Preview Builder",
|
|
8
|
+
"requester_login": "preview-builder",
|
|
9
|
+
"rationale": "Grader outage window \u2014 synthetic row.",
|
|
10
|
+
"credits_reward": 40,
|
|
11
|
+
"created_at": "2026-07-10T12:00:00.000Z",
|
|
12
|
+
"status": "open"
|
|
13
|
+
}
|
|
14
|
+
]
|
|
15
|
+
}
|
|
@@ -1 +1,31 @@
|
|
|
1
|
-
{
|
|
1
|
+
{
|
|
2
|
+
"reports": [
|
|
3
|
+
{
|
|
4
|
+
"id": "31",
|
|
5
|
+
"severity": "high",
|
|
6
|
+
"title": "Synthetic: rate-limit missing on preview endpoint",
|
|
7
|
+
"target": "api/preview",
|
|
8
|
+
"status": "open"
|
|
9
|
+
},
|
|
10
|
+
{
|
|
11
|
+
"id": "32",
|
|
12
|
+
"severity": "medium",
|
|
13
|
+
"title": "Synthetic: verbose error leaks stack trace",
|
|
14
|
+
"target": "api/sample",
|
|
15
|
+
"status": "confirmed"
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
"id": "33",
|
|
19
|
+
"severity": "low",
|
|
20
|
+
"title": "Synthetic: cache header on static asset",
|
|
21
|
+
"target": "assets",
|
|
22
|
+
"status": "fixed"
|
|
23
|
+
}
|
|
24
|
+
],
|
|
25
|
+
"statuses": [
|
|
26
|
+
"open",
|
|
27
|
+
"confirmed",
|
|
28
|
+
"fixed",
|
|
29
|
+
"disputed"
|
|
30
|
+
]
|
|
31
|
+
}
|
|
@@ -19,6 +19,8 @@
|
|
|
19
19
|
// window.__MODULES__ (verifies moduleOn() gating)
|
|
20
20
|
// --empty serve every fixture with its arrays emptied — the
|
|
21
21
|
// empty-state screenshot pass
|
|
22
|
+
// --rank archon override the /me fixture's builder.rank so the Archon-gated
|
|
23
|
+
// pages (watch, harbor, gate) render their content in preview
|
|
22
24
|
//
|
|
23
25
|
// Browse http://builders.localhost:<port>/ — Chromium resolves *.localhost,
|
|
24
26
|
// and the `builders.` host prefix exercises the real subdomain serving path.
|
|
@@ -39,6 +41,11 @@ function arg(name) {
|
|
|
39
41
|
}
|
|
40
42
|
const PORT = Number(arg('--port')) || 4600;
|
|
41
43
|
const EMPTY = process.argv.includes('--empty');
|
|
44
|
+
// --rank: view the hall as a different rank — the /me fixture's builder.rank is
|
|
45
|
+
// overridden in the response, so the Archon-gated pages (watch, harbor, gate)
|
|
46
|
+
// render their real content instead of the sealed notice. Preview-only theatre:
|
|
47
|
+
// the real pages are server-gated (requireArchonPage / ADR 0016).
|
|
48
|
+
const RANK_OVERRIDE = arg('--rank');
|
|
42
49
|
const MODULES_OFF = String(arg('--modules-off') || '').split(',').map((s) => s.trim()).filter(Boolean);
|
|
43
50
|
// --modules-on: force-report modules as enabled — a dev box's local config
|
|
44
51
|
// often lacks prod's instance modules (provisioning, dev-box), and their
|
|
@@ -68,6 +75,10 @@ const FIXTURE_FALLBACKS = [
|
|
|
68
75
|
[/^goals\/\d+\/requests$/, 'goals__requests'],
|
|
69
76
|
[/^goals\/\d+$/, 'goals__detail'],
|
|
70
77
|
[/^tasks\/\d+$/, 'tasks__detail'],
|
|
78
|
+
// the #/idea/N + #/blocker/N deep-link overlay (task-detail.js) — one record
|
|
79
|
+
// fixture serves any id so the overlay seam assertion renders real content.
|
|
80
|
+
[/^inbox\/\d+$/, 'inbox__detail'],
|
|
81
|
+
[/^blockers\/\d+$/, 'blockers__detail'],
|
|
71
82
|
[/^analytics\/builder\/\d+$/, 'analytics__builder'],
|
|
72
83
|
[/^builders\/\d+\/active-claims$/, 'builders__active-claims'],
|
|
73
84
|
];
|
|
@@ -134,7 +145,13 @@ app.use((req, res, next) => {
|
|
|
134
145
|
console.log(` no fixture: ${apiPath} (add scripts/hall-preview/fixtures/${apiPath.replace(/\//g, '__')}.json)`);
|
|
135
146
|
return res.status(404).json({ error: { code: 'no_fixture', message: apiPath } });
|
|
136
147
|
}
|
|
137
|
-
|
|
148
|
+
let payload = EMPTY ? emptied(fix) : fix;
|
|
149
|
+
if (RANK_OVERRIDE && apiPath === 'me') {
|
|
150
|
+
payload = JSON.parse(JSON.stringify(payload));
|
|
151
|
+
if (payload.builder) payload.builder.rank = RANK_OVERRIDE;
|
|
152
|
+
else if ('rank' in payload) payload.rank = RANK_OVERRIDE;
|
|
153
|
+
}
|
|
154
|
+
res.json(payload);
|
|
138
155
|
});
|
|
139
156
|
|
|
140
157
|
// The hall pages, pre-auth-gate, through the real seams: boot-time stamped
|
package/scripts/migrate.sh
CHANGED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
package/scripts/setup-builder.js
CHANGED
|
File without changes
|
package/scripts/setup-builder.sh
CHANGED
|
File without changes
|
|
File without changes
|
package/src/module-api.js
CHANGED
|
@@ -199,8 +199,27 @@ const { validateOrRespond, LIMITS, parseId, asyncHandler, corsPublicGet, parsePa
|
|
|
199
199
|
// plus a THROWAWAY `updateCanary` marker removed in the phase-E follow-up. A
|
|
200
200
|
// backwards-compatible additive change with no API removal is exactly semver
|
|
201
201
|
// PATCH. Every `^1.x` / `^1.17` module pin is satisfied by 1.17.2.
|
|
202
|
+
// 1.17.3 — task 2183 (R89 cutover prereq): republish so the packaged core carries the
|
|
203
|
+
// F6 migrate.sh fix (task 2180). Post-split, `scripts/migrate.sh` cd'd into the
|
|
204
|
+
// installed @bongos/core package before resolving the instance root, so a literal
|
|
205
|
+
// `npm run migrate` read the core's VANILLA module config and SILENTLY skipped
|
|
206
|
+
// every host + flag-gated module migration (rehearsal: 115 rows, no game schema;
|
|
207
|
+
// from the instance cwd, 121). 2180 fixed the script to resolve INSTANCE_ROOT from
|
|
208
|
+
// the invocation cwd (INIT_CWD/$PWD → the instance's bongos.config.json) and added
|
|
209
|
+
// a DB-free `--print-roots` regression guard. Published 1.17.2 PREDATES 2180, so a
|
|
210
|
+
// consumer pinning 1.17.2 would still skip host migrations — hence a PATCH republish
|
|
211
|
+
// at the F6-inclusive main. A functional-file bug fix with no doorway export change
|
|
212
|
+
// is exactly semver PATCH. Every `^1.x` / `^1.17` module pin is satisfied by 1.17.3.
|
|
213
|
+
// 1.18.0 — task 2190 (R89 follow-up): first version cut from the cloud-bongos AUTHORITATIVE
|
|
214
|
+
// source repo. Re-seeds the standalone core (which had frozen at the 2026-07-03
|
|
215
|
+
// v1.14.0 extraction while core dev continued inside amazonprimea through 1.17.3)
|
|
216
|
+
// from amazonprimea's last-with-core commit 345494f2. Content is 1.17.3 PLUS the
|
|
217
|
+
// builders'-hall UI redesign (developed in amazonprimea as tasks 2171/2172/2173/2184) that
|
|
218
|
+
// post-dates the published 1.17.3 tarball — a new user-facing feature set with no
|
|
219
|
+
// doorway export removal, so semver MINOR. NOT yet published to npm (that is the
|
|
220
|
+
// follow-on release task); amazonprimea still runs the vendored 1.17.3.
|
|
202
221
|
// ---------------------------------------------------------------------------
|
|
203
|
-
const CORE_VERSION = '1.
|
|
222
|
+
const CORE_VERSION = '1.18.0'; // see the changelog above — 1.18.0 is the first cut from the cloud-bongos authoritative source (re-seed task 2190); includes the builders'-hall UI redesign beyond published 1.17.3
|
|
204
223
|
|
|
205
224
|
// A namespaced logger so a module's log lines are attributable + consistent.
|
|
206
225
|
// Usage: const log = api.logger('dev-box'); log.info('mounted');
|
package/tests/provision.mjs
CHANGED
|
@@ -100,6 +100,35 @@ t('standalone step generators: root under standaloneBase + pull / npm-ci / insta
|
|
|
100
100
|
assert.match(P.standaloneMigrateCmd(inst), /PGDATABASE=mercury MEDUSA_GDS_ONLY=1 bash node_modules\/@bongos\/core\/scripts\/migrate\.sh/);
|
|
101
101
|
});
|
|
102
102
|
|
|
103
|
+
// ── nightly DB backup units (task 2213 — productizes the manual 2210 fix) ──────
|
|
104
|
+
t('backup units: per-instance names + the DB set on the unit (fixes the task-2210 unset GDS_BACKUP_DB)', () => {
|
|
105
|
+
assert.equal(P.backupUnitName(coTenant), 'mercury-db-backup');
|
|
106
|
+
assert.equal(P.backupServicePath(coTenant), '/etc/systemd/system/mercury-db-backup.service');
|
|
107
|
+
assert.equal(P.backupTimerPath(coTenant), '/etc/systemd/system/mercury-db-backup.timer');
|
|
108
|
+
const svc = P.backupService(coTenant);
|
|
109
|
+
// the DB is pinned on the unit → no fallback to the nonexistent amazonprimea default
|
|
110
|
+
assert.match(svc, /Environment=GDS_BACKUP_DB=mercury/);
|
|
111
|
+
assert.match(svc, /Environment=GDS_BACKUP_DIR=\/var\/backups\/gds/);
|
|
112
|
+
assert.match(svc, new RegExp(`User=${reEsc('lars')}`), 'runs as the app user (mirrors serviceUnit)');
|
|
113
|
+
assert.match(svc, /ReadWritePaths=\/var\/backups\/gds/, 'the only writable path is the backup dir');
|
|
114
|
+
const timer = P.backupTimer(coTenant);
|
|
115
|
+
assert.match(timer, /OnCalendar=\*-\*-\* 04:00:00 UTC/);
|
|
116
|
+
assert.match(timer, /Unit=mercury-db-backup\.service/, 'the timer drives the per-instance service');
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
t('backupScriptPath resolves the CONSUMER LAYOUT, never the old /home/lars/amazonprimea monorepo path', () => {
|
|
120
|
+
// standalone → the script inside the instance's OWN pinned @bongos/core
|
|
121
|
+
const standalone = { ...coTenant, hosting_shape: 'standalone' };
|
|
122
|
+
const sPath = P.backupScriptPath(standalone);
|
|
123
|
+
assert.ok(sPath.startsWith(P.standaloneRoot(standalone) + '/'), 'under the instance root');
|
|
124
|
+
assert.match(sPath, /node_modules\/@bongos\/core\/scripts\/gds\/db-backup-nightly\.sh$/);
|
|
125
|
+
// co-tenant → this control-plane checkout's scripts/gds (mirrors serviceUnit's REPO_ROOT split)
|
|
126
|
+
assert.match(P.backupScriptPath(coTenant), /\/scripts\/gds\/db-backup-nightly\.sh$/);
|
|
127
|
+
// the regression this whole task fixes: NEVER the hardcoded old monorepo path
|
|
128
|
+
assert.doesNotMatch(P.backupService(standalone), /\/home\/lars\/amazonprimea/);
|
|
129
|
+
assert.doesNotMatch(P.backupService(coTenant), /\/home\/lars\/amazonprimea/);
|
|
130
|
+
});
|
|
131
|
+
|
|
103
132
|
t('webEnvBody: OAuth creds use the LOADED pack\'s envPrefix (not the slug) + brand/port/db', () => {
|
|
104
133
|
const b = P.webEnvBody(coTenant); // coTenant.domain === null
|
|
105
134
|
assert.match(b, new RegExp(`${reEsc(NEUTRAL_PREFIX)}_GITHUB_CLIENT_ID=`));
|
|
@@ -280,6 +309,27 @@ await ta('provisionInstance(co-tenant) composes db → migrate → service → h
|
|
|
280
309
|
assert.ok(writes.some((w) => /\/mercury\/web\.env$/.test(w)), 'wrote web.env');
|
|
281
310
|
assert.ok(writes.some((w) => /\/mercury\.service$/.test(w)), 'wrote systemd unit');
|
|
282
311
|
assert.ok(writes.some((w) => /\/mercury\.caddy$/.test(w)), 'wrote per-instance Caddy snippet');
|
|
312
|
+
// task 2213: EVERY instance is born with backups — dir + per-instance timer/service
|
|
313
|
+
assert.match(joined, /install -d -o lars -g lars -m 0750 \/var\/backups\/gds/, 'creates the backup dir');
|
|
314
|
+
assert.match(joined, /systemctl enable --now mercury-db-backup\.timer/, 'enables the per-instance backup timer');
|
|
315
|
+
assert.ok(writes.some((w) => /\/mercury-db-backup\.service$/.test(w)), 'wrote backup service unit');
|
|
316
|
+
assert.ok(writes.some((w) => /\/mercury-db-backup\.timer$/.test(w)), 'wrote backup timer unit');
|
|
317
|
+
// the backup timer is enabled AFTER the dir exists (else the first run exits 2)
|
|
318
|
+
assert.ok(idx(/install -d .*\/var\/backups\/gds/) < idx(/enable --now mercury-db-backup\.timer/), 'dir before timer enable');
|
|
319
|
+
});
|
|
320
|
+
|
|
321
|
+
await ta('provisionInstance(standalone): the backup unit ExecStart points at the instance\'s OWN pinned core (consumer layout)', async () => {
|
|
322
|
+
const { deps, writes } = recorder();
|
|
323
|
+
// capture the unit BODIES, not just paths, so we can assert the ExecStart path
|
|
324
|
+
const bodies = {};
|
|
325
|
+
deps.writeFile = (p, body) => { writes.push(p); bodies[p] = body; return { ok: true }; };
|
|
326
|
+
const inst = { ...coTenant, hosting_shape: 'standalone', target_ref: 'lars589/mercury' };
|
|
327
|
+
await P.provisionInstance(inst, deps);
|
|
328
|
+
const svc = bodies[P.backupServicePath(inst)];
|
|
329
|
+
assert.ok(svc, 'wrote the backup service unit');
|
|
330
|
+
const root = P.standaloneRoot(inst);
|
|
331
|
+
assert.match(svc, new RegExp('ExecStart=/bin/bash ' + reEsc(root) + '/node_modules/@bongos/core/scripts/gds/db-backup-nightly\\.sh'));
|
|
332
|
+
assert.match(svc, /Environment=GDS_BACKUP_DB=mercury/);
|
|
283
333
|
});
|
|
284
334
|
|
|
285
335
|
await ta('provisionInstance(standalone) SCAFFOLDS its repo, then syncs (pull + npm ci) + migrates from the instance root', async () => {
|
|
@@ -427,6 +477,22 @@ await ta('provisionInstance(dedicated) does NOT run local createdb/migrate/seed
|
|
|
427
477
|
assert.ok(!cmds.some((c) => /INSERT INTO versions/.test(c)), 'dedicated does not run the version seed locally (it rides cloud-init)');
|
|
428
478
|
});
|
|
429
479
|
|
|
480
|
+
await ta('dedicatedUserData bakes the nightly backup: unit files (b64) + dir + timer enable (task 2213)', async () => {
|
|
481
|
+
const inst = { ...coTenant, hosting_shape: 'dedicated' };
|
|
482
|
+
const cloudInit = P.dedicatedUserData(inst);
|
|
483
|
+
// the two unit files land via write_files (base64, so no YAML block-scalar escaping)
|
|
484
|
+
assert.match(cloudInit, /write_files:/);
|
|
485
|
+
assert.match(cloudInit, new RegExp('path: ' + reEsc(P.backupServicePath(inst))));
|
|
486
|
+
assert.match(cloudInit, new RegExp('path: ' + reEsc(P.backupTimerPath(inst))));
|
|
487
|
+
assert.match(cloudInit, /encoding: b64/);
|
|
488
|
+
// the base64 content round-trips to a real unit carrying the right DB
|
|
489
|
+
const b64 = cloudInit.match(/content: ([A-Za-z0-9+/=]+)/)[1];
|
|
490
|
+
assert.match(Buffer.from(b64, 'base64').toString('utf8'), /Environment=GDS_BACKUP_DB=mercury/);
|
|
491
|
+
// and the dir is created + the timer enabled on first boot
|
|
492
|
+
assert.match(cloudInit, /install -d -o lars -g lars -m 0750 \/var\/backups\/gds/);
|
|
493
|
+
assert.match(cloudInit, /enable --now mercury mercury-db-backup\.timer/);
|
|
494
|
+
});
|
|
495
|
+
|
|
430
496
|
console.log('\nDNS UPSERT composition (P4, CONFIG override):');
|
|
431
497
|
|
|
432
498
|
// A recorder that also captures each exec's env (so we can assert the DNS hook env).
|
|
@@ -739,6 +805,9 @@ await ta('teardownInstance(privileged): systemctl + rm-under-/etc are sudo-prefi
|
|
|
739
805
|
assert.match(joined, /sudo systemctl disable --now mercury/);
|
|
740
806
|
assert.match(joined, /sudo rm -f \/etc\/systemd\/system\/mercury\.service/);
|
|
741
807
|
assert.match(joined, /sudo systemctl daemon-reload/);
|
|
808
|
+
// task 2213: the backup units come down too (the dumps + dir are left in place)
|
|
809
|
+
assert.match(joined, /sudo systemctl disable --now mercury-db-backup\.timer/);
|
|
810
|
+
assert.match(joined, /sudo rm -f \/etc\/systemd\/system\/mercury-db-backup\.service \/etc\/systemd\/system\/mercury-db-backup\.timer/);
|
|
742
811
|
});
|
|
743
812
|
|
|
744
813
|
await ta('teardownInstance(default): NO sudo (bare, unchanged)', async () => {
|