@bongos/core 1.20.78 → 1.20.80
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 +74 -39
- package/clients/bongos-client/README.md +1 -1
- package/clients/bongos-client/bongos-client.global.js +2 -0
- package/clients/bongos-client/index.cjs +2 -0
- package/clients/bongos-client/index.d.ts +3 -0
- package/clients/bongos-client/index.mjs +2 -0
- package/docs/adr/0161-publish-on-merge.md +1 -1
- package/docs/adr/0361-merges-publish-as-candidates-release-decides-what-others-are-offered.md +59 -0
- package/docs/adr/README.md +1 -0
- package/docs/api/openapi.json +60 -2
- package/docs/api-reference.md +3 -2
- package/docs/module-api-changelog.md +4 -0
- package/docs/recipes/private-npm-distribution.md +2 -0
- package/docs/recipes/upgrading-the-core.md +1 -1
- package/modules/lifecycle/module.json +2 -1
- package/modules/lifecycle/routes/lifecycle.js +7 -0
- package/modules/lifecycle/workflow-dispatch.js +45 -0
- package/modules/npm-release/module.json +2 -1
- package/modules/npm-release/public/work.js +74 -0
- package/modules/npm-release/release.js +129 -0
- package/modules/npm-release/routes/release.js +65 -0
- package/modules/npm-release/work.js +16 -0
- package/modules/provisioning/render-standup.js +2 -1
- package/package-lock.json +2 -2
- package/package.json +1 -1
- package/release-notes.json +16 -0
- package/scripts/gds/init.js +16 -6
- package/scripts/gds/provision-core-upgrade.js +30 -2
- package/scripts/gds/release-core.js +80 -0
- package/scripts/gds/update-channel.js +12 -3
- package/src/bongos/core-update.js +17 -8
- package/src/module-api.js +1 -1
- package/tests/core_update_banner.mjs +41 -5
- package/tests/core_upgrade_runner.mjs +66 -1
- package/tests/init.mjs +21 -5
- package/tests/npm_release_release.mjs +195 -0
- package/tests/release_core.mjs +110 -0
- package/tests/update_channel.mjs +7 -0
- package/tests/upgrade_migrate.mjs +1 -1
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
// tests/release_core.mjs — may this version be released? (task 1004298, ADR 0361)
|
|
2
|
+
//
|
|
3
|
+
// release-core.js is what publish.yml's release job asks before it moves the registry's `latest`
|
|
4
|
+
// label — the label every project other than the platform hall is offered. These pin each way it
|
|
5
|
+
// says no, and that a refusal never reaches the label.
|
|
6
|
+
|
|
7
|
+
import { test } from 'node:test';
|
|
8
|
+
import { strict as assert } from 'node:assert';
|
|
9
|
+
import { createRequire } from 'node:module';
|
|
10
|
+
import fs from 'node:fs';
|
|
11
|
+
import os from 'node:os';
|
|
12
|
+
import path from 'node:path';
|
|
13
|
+
import { fileURLToPath } from 'node:url';
|
|
14
|
+
|
|
15
|
+
const require = createRequire(import.meta.url);
|
|
16
|
+
const { releaseDecision, main } = require('../scripts/gds/release-core.js');
|
|
17
|
+
|
|
18
|
+
const VERSIONS = ['1.20.70', '1.20.75', '1.20.80', '1.20.81-rc.1'];
|
|
19
|
+
|
|
20
|
+
test('a published version newer than the release goes', () => {
|
|
21
|
+
const d = releaseDecision({ version: '1.20.80', versions: VERSIONS, latest: '1.20.70' });
|
|
22
|
+
assert.equal(d.go, true);
|
|
23
|
+
assert.match(d.reason, /was 1\.20\.70/);
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
test('with no release yet, any published stable version goes', () => {
|
|
27
|
+
assert.equal(releaseDecision({ version: '1.20.75', versions: VERSIONS, latest: null }).go, true);
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
test('the version already released is a harmless no-op, not a refusal', () => {
|
|
31
|
+
const d = releaseDecision({ version: '1.20.75', versions: VERSIONS, latest: '1.20.75' });
|
|
32
|
+
assert.equal(d.go, false);
|
|
33
|
+
assert.equal(d.noop, true);
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
test('an OLDER version is refused: it would move every project backwards', () => {
|
|
37
|
+
const d = releaseDecision({ version: '1.20.70', versions: VERSIONS, latest: '1.20.75' });
|
|
38
|
+
assert.equal(d.go, false);
|
|
39
|
+
assert.equal(d.noop, false);
|
|
40
|
+
assert.match(d.reason, /backwards/);
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
test('a version that was never published is refused', () => {
|
|
44
|
+
const d = releaseDecision({ version: '1.20.99', versions: VERSIONS, latest: '1.20.70' });
|
|
45
|
+
assert.equal(d.go, false);
|
|
46
|
+
assert.match(d.reason, /not published/);
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
test('anything that is not a plain x.y.z is refused before it reaches a command line', () => {
|
|
50
|
+
for (const v of ['', '1.20', 'latest', '1.20.80; rm -rf /', '1.20.81-rc.1', null, undefined]) {
|
|
51
|
+
assert.equal(releaseDecision({ version: v, versions: VERSIONS, latest: '1.20.70' }).go, false, String(v));
|
|
52
|
+
}
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
const run = (argv, registry) => {
|
|
56
|
+
const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'release-core-'));
|
|
57
|
+
const out = path.join(dir, 'out');
|
|
58
|
+
const lines = [];
|
|
59
|
+
const code = main(argv, { env: { GITHUB_OUTPUT: out }, read: () => registry, log: (s) => lines.push(s), err: (s) => lines.push(s) });
|
|
60
|
+
return { code, output: fs.existsSync(out) ? fs.readFileSync(out, 'utf8') : '', lines };
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
test('the CLI writes go=true and exits 0 for a release', () => {
|
|
64
|
+
const r = run(['--version', '1.20.80'], { ok: true, versions: VERSIONS, latest: '1.20.75' });
|
|
65
|
+
assert.equal(r.code, 0);
|
|
66
|
+
assert.equal(r.output, 'go=true\n');
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
test('the CLI writes go=false and exits 1 for a refusal, so the label never moves', () => {
|
|
70
|
+
const r = run(['--version', '1.20.70'], { ok: true, versions: VERSIONS, latest: '1.20.75' });
|
|
71
|
+
assert.equal(r.code, 1);
|
|
72
|
+
assert.equal(r.output, 'go=false\n');
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
test('the CLI writes go=false and exits 0 for a double press', () => {
|
|
76
|
+
const r = run(['--version', '1.20.75'], { ok: true, versions: VERSIONS, latest: '1.20.75' });
|
|
77
|
+
assert.equal(r.code, 0);
|
|
78
|
+
assert.equal(r.output, 'go=false\n');
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
test('an unreadable registry is a refusal, never a guess', () => {
|
|
82
|
+
const r = run(['--version', '1.20.80'], { ok: false, error: 'E503' });
|
|
83
|
+
assert.equal(r.code, 1);
|
|
84
|
+
assert.equal(r.output, 'go=false\n');
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
// ── the workflow wires it as described ─────────────────────────────────────────
|
|
88
|
+
|
|
89
|
+
const YML = fs.readFileSync(fileURLToPath(new URL('../.github/workflows/publish.yml', import.meta.url)), 'utf8');
|
|
90
|
+
|
|
91
|
+
test('publish.yml: the release job asks release-core.js and moves `latest` only on go', () => {
|
|
92
|
+
assert.match(YML, /release_version:/, 'the dispatch input exists');
|
|
93
|
+
assert.match(YML, /node scripts\/gds\/release-core\.js --version "\$RELEASE_VERSION"/);
|
|
94
|
+
assert.match(YML, /if: steps\.decide\.outputs\.go == 'true'\s+run: npm dist-tag add "@bongos\/core@\$RELEASE_VERSION" latest/);
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
test('publish.yml: the version reaches the shell through env, never inline ${{ }} interpolation', () => {
|
|
98
|
+
assert.doesNotMatch(YML, /release-core\.js --version "\$\{\{/);
|
|
99
|
+
assert.doesNotMatch(YML, /dist-tag add "@bongos\/core@\$\{\{/);
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
test('publish.yml: a release dispatch never also runs the publish job', () => {
|
|
103
|
+
assert.match(YML, /github\.event_name == 'workflow_dispatch' && inputs\.release_version == ''/);
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
test('publish.yml: candidates publish under `next` only when the owner has armed it', () => {
|
|
107
|
+
assert.match(YML, /CANDIDATES: \$\{\{ vars\.PUBLISH_AS_CANDIDATE \}\}/);
|
|
108
|
+
assert.match(YML, /if \[ "\$CANDIDATES" = "1" \]; then TAG_ARGS="--tag next"; fi/);
|
|
109
|
+
assert.match(YML, /npm publish "\$TGZ" --access public \$TAG_ARGS/);
|
|
110
|
+
});
|
package/tests/update_channel.mjs
CHANGED
|
@@ -348,6 +348,13 @@ t('normalizeFollow: released and candidates are the words; anything else reads a
|
|
|
348
348
|
for (const v of [undefined, null, '', 'every', 'latest', 'next', 42]) assert.equal(uc.normalizeFollow(v), 'released', String(v));
|
|
349
349
|
});
|
|
350
350
|
|
|
351
|
+
t('followForShape: only the platform hall (control-plane) follows candidates (task 1004298)', () => {
|
|
352
|
+
assert.equal(uc.followForShape('control-plane'), 'candidates');
|
|
353
|
+
for (const shape of ['co-tenant', 'cloud-host', 'byo-host', undefined, null, 'bogus']) {
|
|
354
|
+
assert.equal(uc.followForShape(shape), 'released', `${shape} follows releases`);
|
|
355
|
+
}
|
|
356
|
+
});
|
|
357
|
+
|
|
351
358
|
t('versionsForFollow: candidates takes every version; released stops at the released label', () => {
|
|
352
359
|
const available = ['1.19.1038', '1.19.1039', '1.19.1040', '1.19.1041'];
|
|
353
360
|
assert.deepEqual(uc.versionsForFollow({ follow: 'candidates', available, released: '1.19.1039' }), available);
|
|
@@ -47,7 +47,7 @@ function adoptedProject({ installed = '1.19.312' } = {}) {
|
|
|
47
47
|
return dir;
|
|
48
48
|
}
|
|
49
49
|
|
|
50
|
-
function scaffoldedProject(scripts = { migrate: init.INSTANCE_MIGRATE_SCRIPT,
|
|
50
|
+
function scaffoldedProject(scripts = { migrate: init.INSTANCE_MIGRATE_SCRIPT, hall: init.INSTANCE_HALL_SCRIPT }) {
|
|
51
51
|
const dir = fixture('upgrade-migrate-scaffold-');
|
|
52
52
|
writeFileSync(path.join(dir, 'package.json'), `${JSON.stringify({ name: 'charter', private: true, scripts, dependencies: { '@bongos/core': '1.19.871' } }, null, 2)}\n`);
|
|
53
53
|
return dir;
|