@bongos/core 1.19.205 → 1.19.206
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 +28 -28
- package/.claude/skills/new-project/SKILL.md +3 -1
- package/clients/bongos-client/index.d.ts +1 -1
- package/docs/adr/0055-server-mediated-branch-publish.md +2 -0
- package/docs/adr/0133-github-app-manifest-onboarding-signin.md +2 -2
- package/docs/api/openapi.json +4 -2
- package/docs/recipes/ops-gotchas.md +8 -1
- package/modules/lifecycle/github-push.js +130 -18
- package/modules/lifecycle/publish-reconciler.js +44 -1
- package/modules/lifecycle/routes/tasks.js +21 -4
- package/modules/provisioning/onboard-plan.js +1 -0
- package/modules/provisioning/provisioning.js +18 -8
- package/modules/provisioning/routes/provisioning.js +4 -0
- package/modules/provisioning/tests/provisioning.mjs +5 -3
- package/package-lock.json +2 -2
- package/package.json +1 -1
- package/public-landing/projects.html +19 -2
- package/scripts/gds/onboard.js +11 -3
- package/scripts/gds/provision.js +27 -4
- package/src/module-api.js +3 -1
- package/tests/onboard.mjs +5 -1
- package/tests/provision.mjs +30 -2
- package/tests/publish_branch_route.mjs +153 -2
- package/tests/publish_reconciler.mjs +63 -0
|
@@ -212,6 +212,69 @@ test('no push credential → no-op (skipped: unconfigured), never touches the DB
|
|
|
212
212
|
assert.equal(deps._calls.status.length, 0, 'must not call GitHub when unconfigured');
|
|
213
213
|
});
|
|
214
214
|
|
|
215
|
+
// task 1003049 — an App with id + key but no repo installation passes isConfigured,
|
|
216
|
+
// then every per-branch call throws APP_NOT_INSTALLED into a best-effort catch:
|
|
217
|
+
// silent stranding. The sweep must instead say it loudly ONCE and park.
|
|
218
|
+
test('1003049 App configured but NOT installed → loud one-time WARN, sweep parked, heals when installed', async () => {
|
|
219
|
+
const deps = mkDeps({ configured: true, rows: [{ task_id: 1, builder_id: 9, worktree_name: 'wt' }] });
|
|
220
|
+
deps.warnState = {};
|
|
221
|
+
let installed = false;
|
|
222
|
+
deps.githubPush.appConfigured = () => true;
|
|
223
|
+
deps.githubPush.getInstallationToken = async () => {
|
|
224
|
+
if (!installed) {
|
|
225
|
+
throw Object.assign(new Error('APP_NOT_INSTALLED'), {
|
|
226
|
+
code: 'APP_NOT_INSTALLED', owner: 'someowner', repo: 'somerepo',
|
|
227
|
+
install_url: 'https://github.com/apps/x/installations/new',
|
|
228
|
+
});
|
|
229
|
+
}
|
|
230
|
+
return 'tok';
|
|
231
|
+
};
|
|
232
|
+
const r1 = await reconciler.runSweep(deps);
|
|
233
|
+
assert.equal(r1.skipped, 'app_not_installed');
|
|
234
|
+
assert.equal(deps._calls.status.length, 0, 'no per-branch GitHub calls while uninstallable');
|
|
235
|
+
const warns = () => deps._calls.errors.filter((m) => /not installed/.test(m));
|
|
236
|
+
assert.equal(warns().length, 1, 'warned exactly once');
|
|
237
|
+
assert.match(warns()[0], /someowner\/somerepo/, 'names the repo');
|
|
238
|
+
assert.match(warns()[0], /installations\/new/, 'carries the install URL');
|
|
239
|
+
const r2 = await reconciler.runSweep(deps);
|
|
240
|
+
assert.equal(r2.skipped, 'app_not_installed');
|
|
241
|
+
assert.equal(warns().length, 1, 'the streak stays quiet');
|
|
242
|
+
installed = true;
|
|
243
|
+
const r3 = await reconciler.runSweep(deps);
|
|
244
|
+
assert.ok(!r3.skipped, 'an installed App lets the sweep run again');
|
|
245
|
+
});
|
|
246
|
+
|
|
247
|
+
test('1003049 foreign-install watch: WARNs once per streak on installs beyond the repo owner, re-arms when clean', async () => {
|
|
248
|
+
const deps = mkDeps({ configured: true, rows: [] });
|
|
249
|
+
deps.warnState = {};
|
|
250
|
+
let roster = [{ id: 2, account: 'stranger' }];
|
|
251
|
+
deps.githubPush.appConfigured = () => true;
|
|
252
|
+
deps.githubPush.getInstallationToken = async () => 'tok';
|
|
253
|
+
deps.githubPush.foreignInstallations = async () => roster;
|
|
254
|
+
const warns = () => deps._calls.errors.filter((m) => /beyond the repo owner/.test(m));
|
|
255
|
+
await reconciler.runSweep(deps);
|
|
256
|
+
assert.equal(warns().length, 1, 'a foreign install is surfaced loudly');
|
|
257
|
+
assert.match(warns()[0], /stranger/, 'names the account');
|
|
258
|
+
await reconciler.runSweep(deps);
|
|
259
|
+
assert.equal(warns().length, 1, 'the streak stays quiet');
|
|
260
|
+
roster = [];
|
|
261
|
+
await reconciler.runSweep(deps);
|
|
262
|
+
roster = [{ id: 9, account: 'other' }];
|
|
263
|
+
await reconciler.runSweep(deps);
|
|
264
|
+
assert.equal(warns().length, 2, 'a clean roster re-arms the warning');
|
|
265
|
+
});
|
|
266
|
+
|
|
267
|
+
test('1003049 a transient App-token exchange failure does NOT park the sweep', async () => {
|
|
268
|
+
const deps = mkDeps({ configured: true, rows: [] });
|
|
269
|
+
deps.warnState = {};
|
|
270
|
+
deps.githubPush.appConfigured = () => true;
|
|
271
|
+
deps.githubPush.getInstallationToken = async () => {
|
|
272
|
+
throw Object.assign(new Error('boom'), { code: 'APP_TOKEN_FAILED', status: 500 });
|
|
273
|
+
};
|
|
274
|
+
const r = await reconciler.runSweep(deps);
|
|
275
|
+
assert.ok(!r.skipped, 'transient exchange failures are per-branch territory, not a park');
|
|
276
|
+
});
|
|
277
|
+
|
|
215
278
|
test('a landed branch → shipTask + broadcast fire, reconciled=1, attributed to the claim holder', async () => {
|
|
216
279
|
const deps = mkDeps({
|
|
217
280
|
rows: [{ task_id: 1096, builder_id: 7, worktree_name: 't1096-helpers', published_head_sha: ON_MAIN }],
|