@bongos/core 1.21.157 → 1.21.158
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 +17 -17
- package/clients/bongos-client/index.d.ts +1 -1
- package/docs/api/openapi.json +5 -3
- package/docs/module-api-changelog.md +2 -0
- package/docs/modules-contract.md +2 -1
- package/package-lock.json +2 -2
- package/package.json +1 -1
- package/release-notes.json +6 -0
- package/scripts/gds/module.js +3 -0
- package/src/bongos/module-store.js +15 -3
- package/src/bongos/routes/modules.js +3 -2
- package/src/module-api.js +1 -1
- package/tests/module_store_publish.mjs +32 -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.21.
|
|
6
|
-
"core_contract": "1.21.
|
|
7
|
-
"source_commit": "
|
|
5
|
+
"core_version": "1.21.158",
|
|
6
|
+
"core_contract": "1.21.158",
|
|
7
|
+
"source_commit": "3489a15a31499506f0eff7e906cfda2d98431e90",
|
|
8
8
|
"source_ref": "HEAD",
|
|
9
|
-
"built_at": "2026-10-22T22:00
|
|
9
|
+
"built_at": "2026-10-22T22:19:00.152Z",
|
|
10
10
|
"redaction": {
|
|
11
11
|
"model": "docs-redacted+functional-verbatim",
|
|
12
12
|
"docs_redacted": 608,
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
"gate": "passed"
|
|
18
18
|
},
|
|
19
19
|
"file_count": 3810,
|
|
20
|
-
"tree_sha256": "
|
|
20
|
+
"tree_sha256": "3b096c21388e06866acbe8324be4412fd05377ff38878dafe62e112c722447b0",
|
|
21
21
|
"files": [
|
|
22
22
|
{
|
|
23
23
|
"path": ".claude/skills/ask-for-help/SKILL.md",
|
|
@@ -337,7 +337,7 @@
|
|
|
337
337
|
{
|
|
338
338
|
"path": "clients/bongos-client/index.d.ts",
|
|
339
339
|
"mode": "0000644",
|
|
340
|
-
"sha256": "
|
|
340
|
+
"sha256": "71db0ddcc4d2ca7d06c2aceabcd9b231b2a27820a8907693f3729712fd628444"
|
|
341
341
|
},
|
|
342
342
|
{
|
|
343
343
|
"path": "clients/bongos-client/index.mjs",
|
|
@@ -2387,7 +2387,7 @@
|
|
|
2387
2387
|
{
|
|
2388
2388
|
"path": "docs/api/openapi.json",
|
|
2389
2389
|
"mode": "0000644",
|
|
2390
|
-
"sha256": "
|
|
2390
|
+
"sha256": "4e52703b8cda179fb90c9ab0dfee088ad7b76761af74e112a1de7f8887f5e3f2"
|
|
2391
2391
|
},
|
|
2392
2392
|
{
|
|
2393
2393
|
"path": "docs/architecture.md",
|
|
@@ -2927,12 +2927,12 @@
|
|
|
2927
2927
|
{
|
|
2928
2928
|
"path": "docs/module-api-changelog.md",
|
|
2929
2929
|
"mode": "0000644",
|
|
2930
|
-
"sha256": "
|
|
2930
|
+
"sha256": "6fad8de7a3054c6e7b8b332031be0c18ed8f46c2af96f4239e4a4ccec33ba99d"
|
|
2931
2931
|
},
|
|
2932
2932
|
{
|
|
2933
2933
|
"path": "docs/modules-contract.md",
|
|
2934
2934
|
"mode": "0000644",
|
|
2935
|
-
"sha256": "
|
|
2935
|
+
"sha256": "e8063afec0e26bb9b745fa18e1856f8a26e7ef0f3e4e9f31677efb623fb58ac0"
|
|
2936
2936
|
},
|
|
2937
2937
|
{
|
|
2938
2938
|
"path": "docs/onboarding/diagrams/01-task-lifecycle.mmd",
|
|
@@ -10557,12 +10557,12 @@
|
|
|
10557
10557
|
{
|
|
10558
10558
|
"path": "package-lock.json",
|
|
10559
10559
|
"mode": "0000644",
|
|
10560
|
-
"sha256": "
|
|
10560
|
+
"sha256": "43aaf784a6ee3ebf4f84cc98e112f8997a5203aa14a07af4dfbedcc095095b84"
|
|
10561
10561
|
},
|
|
10562
10562
|
{
|
|
10563
10563
|
"path": "package.json",
|
|
10564
10564
|
"mode": "0000644",
|
|
10565
|
-
"sha256": "
|
|
10565
|
+
"sha256": "2dd1e6902491a012efb456d02ebe5b3fd8d1d45cd5f85780a2e068b465a73911"
|
|
10566
10566
|
},
|
|
10567
10567
|
{
|
|
10568
10568
|
"path": "public-docs/index.html",
|
|
@@ -10582,7 +10582,7 @@
|
|
|
10582
10582
|
{
|
|
10583
10583
|
"path": "release-notes.json",
|
|
10584
10584
|
"mode": "0000644",
|
|
10585
|
-
"sha256": "
|
|
10585
|
+
"sha256": "8081f6270f14a4a734dbd8b65c3eab717e38a0a7089c14f3b21405bb734639e5"
|
|
10586
10586
|
},
|
|
10587
10587
|
{
|
|
10588
10588
|
"path": "scripts/cron/style_guide_audit.sh",
|
|
@@ -11512,7 +11512,7 @@
|
|
|
11512
11512
|
{
|
|
11513
11513
|
"path": "scripts/gds/module.js",
|
|
11514
11514
|
"mode": "0000644",
|
|
11515
|
-
"sha256": "
|
|
11515
|
+
"sha256": "7277f499a707f7c946ee8aec839f4f13539b8178ef9915f6c5d623acb341c9ae"
|
|
11516
11516
|
},
|
|
11517
11517
|
{
|
|
11518
11518
|
"path": "scripts/gds/move-escalation.js",
|
|
@@ -12712,7 +12712,7 @@
|
|
|
12712
12712
|
{
|
|
12713
12713
|
"path": "src/bongos/module-store.js",
|
|
12714
12714
|
"mode": "0000644",
|
|
12715
|
-
"sha256": "
|
|
12715
|
+
"sha256": "fb6fdff53de296e7b8d981082013d57f65f1e89ae234a7d417e6e455578b94a3"
|
|
12716
12716
|
},
|
|
12717
12717
|
{
|
|
12718
12718
|
"path": "src/bongos/module-submissions.js",
|
|
@@ -12877,7 +12877,7 @@
|
|
|
12877
12877
|
{
|
|
12878
12878
|
"path": "src/bongos/routes/modules.js",
|
|
12879
12879
|
"mode": "0000644",
|
|
12880
|
-
"sha256": "
|
|
12880
|
+
"sha256": "a3d9bfef9fdb4a1b8c1050555eaeafc80d312963368eaab6838cf56089c02578"
|
|
12881
12881
|
},
|
|
12882
12882
|
{
|
|
12883
12883
|
"path": "src/bongos/routes/my-passkeys.js",
|
|
@@ -12967,7 +12967,7 @@
|
|
|
12967
12967
|
{
|
|
12968
12968
|
"path": "src/module-api.js",
|
|
12969
12969
|
"mode": "0000644",
|
|
12970
|
-
"sha256": "
|
|
12970
|
+
"sha256": "eba81df2b570c255f36f7ed29ac55ff488f1aad92984d9c38fe2d4842c047cef"
|
|
12971
12971
|
},
|
|
12972
12972
|
{
|
|
12973
12973
|
"path": "src/module-loader/catalog.js",
|
|
@@ -16652,7 +16652,7 @@
|
|
|
16652
16652
|
{
|
|
16653
16653
|
"path": "tests/module_store_publish.mjs",
|
|
16654
16654
|
"mode": "0000644",
|
|
16655
|
-
"sha256": "
|
|
16655
|
+
"sha256": "1ef4980d9afca0dd5bbf2af174098144eac20d62bec52ab642cf314a5d7889d1"
|
|
16656
16656
|
},
|
|
16657
16657
|
{
|
|
16658
16658
|
"path": "tests/module_store_publish_route.mjs",
|
|
@@ -548,7 +548,7 @@ export interface PostStoreModulesKeyAcquiredRequest { version: string }
|
|
|
548
548
|
export interface PostStoreModulesKeyAcquiredResponse { ok: boolean; entitlement: unknown }
|
|
549
549
|
export interface PostStoreModulesKeyDelistRequest { reason?: string }
|
|
550
550
|
export interface PostStoreModulesKeyDelistResponse { ok: boolean; module_key: unknown; status: unknown; delisted_at: unknown }
|
|
551
|
-
export interface PostStoreModulesKeyVersionsResponse { ok: boolean; created_module: unknown; version: unknown }
|
|
551
|
+
export interface PostStoreModulesKeyVersionsResponse { ok: boolean; created_module: unknown; version: unknown; channel: unknown }
|
|
552
552
|
export interface PostStoreModulesKeyVersionsVersionErrorCountRequest { day: string; count: number }
|
|
553
553
|
export interface PostStoreModulesKeyVersionsVersionInstallReportRequest { ok: boolean; error_code?: string }
|
|
554
554
|
export interface PostStoreModulesKeyVersionsVersionReassessRequest { reason?: string }
|
package/docs/api/openapi.json
CHANGED
|
@@ -23443,7 +23443,7 @@
|
|
|
23443
23443
|
"store"
|
|
23444
23444
|
],
|
|
23445
23445
|
"summary": "POST /store/modules/:key/versions",
|
|
23446
|
-
"description": "POST /api/bongos/store/modules/:key/versions — publish one module version to the store (task 1004271, ADR 0338 D1). Body: the gzip tarball `bongos module publish` builds (scripts/gds/module-artifact.js), sent as application/gzip. The route trusts nothing but the bytes: it recomputes every hash, re-runs the publish denylist, validates module.json and checks that HOWTO.md has its required sections (ADR 0347 D4) itself (verifyModuleArtifact), then keeps the tarball on the control plane's disk and INSERTs the version row in one transaction (module-store.js). A key's first publish makes the caller its author; after that only the author may publish, a delisted key takes nothing new, and a version is published once and never changed. rank: metic+archon — it puts code in a shared store other instances install from. Gate: requirePermission('module.submit') — the atom that already guards filing a module into a shared queue; a dedicated `module.publish` atom is a follow-up once who-may-sell is decided.\n\n**Rank:** `metic+archon` — Metic or Archon rank (review/triage powers).\n\n**Permissions:** `module.submit` (all required).",
|
|
23446
|
+
"description": "POST /api/bongos/store/modules/:key/versions — publish one module version to the store (task 1004271, ADR 0338 D1). Body: the gzip tarball `bongos module publish` builds (scripts/gds/module-artifact.js), sent as application/gzip. The route trusts nothing but the bytes: it recomputes every hash, re-runs the publish denylist, validates module.json and checks that HOWTO.md has its required sections (ADR 0347 D4) itself (verifyModuleArtifact), then keeps the tarball on the control plane's disk and INSERTs the version row in one transaction (module-store.js). A key's first publish makes the caller its author; after that only the author may publish, a delisted key takes nothing new, and a version is published once and never changed. Every new version lands on the tester channel; the body names no channel, so nothing here can skip testers (task 1003803, ADR 0344 D2). rank: metic+archon — it puts code in a shared store other instances install from. Gate: requirePermission('module.submit') — the atom that already guards filing a module into a shared queue; a dedicated `module.publish` atom is a follow-up once who-may-sell is decided.\n\n**Rank:** `metic+archon` — Metic or Archon rank (review/triage powers).\n\n**Permissions:** `module.submit` (all required).",
|
|
23447
23447
|
"x-rank": "metic+archon",
|
|
23448
23448
|
"x-source": "src/bongos/routes/modules.js",
|
|
23449
23449
|
"x-permissions": [
|
|
@@ -35052,12 +35052,14 @@
|
|
|
35052
35052
|
"type": "boolean"
|
|
35053
35053
|
},
|
|
35054
35054
|
"created_module": {},
|
|
35055
|
-
"version": {}
|
|
35055
|
+
"version": {},
|
|
35056
|
+
"channel": {}
|
|
35056
35057
|
},
|
|
35057
35058
|
"required": [
|
|
35058
35059
|
"ok",
|
|
35059
35060
|
"created_module",
|
|
35060
|
-
"version"
|
|
35061
|
+
"version",
|
|
35062
|
+
"channel"
|
|
35061
35063
|
]
|
|
35062
35064
|
},
|
|
35063
35065
|
"PostStoreModulesKeyVersionsVersionErrorCountRequest": {
|
|
@@ -3151,5 +3151,7 @@ is load-bearing: the script throws rather than guess if it is missing, and
|
|
|
3151
3151
|
landed since 1.21.155 with no explicit bump. run 39808834444. (task 1002620)
|
|
3152
3152
|
1.21.157 — CI auto-patch (publish-on-merge, ADR 0161): carrier for merges
|
|
3153
3153
|
landed since 1.21.156 with no explicit bump. run 39861648960. (task 1002620)
|
|
3154
|
+
1.21.158 — CI auto-patch (publish-on-merge, ADR 0161): carrier for merges
|
|
3155
|
+
landed since 1.21.157 with no explicit bump. run 39863960705. (task 1002620)
|
|
3154
3156
|
---------------------------------------------------------------------------
|
|
3155
3157
|
```
|
package/docs/modules-contract.md
CHANGED
|
@@ -388,6 +388,7 @@ Run `bongos upgrade` to confirm coreVersion compatibility, then `node scripts/gd
|
|
|
388
388
|
|
|
389
389
|
- The version is `module.json`'s `version`. Each version is published **once** and must be newer than the last — bump it to publish a fix.
|
|
390
390
|
- Your first publish of a key makes you its author; only the author can publish later versions, and a delisted key takes none.
|
|
391
|
+
- **Every new version goes to testers first** (task 1003803, [ADR 0344](adr/0344-a-tester-is-a-project-that-opts-in-once.md) D2). A published version, first release or update, is recorded on the `tester` channel (`store_module_version_channels`, reason `new_version`), and the publish response says so as `channel`. A publish cannot name a channel, so there is no flag that sends an untested version to every project. Moving it to `general` is a separate, later step: the promotion gate (task 1003804).
|
|
391
392
|
- The publish denylist applies (ADR 0098), plus a refusal of credential-named files (`.env*`, `*.pem`, `*.key`, `id_rsa`, …) anywhere in the module. The store re-checks every hash and the denylist itself.
|
|
392
393
|
- Gate: Metic+ (`module.submit`) for now. This is not `bongos module submit`, which proposes a module *into* core.
|
|
393
394
|
- **A how-to is required** ([ADR 0347](adr/0347-every-store-module-ships-a-how-to.md)). `modules/<key>/HOWTO.md` is what a buyer reads — the store shows it as the module's page — so it is written for the person who installs the module, not for an AI working inside it (that is `CLAUDE.md`). It is plain Markdown: any AI or person can write it. Publish refuses the version unless it has these five level-2 headings, in any order and any case, each with some text under it: `## What it does`, `## Install and enable`, `## How to use it` (with one worked example), `## Configuration` ("None." is fine) and `## Limits and known issues` ("None known." is fine). HTML comments don't count as text, so the scaffold's prompts must be replaced. The refusal names each failing section. The check is one function, `checkHowto` in `scripts/gds/module-artifact.js`; the CLI runs it before uploading and the store runs it again on upload, so a hand-built upload can't skip it. `bongos module check` reports it as advice only — an upstream submit is not gated by it. Install and update don't re-check it, so a version published before the gate still installs. The file travels in the tarball, so its hash pins it to the version. An optional `howto.artifactUrl` in `module.json` (an `https://` link on `claude.ai`) is shown as an extra, never graded.
|
|
@@ -405,7 +406,7 @@ Run `bongos upgrade` to confirm coreVersion compatibility, then `node scripts/gd
|
|
|
405
406
|
- Install never enables. Switch the module on from the hall's Modules tab.
|
|
406
407
|
- **Every version's how-to is a page** (task 1004366). `GET /store/modules/:key/versions/:version/howto` reads the `HOWTO.md` from that version's own stored tarball, after the same hash check install runs, and returns it as text; the hall shows it at `/builders/modules?howto=<key>&version=<X.Y.Z>`, rendered through the hall's escaping markdown reader (`md-reader.js`) because the file is author-written. Any signed-in builder can read it without holding the module; a delisted module's how-to reads only for people who hold it. "Save as PDF" is the browser's own print, with a print sheet that keeps only the document. The optional `howto.artifactUrl` shows as "Also available as a Claude page". Install prints the link, and a Modules-tab row links it when the module on disk is a published store version. A version published before the gate has none and answers 404 `howto_missing`.
|
|
407
408
|
- A module already on disk, in the instance's `modules/` or bundled with the core, is refused; moving one to a newer version is `update`. A module this core cannot mount is refused before download.
|
|
408
|
-
- A delisted module cannot be acquired or downloaded. Channels are not checked yet:
|
|
409
|
+
- A delisted module cannot be acquired or downloaded. Channels are not checked yet: install and update hand out a version whatever its channel until task 1003805 lands.
|
|
409
410
|
|
|
410
411
|
`bongos module update <key> [--version X.Y.Z]` moves an installed store module forward through the same three calls, so a paid module and a free one take one identical path, and a module you already hold is never charged again (`tests/module_update.mjs` fails if a price-conditional branch appears).
|
|
411
412
|
|
package/package-lock.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bongos/core",
|
|
3
|
-
"version": "1.21.
|
|
3
|
+
"version": "1.21.158",
|
|
4
4
|
"lockfileVersion": 3,
|
|
5
5
|
"requires": true,
|
|
6
6
|
"packages": {
|
|
7
7
|
"": {
|
|
8
8
|
"name": "@bongos/core",
|
|
9
|
-
"version": "1.21.
|
|
9
|
+
"version": "1.21.158",
|
|
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.21.
|
|
3
|
+
"version": "1.21.158",
|
|
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",
|
package/release-notes.json
CHANGED
|
@@ -9976,5 +9976,11 @@
|
|
|
9976
9976
|
"id": "1003802",
|
|
9977
9977
|
"text": "A project can now volunteer to try new versions of its modules before everyone else, and keep any single module on the normal versions."
|
|
9978
9978
|
}
|
|
9979
|
+
],
|
|
9980
|
+
"1.21.158": [
|
|
9981
|
+
{
|
|
9982
|
+
"id": "1003803",
|
|
9983
|
+
"text": "When a module author publishes a new version, it now goes only to projects that volunteered to test, never straight to everyone."
|
|
9984
|
+
}
|
|
9979
9985
|
]
|
|
9980
9986
|
}
|
package/scripts/gds/module.js
CHANGED
|
@@ -1006,6 +1006,9 @@ async function cmdPublish(args, {
|
|
|
1006
1006
|
}
|
|
1007
1007
|
const row = res.data && res.data.version;
|
|
1008
1008
|
log(`\n✓ Published "${key}" ${row ? row.version : sidecar.module_version} to the store${res.data && res.data.created_module ? ' — its first version, so you are its author' : ''}.`);
|
|
1009
|
+
if (res.data && res.data.channel === 'tester') {
|
|
1010
|
+
log(' It goes to testing projects first, and reaches every project once it has been tested (ADR 0344).');
|
|
1011
|
+
}
|
|
1009
1012
|
return 0;
|
|
1010
1013
|
}
|
|
1011
1014
|
|
|
@@ -14,7 +14,8 @@
|
|
|
14
14
|
// the store_modules row on a key's first publish (the publisher becomes its
|
|
15
15
|
// author), refuse anyone else's key, refuse a delisted key (ADR 0338 D2: delisting
|
|
16
16
|
// stops what comes next), refuse a version that already exists or is not newer
|
|
17
|
-
// than the latest, then INSERT the version row
|
|
17
|
+
// than the latest, then INSERT the version row and put it on the tester
|
|
18
|
+
// channel (task 1003803, ADR 0344 D2). Rows are only ever INSERTed.
|
|
18
19
|
//
|
|
19
20
|
// If step 2 refuses, the route removes the temp file step 1 wrote, so a refused
|
|
20
21
|
// publish leaves nothing behind.
|
|
@@ -164,8 +165,14 @@ function relativeArtifactPath(file, { dir = storeDir() } = {}) {
|
|
|
164
165
|
return path.relative(dir, file).split(path.sep).join('/');
|
|
165
166
|
}
|
|
166
167
|
|
|
168
|
+
// Every new version, first release or update, starts on the tester channel (task
|
|
169
|
+
// 1003803, ADR 0344 D2). It is not an input: a publish cannot name a channel, so no
|
|
170
|
+
// omitted or mistyped flag can send an untested version to every project at once.
|
|
171
|
+
// Reaching general is a separate, later act — the promotion gate (task 1003804).
|
|
172
|
+
const PUBLISH_CHANNEL = 'tester';
|
|
173
|
+
|
|
167
174
|
// One transaction. `db` is a pg Pool (anything with connect()). Returns
|
|
168
|
-
// { ok: true, version: row, created: bool } or { ok: false, code, message, status }.
|
|
175
|
+
// { ok: true, version: row, created: bool, channel } or { ok: false, code, message, status }.
|
|
169
176
|
async function publishVersion(input, { db, beforeCommit } = {}) {
|
|
170
177
|
const {
|
|
171
178
|
key, title, description, version, coreVersion, manifest,
|
|
@@ -217,11 +224,16 @@ async function publishVersion(input, { db, beforeCommit } = {}) {
|
|
|
217
224
|
RETURNING id, module_key, version, core_version, tree_sha256, tarball_sha256,
|
|
218
225
|
tarball_bytes, published_by, published_at`,
|
|
219
226
|
[key, version, coreVersion, manifest, treeSha256, tarballSha256, bytes, relPath, builderId]);
|
|
227
|
+
// Same transaction as the version row, so no version is ever without a channel.
|
|
228
|
+
await client.query(
|
|
229
|
+
`INSERT INTO store_module_version_channels (version_id, channel, reason, set_by)
|
|
230
|
+
VALUES ($1, $2, $3, $4)`,
|
|
231
|
+
[row.id, PUBLISH_CHANNEL, 'new_version', builderId]);
|
|
220
232
|
// Put the tarball in place while the row is still uncommitted: if this throws,
|
|
221
233
|
// the catch below rolls the row back, so the two can never disagree.
|
|
222
234
|
if (beforeCommit) await beforeCommit();
|
|
223
235
|
await client.query('COMMIT');
|
|
224
|
-
return { ok: true, created, version: row };
|
|
236
|
+
return { ok: true, created, version: row, channel: PUBLISH_CHANNEL };
|
|
225
237
|
} catch (e) {
|
|
226
238
|
try { await client.query('ROLLBACK'); } catch { /* the connection may be gone */ }
|
|
227
239
|
// Two publishes of the same version racing past the check: the UNIQUE wins.
|
|
@@ -469,7 +469,8 @@ module.exports = function buildModulesRouter() {
|
|
|
469
469
|
// in one transaction (module-store.js). A key's first
|
|
470
470
|
// publish makes the caller its author; after that only the author may publish,
|
|
471
471
|
// a delisted key takes nothing new, and a version is published once and never
|
|
472
|
-
// changed.
|
|
472
|
+
// changed. Every new version lands on the tester channel; the body names no
|
|
473
|
+
// channel, so nothing here can skip testers (task 1003803, ADR 0344 D2).
|
|
473
474
|
//
|
|
474
475
|
// rank: metic+archon — it puts code in a shared store other instances install
|
|
475
476
|
// from. Gate: requirePermission('module.submit') — the atom that already guards
|
|
@@ -516,7 +517,7 @@ module.exports = function buildModulesRouter() {
|
|
|
516
517
|
if (!result.ok) return res.fail(result.code, { status: result.status, message: result.message });
|
|
517
518
|
|
|
518
519
|
console.log(`[gds] module "${key}" ${v.version} published to the store by builder ${req.builder.id}`);
|
|
519
|
-
res.status(201).json({ ok: true, created_module: result.created, version: result.version });
|
|
520
|
+
res.status(201).json({ ok: true, created_module: result.created, version: result.version, channel: result.channel });
|
|
520
521
|
// A new version is a new thing to judge (task 1003794, ADR 0343): queue its
|
|
521
522
|
// assessment after answering, since the Security gate asks npm about its
|
|
522
523
|
// dependencies and can take a minute. The queue runs one at a time and is
|
package/src/module-api.js
CHANGED
|
@@ -75,7 +75,7 @@ const { responsibilityFor, ROLE_RESPONSIBILITIES } = require('./role-responsibil
|
|
|
75
75
|
// MAJOR (see allowBoxScope below): passes the request through untouched.
|
|
76
76
|
function deprecatedNoopMiddleware(_req, _res, next) { next(); }
|
|
77
77
|
|
|
78
|
-
const CORE_VERSION = '1.21.
|
|
78
|
+
const CORE_VERSION = '1.21.158'; // CI auto-patch carrier (ADR 0161); changelog: docs/module-api-changelog.md
|
|
79
79
|
|
|
80
80
|
// A namespaced logger so a module's log lines are attributable + consistent.
|
|
81
81
|
// Usage: const log = api.logger('discord'); log.info('mounted');
|
|
@@ -195,6 +195,7 @@ test('store: a key or version that could escape the store dir is refused before
|
|
|
195
195
|
// A fake pg pool: `state` is the registry, answers are computed from it.
|
|
196
196
|
function fakeDb(state = { modules: {}, versions: [] }) {
|
|
197
197
|
const log = [];
|
|
198
|
+
state.channels = state.channels || [];
|
|
198
199
|
const client = {
|
|
199
200
|
async query(sql, params = []) {
|
|
200
201
|
log.push(sql.trim().split(/\s+/).slice(0, 2).join(' '));
|
|
@@ -211,6 +212,10 @@ function fakeDb(state = { modules: {}, versions: [] }) {
|
|
|
211
212
|
const row = { id: state.versions.length + 1, module_key: params[0], version: params[1] };
|
|
212
213
|
state.versions.push(row); return { rows: [row] };
|
|
213
214
|
}
|
|
215
|
+
if (/^INSERT INTO store_module_version_channels/.test(sql)) {
|
|
216
|
+
state.channels.push({ version_id: params[0], channel: params[1], reason: params[2], set_by: params[3] });
|
|
217
|
+
return { rows: [] };
|
|
218
|
+
}
|
|
214
219
|
return { rows: [] };
|
|
215
220
|
},
|
|
216
221
|
release() {},
|
|
@@ -256,6 +261,33 @@ test('registry: a version is published once, and never older than the latest', a
|
|
|
256
261
|
assert.equal(ok.ok, true, 'numeric, not string, ordering: 1.10.0 > 1.2.0');
|
|
257
262
|
});
|
|
258
263
|
|
|
264
|
+
// Task 1003803, ADR 0344 D2: every new version, first release or update, lands on
|
|
265
|
+
// the tester channel. Publish takes no channel at all, so there is no flag to omit
|
|
266
|
+
// or misspell; reaching everyone is the promotion gate's later act (task 1003804).
|
|
267
|
+
test('channel: a first release lands on tester, recorded with who published it', async () => {
|
|
268
|
+
const db = fakeDb();
|
|
269
|
+
const r = await store.publishVersion(input(), { db: db.pool });
|
|
270
|
+
assert.equal(r.ok, true);
|
|
271
|
+
assert.equal(r.channel, 'tester');
|
|
272
|
+
assert.deepEqual(db.state.channels, [{ version_id: 1, channel: 'tester', reason: 'new_version', set_by: 7 }]);
|
|
273
|
+
});
|
|
274
|
+
|
|
275
|
+
test('channel: an update lands on tester too, never straight to general', async () => {
|
|
276
|
+
const state = { modules: { weather: { module_key: 'weather', author_id: 7, status: 'listed' } },
|
|
277
|
+
versions: [{ id: 1, module_key: 'weather', version: '1.1.0' }] };
|
|
278
|
+
const db = fakeDb(state);
|
|
279
|
+
const r = await store.publishVersion(input({ channel: 'general' }), { db: db.pool });
|
|
280
|
+
assert.equal(r.ok, true);
|
|
281
|
+
assert.equal(r.channel, 'tester', 'a channel named by the caller is not a way past testers');
|
|
282
|
+
assert.deepEqual(db.state.channels.map((c) => c.channel), ['tester']);
|
|
283
|
+
});
|
|
284
|
+
|
|
285
|
+
test('channel: a refused publish records no channel', async () => {
|
|
286
|
+
const db = fakeDb({ modules: { weather: { module_key: 'weather', author_id: 7, status: 'listed' } }, versions: [] });
|
|
287
|
+
await store.publishVersion(input({ builderId: 8 }), { db: db.pool });
|
|
288
|
+
assert.deepEqual(db.state.channels, []);
|
|
289
|
+
});
|
|
290
|
+
|
|
259
291
|
test('registry: if putting the file in place fails, the row is rolled back', async () => {
|
|
260
292
|
const db = fakeDb();
|
|
261
293
|
await assert.rejects(
|