@pellux/goodvibes-toolchain 1.11.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/README.md +50 -0
- package/dist/bin/build-binaries.d.ts +3 -0
- package/dist/bin/build-binaries.d.ts.map +1 -0
- package/dist/bin/build-binaries.js +67 -0
- package/dist/bin/changelog-gate.d.ts +3 -0
- package/dist/bin/changelog-gate.d.ts.map +1 -0
- package/dist/bin/changelog-gate.js +13 -0
- package/dist/bin/coverage-gate.d.ts +3 -0
- package/dist/bin/coverage-gate.d.ts.map +1 -0
- package/dist/bin/coverage-gate.js +19 -0
- package/dist/bin/package-install-check.d.ts +3 -0
- package/dist/bin/package-install-check.d.ts.map +1 -0
- package/dist/bin/package-install-check.js +14 -0
- package/dist/bin/per-job-green.d.ts +3 -0
- package/dist/bin/per-job-green.d.ts.map +1 -0
- package/dist/bin/per-job-green.js +54 -0
- package/dist/bin/post-build-smoke.d.ts +3 -0
- package/dist/bin/post-build-smoke.d.ts.map +1 -0
- package/dist/bin/post-build-smoke.js +14 -0
- package/dist/bin/publish-package.d.ts +3 -0
- package/dist/bin/publish-package.d.ts.map +1 -0
- package/dist/bin/publish-package.js +25 -0
- package/dist/bin/release-cut.d.ts +3 -0
- package/dist/bin/release-cut.d.ts.map +1 -0
- package/dist/bin/release-cut.js +27 -0
- package/dist/bin/sdk-pin-gate.d.ts +3 -0
- package/dist/bin/sdk-pin-gate.d.ts.map +1 -0
- package/dist/bin/sdk-pin-gate.js +15 -0
- package/dist/bin/sha256sums.d.ts +3 -0
- package/dist/bin/sha256sums.d.ts.map +1 -0
- package/dist/bin/sha256sums.js +37 -0
- package/dist/bin/verification-ledger.d.ts +3 -0
- package/dist/bin/verification-ledger.d.ts.map +1 -0
- package/dist/bin/verification-ledger.js +28 -0
- package/dist/config.d.ts +147 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +62 -0
- package/dist/index.d.ts +23 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +22 -0
- package/dist/lib/build-binaries.d.ts +45 -0
- package/dist/lib/build-binaries.d.ts.map +1 -0
- package/dist/lib/build-binaries.js +98 -0
- package/dist/lib/changelog-gate.d.ts +18 -0
- package/dist/lib/changelog-gate.d.ts.map +1 -0
- package/dist/lib/changelog-gate.js +32 -0
- package/dist/lib/coverage-gate.d.ts +25 -0
- package/dist/lib/coverage-gate.d.ts.map +1 -0
- package/dist/lib/coverage-gate.js +46 -0
- package/dist/lib/effects.d.ts +53 -0
- package/dist/lib/effects.d.ts.map +1 -0
- package/dist/lib/effects.js +78 -0
- package/dist/lib/load-config.d.ts +8 -0
- package/dist/lib/load-config.d.ts.map +1 -0
- package/dist/lib/load-config.js +15 -0
- package/dist/lib/package-install-check.d.ts +49 -0
- package/dist/lib/package-install-check.d.ts.map +1 -0
- package/dist/lib/package-install-check.js +66 -0
- package/dist/lib/per-job-green.d.ts +54 -0
- package/dist/lib/per-job-green.d.ts.map +1 -0
- package/dist/lib/per-job-green.js +247 -0
- package/dist/lib/post-build-smoke.d.ts +24 -0
- package/dist/lib/post-build-smoke.d.ts.map +1 -0
- package/dist/lib/post-build-smoke.js +33 -0
- package/dist/lib/publish-package.d.ts +45 -0
- package/dist/lib/publish-package.d.ts.map +1 -0
- package/dist/lib/publish-package.js +58 -0
- package/dist/lib/release-cut.d.ts +52 -0
- package/dist/lib/release-cut.d.ts.map +1 -0
- package/dist/lib/release-cut.js +120 -0
- package/dist/lib/sdk-pin-gate.d.ts +25 -0
- package/dist/lib/sdk-pin-gate.d.ts.map +1 -0
- package/dist/lib/sdk-pin-gate.js +146 -0
- package/dist/lib/sha256sums.d.ts +41 -0
- package/dist/lib/sha256sums.d.ts.map +1 -0
- package/dist/lib/sha256sums.js +53 -0
- package/dist/lib/verification-ledger.d.ts +30 -0
- package/dist/lib/verification-ledger.d.ts.map +1 -0
- package/dist/lib/verification-ledger.js +37 -0
- package/package.json +59 -0
package/README.md
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
# @pellux/goodvibes-toolchain
|
|
2
|
+
|
|
3
|
+
Shared GoodVibes CI/CD toolchain. One published home for the release, publish,
|
|
4
|
+
and verification scripts that used to live as 2–3 parallel copies across the
|
|
5
|
+
`goodvibes-tui`, `goodvibes-agent`, `goodvibes-webui`, and `goodvibes-sdk`
|
|
6
|
+
repos. Repo-specific values live in each repo's `toolchain.config.json`; the
|
|
7
|
+
behavior lives here, so every repo maintains one implementation instead of
|
|
8
|
+
several drifting ones.
|
|
9
|
+
|
|
10
|
+
## Tools
|
|
11
|
+
|
|
12
|
+
Each tool ships as a library policy function (with injectable I/O for testing)
|
|
13
|
+
and a thin CLI (`bin` entry):
|
|
14
|
+
|
|
15
|
+
- **sdk-pin-gate** — pin ⇄ lockfile ⇄ installed tri-agreement plus a non-npm
|
|
16
|
+
import sweep and an optional exports-map check.
|
|
17
|
+
- **build-binaries** — `bun build --compile` across a target matrix, with an
|
|
18
|
+
optional daemon leg and native-addon copy/cross-fetch, all config-driven.
|
|
19
|
+
- **release-cut** — prepare/bump/changelog/tag only. Never re-runs gates (CI
|
|
20
|
+
owns validation).
|
|
21
|
+
- **coverage-gate** — aggregate coverage ratchet against per-repo floors.
|
|
22
|
+
- **verification-ledger** — totals math and JSON/Markdown rendering of a
|
|
23
|
+
repo-collected verification inventory.
|
|
24
|
+
- **post-build-smoke** — boots a compiled binary and checks its version banner.
|
|
25
|
+
- **package-install-check** — static `npm pack` tarball + bin-shim policy check.
|
|
26
|
+
- **publish-package** — idempotent `npm publish` plus a propagation poll.
|
|
27
|
+
- **per-job-green** — verifies a commit's push-CI run concluded with every job
|
|
28
|
+
green, with a 503-resilient check-suites fallback. The by-reference
|
|
29
|
+
validation primitive.
|
|
30
|
+
- **changelog-gate** — asserts CHANGELOG carries a section for a version.
|
|
31
|
+
- **sha256sums** — generate/verify a `SHA256SUMS` manifest over release assets.
|
|
32
|
+
|
|
33
|
+
## Config
|
|
34
|
+
|
|
35
|
+
See `docs/release-and-publishing.md` in the SDK repo for the full
|
|
36
|
+
`toolchain.config.json` contract and per-repo examples. Import the
|
|
37
|
+
`ToolchainConfig` type from `@pellux/goodvibes-toolchain` for editor help.
|
|
38
|
+
|
|
39
|
+
## Usage
|
|
40
|
+
|
|
41
|
+
```ts
|
|
42
|
+
import { runSdkPinGate, realFsReader, loadToolchainConfig } from '@pellux/goodvibes-toolchain';
|
|
43
|
+
|
|
44
|
+
const config = loadToolchainConfig();
|
|
45
|
+
const results = runSdkPinGate(realFsReader(process.cwd()), config.sdkPin);
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
Every function accepts its effects (exec, fs, http, sleep, logger) as
|
|
49
|
+
parameters, so unit tests drive them with in-memory stubs — no network and no
|
|
50
|
+
real git mutations.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"build-binaries.d.ts","sourceRoot":"","sources":["../../src/bin/build-binaries.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { copyFileSync, existsSync, mkdirSync, mkdtempSync, readFileSync, rmSync } from 'node:fs';
|
|
3
|
+
import { tmpdir } from 'node:os';
|
|
4
|
+
import { join, resolve } from 'node:path';
|
|
5
|
+
import { loadToolchainConfig } from '../lib/load-config.js';
|
|
6
|
+
import { realExec, consoleLogger } from '../lib/effects.js';
|
|
7
|
+
import { resolveTargets, runBuildBinaries } from '../lib/build-binaries.js';
|
|
8
|
+
const root = process.cwd();
|
|
9
|
+
const config = loadToolchainConfig(root);
|
|
10
|
+
if (!config.build) {
|
|
11
|
+
consoleLogger.error('build-binaries: no `build` section in toolchain.config.json');
|
|
12
|
+
process.exit(1);
|
|
13
|
+
}
|
|
14
|
+
const build = config.build;
|
|
15
|
+
const nativeKey = `${process.platform === 'darwin' ? 'darwin' : process.platform}-${process.arch}`;
|
|
16
|
+
/** Copy the native addon beside the binary; same-host miss is fatal, cross-target miss fetches via npm pack + tar. */
|
|
17
|
+
function provideAddon(target, sameHost) {
|
|
18
|
+
if (!target.nativeAddonPackage || !target.nativeAddonFile)
|
|
19
|
+
return true;
|
|
20
|
+
const destDir = resolve(root, build.addonOutDir, target.nativeAddonPackage);
|
|
21
|
+
const dest = join(destDir, target.nativeAddonFile);
|
|
22
|
+
const source = resolve(root, 'node_modules', target.nativeAddonPackage, target.nativeAddonFile);
|
|
23
|
+
mkdirSync(destDir, { recursive: true });
|
|
24
|
+
if (existsSync(source)) {
|
|
25
|
+
copyFileSync(source, dest);
|
|
26
|
+
return true;
|
|
27
|
+
}
|
|
28
|
+
if (sameHost) {
|
|
29
|
+
consoleLogger.error(`[build-binaries] native addon missing for host target ${target.key}; run install`);
|
|
30
|
+
return false;
|
|
31
|
+
}
|
|
32
|
+
const versionPkg = resolve(root, 'node_modules', 'sqlite-vec', 'package.json');
|
|
33
|
+
if (!existsSync(versionPkg))
|
|
34
|
+
return false;
|
|
35
|
+
const version = JSON.parse(readFileSync(versionPkg, 'utf8')).version;
|
|
36
|
+
const tmp = mkdtempSync(join(tmpdir(), 'gv-addon-'));
|
|
37
|
+
try {
|
|
38
|
+
const pack = realExec('npm', ['pack', `${target.nativeAddonPackage}@${version}`, '--pack-destination', tmp]);
|
|
39
|
+
if (pack.status !== 0)
|
|
40
|
+
return false;
|
|
41
|
+
const tarball = pack.stdout.trim().split('\n').pop();
|
|
42
|
+
if (!tarball)
|
|
43
|
+
return false;
|
|
44
|
+
const untar = realExec('tar', ['-xzf', join(tmp, tarball), '-C', tmp]);
|
|
45
|
+
if (untar.status !== 0)
|
|
46
|
+
return false;
|
|
47
|
+
const extracted = join(tmp, 'package', target.nativeAddonFile);
|
|
48
|
+
if (!existsSync(extracted))
|
|
49
|
+
return false;
|
|
50
|
+
copyFileSync(extracted, dest);
|
|
51
|
+
return true;
|
|
52
|
+
}
|
|
53
|
+
finally {
|
|
54
|
+
rmSync(tmp, { recursive: true, force: true });
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
try {
|
|
58
|
+
const selection = resolveTargets(process.argv.slice(2), build, nativeKey);
|
|
59
|
+
const outcomes = runBuildBinaries({ cwd: root, config: build, selection, nativeKey, provideAddon, logger: consoleLogger });
|
|
60
|
+
const failed = outcomes.filter((o) => !o.ok);
|
|
61
|
+
consoleLogger.info(`build-binaries: ${outcomes.length - failed.length}/${outcomes.length} target(s) built`);
|
|
62
|
+
process.exit(failed.length > 0 ? 1 : 0);
|
|
63
|
+
}
|
|
64
|
+
catch (error) {
|
|
65
|
+
consoleLogger.error(`build-binaries: ${error instanceof Error ? error.message : String(error)}`);
|
|
66
|
+
process.exit(1);
|
|
67
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"changelog-gate.d.ts","sourceRoot":"","sources":["../../src/bin/changelog-gate.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { readFileSync } from 'node:fs';
|
|
3
|
+
import { resolve } from 'node:path';
|
|
4
|
+
import { consoleLogger } from '../lib/effects.js';
|
|
5
|
+
import { runChangelogGate } from '../lib/changelog-gate.js';
|
|
6
|
+
const root = process.cwd();
|
|
7
|
+
const versionArg = process.argv.find((a) => /^\d+\.\d+\.\d+/.test(a));
|
|
8
|
+
const version = versionArg ?? JSON.parse(readFileSync(resolve(root, 'package.json'), 'utf8')).version;
|
|
9
|
+
const headingArg = process.argv.includes('--plain') ? 'plain' : process.argv.includes('--bracket') ? 'bracket' : 'either';
|
|
10
|
+
const changelog = readFileSync(resolve(root, 'CHANGELOG.md'), 'utf8');
|
|
11
|
+
const result = runChangelogGate(changelog, version, headingArg);
|
|
12
|
+
consoleLogger.info(`changelog-gate: ${result.detail}`);
|
|
13
|
+
process.exit(result.ok ? 0 : 1);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"coverage-gate.d.ts","sourceRoot":"","sources":["../../src/bin/coverage-gate.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { loadToolchainConfig } from '../lib/load-config.js';
|
|
3
|
+
import { realExec, consoleLogger } from '../lib/effects.js';
|
|
4
|
+
import { evaluateCoverageGate } from '../lib/coverage-gate.js';
|
|
5
|
+
const root = process.cwd();
|
|
6
|
+
const config = loadToolchainConfig(root);
|
|
7
|
+
if (!config.coverage) {
|
|
8
|
+
consoleLogger.error('coverage-gate: no `coverage` section in toolchain.config.json');
|
|
9
|
+
process.exit(1);
|
|
10
|
+
}
|
|
11
|
+
const [bin, ...args] = config.coverage.command;
|
|
12
|
+
if (!bin) {
|
|
13
|
+
consoleLogger.error('coverage-gate: coverage.command is empty');
|
|
14
|
+
process.exit(1);
|
|
15
|
+
}
|
|
16
|
+
const run = realExec(bin, args, { cwd: root });
|
|
17
|
+
const result = evaluateCoverageGate(`${run.stdout}\n${run.stderr}`, config.coverage);
|
|
18
|
+
consoleLogger.info(`coverage-gate: ${result.detail}`);
|
|
19
|
+
process.exit(result.ok ? 0 : 1);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"package-install-check.d.ts","sourceRoot":"","sources":["../../src/bin/package-install-check.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { loadToolchainConfig } from '../lib/load-config.js';
|
|
3
|
+
import { consoleLogger } from '../lib/effects.js';
|
|
4
|
+
import { runPackageInstallCheck } from '../lib/package-install-check.js';
|
|
5
|
+
const root = process.cwd();
|
|
6
|
+
const config = loadToolchainConfig(root);
|
|
7
|
+
if (!config.publish) {
|
|
8
|
+
consoleLogger.error('package-install-check: no `publish` section in toolchain.config.json');
|
|
9
|
+
process.exit(1);
|
|
10
|
+
}
|
|
11
|
+
const result = runPackageInstallCheck({ cwd: root, config: config.publish, logger: consoleLogger });
|
|
12
|
+
for (const issue of result.issues)
|
|
13
|
+
consoleLogger.error(` - ${issue}`);
|
|
14
|
+
process.exit(result.ok ? 0 : 1);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"per-job-green.d.ts","sourceRoot":"","sources":["../../src/bin/per-job-green.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { appendFileSync } from 'node:fs';
|
|
3
|
+
import { realHttpGetJson, realSleep, consoleLogger } from '../lib/effects.js';
|
|
4
|
+
import { verifyPerJobGreen } from '../lib/per-job-green.js';
|
|
5
|
+
import { resolvePerJobGreenConfig } from '../config.js';
|
|
6
|
+
function argValue(flag) {
|
|
7
|
+
const idx = process.argv.indexOf(flag);
|
|
8
|
+
return idx !== -1 ? process.argv[idx + 1] : undefined;
|
|
9
|
+
}
|
|
10
|
+
const repoSlug = argValue('--repo') ?? process.env.GITHUB_REPOSITORY ?? '';
|
|
11
|
+
const [owner, repo] = repoSlug.split('/');
|
|
12
|
+
const sha = argValue('--sha') ?? process.env.GITHUB_SHA;
|
|
13
|
+
if (!owner || !repo || !sha) {
|
|
14
|
+
consoleLogger.error('per-job-green: --repo <owner/repo> (or GITHUB_REPOSITORY) and --sha (or GITHUB_SHA) are required');
|
|
15
|
+
process.exit(2);
|
|
16
|
+
}
|
|
17
|
+
function intArg(flag) {
|
|
18
|
+
const raw = argValue(flag);
|
|
19
|
+
if (raw === undefined)
|
|
20
|
+
return undefined;
|
|
21
|
+
const value = Number.parseInt(raw, 10);
|
|
22
|
+
if (!Number.isFinite(value) || value <= 0) {
|
|
23
|
+
consoleLogger.error(`per-job-green: ${flag} must be a positive integer (got '${raw}')`);
|
|
24
|
+
process.exit(2);
|
|
25
|
+
}
|
|
26
|
+
return value;
|
|
27
|
+
}
|
|
28
|
+
const retryAttempts = intArg('--retry-attempts');
|
|
29
|
+
const retryDelayMs = intArg('--retry-delay-ms');
|
|
30
|
+
// Size --deadline-ms UNDER the enclosing CI job's timeout cap so an exhausted
|
|
31
|
+
// wait produces the tool's honest named verdict (deadline-exceeded) instead of
|
|
32
|
+
// a raw job kill with no verdict at all.
|
|
33
|
+
const deadlineMs = intArg('--deadline-ms');
|
|
34
|
+
const config = resolvePerJobGreenConfig({
|
|
35
|
+
owner,
|
|
36
|
+
repo,
|
|
37
|
+
workflow: argValue('--workflow') ?? 'ci.yml',
|
|
38
|
+
event: argValue('--event') ?? 'push',
|
|
39
|
+
...(retryAttempts !== undefined ? { retryAttempts } : {}),
|
|
40
|
+
...(retryDelayMs !== undefined ? { retryDelayMs } : {}),
|
|
41
|
+
...(deadlineMs !== undefined ? { deadlineMs } : {}),
|
|
42
|
+
});
|
|
43
|
+
const token = process.env.GH_TOKEN ?? process.env.GITHUB_TOKEN;
|
|
44
|
+
const result = await verifyPerJobGreen({ http: realHttpGetJson, sleep: realSleep, logger: consoleLogger, now: () => Date.now(), ...(token ? { token } : {}) }, config, sha);
|
|
45
|
+
consoleLogger.info(`per-job-green: ${result.reason}`);
|
|
46
|
+
if (result.failures.length > 0)
|
|
47
|
+
consoleLogger.info(` non-green: ${result.failures.join(', ')}`);
|
|
48
|
+
if (result.ok && result.runId === null) {
|
|
49
|
+
consoleLogger.warn('per-job-green: verdict is green but the run id is UNRESOLVED — run_id output is empty; artifact restores keyed on it must fail fast rather than download from the wrong run');
|
|
50
|
+
}
|
|
51
|
+
if (process.env.GITHUB_OUTPUT) {
|
|
52
|
+
appendFileSync(process.env.GITHUB_OUTPUT, `run_id=${result.runId ?? ''}\nhead_sha=${result.headSha ?? ''}\nok=${result.ok}\n`);
|
|
53
|
+
}
|
|
54
|
+
process.exit(result.ok ? 0 : 1);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"post-build-smoke.d.ts","sourceRoot":"","sources":["../../src/bin/post-build-smoke.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { loadToolchainConfig } from '../lib/load-config.js';
|
|
3
|
+
import { consoleLogger } from '../lib/effects.js';
|
|
4
|
+
import { runPostBuildSmoke } from '../lib/post-build-smoke.js';
|
|
5
|
+
const root = process.cwd();
|
|
6
|
+
const config = loadToolchainConfig(root);
|
|
7
|
+
if (!config.smoke) {
|
|
8
|
+
consoleLogger.error('post-build-smoke: no `smoke` section in toolchain.config.json');
|
|
9
|
+
process.exit(1);
|
|
10
|
+
}
|
|
11
|
+
const binaryIdx = process.argv.indexOf('--binary');
|
|
12
|
+
const binary = binaryIdx !== -1 ? process.argv[binaryIdx + 1] : undefined;
|
|
13
|
+
const result = runPostBuildSmoke({ binary: binary ?? config.smoke.binaryDefault, config: config.smoke, logger: consoleLogger });
|
|
14
|
+
process.exit(result.ok ? 0 : 1);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"publish-package.d.ts","sourceRoot":"","sources":["../../src/bin/publish-package.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { readFileSync } from 'node:fs';
|
|
3
|
+
import { resolve } from 'node:path';
|
|
4
|
+
import { loadToolchainConfig } from '../lib/load-config.js';
|
|
5
|
+
import { consoleLogger } from '../lib/effects.js';
|
|
6
|
+
import { runPublishPackage, pollPropagation } from '../lib/publish-package.js';
|
|
7
|
+
const root = process.cwd();
|
|
8
|
+
const config = loadToolchainConfig(root);
|
|
9
|
+
if (!config.publish) {
|
|
10
|
+
consoleLogger.error('publish-package: no `publish` section in toolchain.config.json');
|
|
11
|
+
process.exit(1);
|
|
12
|
+
}
|
|
13
|
+
const version = JSON.parse(readFileSync(resolve(root, 'package.json'), 'utf8')).version;
|
|
14
|
+
const dryRun = process.argv.includes('--dry-run');
|
|
15
|
+
const registry = process.env.GOODVIBES_PUBLISH_REGISTRY ?? config.publish.defaultRegistry;
|
|
16
|
+
const result = runPublishPackage({ cwd: root, name: config.publish.packageName, version, registry, dryRun, logger: consoleLogger });
|
|
17
|
+
consoleLogger.info(`publish-package: ${result.detail}`);
|
|
18
|
+
if (!result.ok)
|
|
19
|
+
process.exit(1);
|
|
20
|
+
if (!dryRun && !result.skipped && process.argv.includes('--poll')) {
|
|
21
|
+
const prop = await pollPropagation({ name: config.publish.packageName, version, registry, logger: consoleLogger });
|
|
22
|
+
consoleLogger.info(`publish-package: ${prop.detail}`);
|
|
23
|
+
process.exit(prop.ok ? 0 : 1);
|
|
24
|
+
}
|
|
25
|
+
process.exit(0);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"release-cut.d.ts","sourceRoot":"","sources":["../../src/bin/release-cut.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { readFileSync } from 'node:fs';
|
|
3
|
+
import { resolve } from 'node:path';
|
|
4
|
+
import { loadToolchainConfig } from '../lib/load-config.js';
|
|
5
|
+
import { consoleLogger } from '../lib/effects.js';
|
|
6
|
+
import { runReleaseCut } from '../lib/release-cut.js';
|
|
7
|
+
const root = process.cwd();
|
|
8
|
+
const config = loadToolchainConfig(root);
|
|
9
|
+
if (!config.releaseCut) {
|
|
10
|
+
consoleLogger.error('release-cut: no `releaseCut` section in toolchain.config.json');
|
|
11
|
+
process.exit(1);
|
|
12
|
+
}
|
|
13
|
+
const bump = process.argv.includes('--major') ? 'major' : process.argv.includes('--minor') ? 'minor' : 'patch';
|
|
14
|
+
const dryRun = process.argv.includes('--dry-run');
|
|
15
|
+
const notesIdx = process.argv.indexOf('--notes-file');
|
|
16
|
+
const notes = notesIdx !== -1 && process.argv[notesIdx + 1]
|
|
17
|
+
? readFileSync(resolve(root, process.argv[notesIdx + 1]), 'utf8').split('\n').filter((l) => l.trim().length > 0)
|
|
18
|
+
: [];
|
|
19
|
+
try {
|
|
20
|
+
const result = runReleaseCut({ cwd: root, bump, config: config.releaseCut, notes, dryRun, logger: consoleLogger });
|
|
21
|
+
consoleLogger.info(`release-cut: prepared ${result.tag}${result.committed ? ' (committed + tagged)' : ' (dry-run)'}`);
|
|
22
|
+
process.exit(0);
|
|
23
|
+
}
|
|
24
|
+
catch (error) {
|
|
25
|
+
consoleLogger.error(`release-cut: ${error instanceof Error ? error.message : String(error)}`);
|
|
26
|
+
process.exit(1);
|
|
27
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sdk-pin-gate.d.ts","sourceRoot":"","sources":["../../src/bin/sdk-pin-gate.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { loadToolchainConfig } from '../lib/load-config.js';
|
|
3
|
+
import { realFsReader, consoleLogger } from '../lib/effects.js';
|
|
4
|
+
import { runSdkPinGate } from '../lib/sdk-pin-gate.js';
|
|
5
|
+
const root = process.cwd();
|
|
6
|
+
const config = loadToolchainConfig(root);
|
|
7
|
+
const results = runSdkPinGate(realFsReader(root), config.sdkPin);
|
|
8
|
+
let failed = 0;
|
|
9
|
+
for (const result of results) {
|
|
10
|
+
consoleLogger.info(`${result.ok ? 'PASS' : 'FAIL'} ${result.id} — ${result.detail}`);
|
|
11
|
+
if (!result.ok)
|
|
12
|
+
failed += 1;
|
|
13
|
+
}
|
|
14
|
+
consoleLogger.info(`sdk-pin-gate: ${results.length - failed}/${results.length} gates passed`);
|
|
15
|
+
process.exit(failed > 0 ? 1 : 0);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sha256sums.d.ts","sourceRoot":"","sources":["../../src/bin/sha256sums.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { createHash } from 'node:crypto';
|
|
3
|
+
import { existsSync, readFileSync, writeFileSync } from 'node:fs';
|
|
4
|
+
import { basename, resolve } from 'node:path';
|
|
5
|
+
import { consoleLogger } from '../lib/effects.js';
|
|
6
|
+
import { generateSha256Sums, verifySha256Sums } from '../lib/sha256sums.js';
|
|
7
|
+
const root = process.cwd();
|
|
8
|
+
const readBytes = (path) => (existsSync(resolve(root, path)) ? new Uint8Array(readFileSync(resolve(root, path))) : null);
|
|
9
|
+
const hashBytes = (bytes) => createHash('sha256').update(bytes).digest('hex');
|
|
10
|
+
function argValue(flag) {
|
|
11
|
+
const idx = process.argv.indexOf(flag);
|
|
12
|
+
return idx !== -1 ? process.argv[idx + 1] : undefined;
|
|
13
|
+
}
|
|
14
|
+
const verifyPath = argValue('--verify');
|
|
15
|
+
if (verifyPath) {
|
|
16
|
+
const result = verifySha256Sums(readFileSync(resolve(root, verifyPath), 'utf8'), readBytes, hashBytes);
|
|
17
|
+
if (!result.ok) {
|
|
18
|
+
if (result.missing.length > 0)
|
|
19
|
+
consoleLogger.error(`sha256sums: missing ${result.missing.join(', ')}`);
|
|
20
|
+
if (result.mismatched.length > 0)
|
|
21
|
+
consoleLogger.error(`sha256sums: mismatched ${result.mismatched.join(', ')}`);
|
|
22
|
+
process.exit(1);
|
|
23
|
+
}
|
|
24
|
+
consoleLogger.info('sha256sums: all assets verified');
|
|
25
|
+
process.exit(0);
|
|
26
|
+
}
|
|
27
|
+
const outPath = argValue('--out') ?? 'SHA256SUMS.txt';
|
|
28
|
+
const assets = process.argv.slice(2).filter((a) => !a.startsWith('--') && a !== outPath && a !== verifyPath);
|
|
29
|
+
const entries = assets.map((path) => ({ name: basename(path), path }));
|
|
30
|
+
const result = generateSha256Sums(entries, readBytes, hashBytes);
|
|
31
|
+
if (!result.ok) {
|
|
32
|
+
consoleLogger.error(`sha256sums: refusing to write — missing asset(s): ${result.missing.join(', ')}`);
|
|
33
|
+
process.exit(1);
|
|
34
|
+
}
|
|
35
|
+
writeFileSync(resolve(root, outPath), result.manifest);
|
|
36
|
+
consoleLogger.info(`sha256sums: wrote ${entries.length} checksum(s) to ${outPath}`);
|
|
37
|
+
process.exit(0);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"verification-ledger.d.ts","sourceRoot":"","sources":["../../src/bin/verification-ledger.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { readFileSync, writeFileSync } from 'node:fs';
|
|
3
|
+
import { resolve } from 'node:path';
|
|
4
|
+
import { consoleLogger } from '../lib/effects.js';
|
|
5
|
+
import { renderLedgerJson, renderLedgerMarkdown } from '../lib/verification-ledger.js';
|
|
6
|
+
function argValue(flag) {
|
|
7
|
+
const idx = process.argv.indexOf(flag);
|
|
8
|
+
return idx !== -1 ? process.argv[idx + 1] : undefined;
|
|
9
|
+
}
|
|
10
|
+
const root = process.cwd();
|
|
11
|
+
const areasPath = argValue('--areas');
|
|
12
|
+
if (!areasPath) {
|
|
13
|
+
consoleLogger.error('verification-ledger: --areas <file.json> is required (the repo-collected area inventory)');
|
|
14
|
+
process.exit(1);
|
|
15
|
+
}
|
|
16
|
+
const areas = JSON.parse(readFileSync(resolve(root, areasPath), 'utf8'));
|
|
17
|
+
const json = process.argv.includes('--json');
|
|
18
|
+
const rendered = json ? renderLedgerJson(areas) : renderLedgerMarkdown(areas);
|
|
19
|
+
const outDir = argValue('--out');
|
|
20
|
+
if (outDir) {
|
|
21
|
+
writeFileSync(resolve(root, outDir, 'verification-ledger.json'), renderLedgerJson(areas));
|
|
22
|
+
writeFileSync(resolve(root, outDir, 'verification-ledger.md'), renderLedgerMarkdown(areas));
|
|
23
|
+
consoleLogger.info(`verification-ledger: wrote verification-ledger.{json,md} to ${outDir}`);
|
|
24
|
+
}
|
|
25
|
+
else {
|
|
26
|
+
consoleLogger.info(rendered);
|
|
27
|
+
}
|
|
28
|
+
process.exit(0);
|
package/dist/config.d.ts
ADDED
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* toolchain.config contract.
|
|
3
|
+
*
|
|
4
|
+
* Every GoodVibes repo keeps a small `toolchain.config.json` (or a
|
|
5
|
+
* `toolchain.config.ts` that default-exports a {@link ToolchainConfig}) at its
|
|
6
|
+
* root. The published `@pellux/goodvibes-toolchain` package holds the behavior;
|
|
7
|
+
* the config holds the repo-specific values (package names, binary matrix,
|
|
8
|
+
* coverage floors, publish ordering) so one implementation serves every repo.
|
|
9
|
+
*
|
|
10
|
+
* The full contract and per-repo examples are documented in
|
|
11
|
+
* `docs/release-and-publishing.md` (SDK repo).
|
|
12
|
+
*/
|
|
13
|
+
/** Where the SDK dependency pin lives and how the pin gate is parameterized. */
|
|
14
|
+
export interface SdkPinConfig {
|
|
15
|
+
/** npm name of the pinned SDK package, e.g. `@pellux/goodvibes-sdk`. */
|
|
16
|
+
readonly sdkPackage: string;
|
|
17
|
+
/** Manifest group the pin is read from. TUI/webui use `dependencies`; the agent bundles the SDK as a `devDependencies` pin. */
|
|
18
|
+
readonly pinSource: 'dependencies' | 'devDependencies';
|
|
19
|
+
/** Lockfile whose text must resolve the pin, e.g. `bun.lock`. */
|
|
20
|
+
readonly lockfile: string;
|
|
21
|
+
/** Path (relative to repo root) of the dev-link overlay marker whose presence blocks a cut. */
|
|
22
|
+
readonly overlayMarker: string;
|
|
23
|
+
/** Source roots scanned for non-npm SDK imports. */
|
|
24
|
+
readonly sourceRoots: readonly string[];
|
|
25
|
+
/** When true, also assert every SDK subpath import resolves to a key in the installed package's `exports` map (webui browser-bundle gate). */
|
|
26
|
+
readonly enforceExportsMap: boolean;
|
|
27
|
+
}
|
|
28
|
+
/** One row of the compiled-binary build matrix. */
|
|
29
|
+
export interface BinaryTarget {
|
|
30
|
+
/** Canonical key, e.g. `linux-x64`. */
|
|
31
|
+
readonly key: string;
|
|
32
|
+
/** `bun build --compile --target` value, e.g. `bun-linux-x64`. */
|
|
33
|
+
readonly bunTarget: string;
|
|
34
|
+
/** Primary binary output name, e.g. `goodvibes-linux-x64`. */
|
|
35
|
+
readonly appArtifact: string;
|
|
36
|
+
/** Optional second binary (the TUI daemon leg), e.g. `goodvibes-daemon-linux-x64`. */
|
|
37
|
+
readonly daemonArtifact?: string;
|
|
38
|
+
/** Optional native-addon package that stays `--external` and is copied beside the binary, e.g. `sqlite-vec-linux-x64`. */
|
|
39
|
+
readonly nativeAddonPackage?: string;
|
|
40
|
+
/** Native-addon filename, e.g. `vec0.so` / `vec0.dylib`. */
|
|
41
|
+
readonly nativeAddonFile?: string;
|
|
42
|
+
}
|
|
43
|
+
/** build-binaries parameters. */
|
|
44
|
+
export interface BuildConfig {
|
|
45
|
+
/** Primary compile entrypoint, e.g. `src/main.ts`. */
|
|
46
|
+
readonly appEntrypoint: string;
|
|
47
|
+
/** Optional daemon compile entrypoint (present only for repos with a daemon leg), e.g. `src/daemon/cli.ts`. */
|
|
48
|
+
readonly daemonEntrypoint?: string;
|
|
49
|
+
/** Output directory for binaries, e.g. `dist`. */
|
|
50
|
+
readonly outDir: string;
|
|
51
|
+
/** Directory for copied native addons, e.g. `dist/lib`. */
|
|
52
|
+
readonly addonOutDir: string;
|
|
53
|
+
/** The build matrix. */
|
|
54
|
+
readonly targets: readonly BinaryTarget[];
|
|
55
|
+
/** Commands run once before compiling (each a full argv), e.g. `[["bun","run","scripts/prebuild.ts"]]`. */
|
|
56
|
+
readonly prebuild: readonly (readonly string[])[];
|
|
57
|
+
}
|
|
58
|
+
/** coverage-gate ratchet floors and the coverage command. */
|
|
59
|
+
export interface CoverageConfig {
|
|
60
|
+
readonly funcsFloor: number;
|
|
61
|
+
readonly linesFloor: number;
|
|
62
|
+
/** argv that emits Bun's text coverage table, e.g. `["bun","test","--coverage","src"]`. */
|
|
63
|
+
readonly command: readonly string[];
|
|
64
|
+
}
|
|
65
|
+
/** post-build-smoke parameters. */
|
|
66
|
+
export interface SmokeConfig {
|
|
67
|
+
/** Expected `--version` banner prefix, e.g. `goodvibes-agent `. */
|
|
68
|
+
readonly bannerPrefix: string;
|
|
69
|
+
/** Substrings whose presence in output signals a packaging failure, e.g. `["sqlite-vec","$bunfs/root"]`. */
|
|
70
|
+
readonly forbiddenStrings: readonly string[];
|
|
71
|
+
/**
|
|
72
|
+
* Default binary path when `--binary` is not passed. Serves LOCAL CLI runs
|
|
73
|
+
* only — CI matrix legs each build a different suffixed artifact, so the
|
|
74
|
+
* reusable binary-matrix workflow always passes the leg's own `--binary`.
|
|
75
|
+
*/
|
|
76
|
+
readonly binaryDefault: string;
|
|
77
|
+
}
|
|
78
|
+
/** release-cut parameters (prepare/bump/changelog/tag only — never re-runs gates). */
|
|
79
|
+
export interface ReleaseCutConfig {
|
|
80
|
+
/** Branch a cut is allowed from, e.g. `main`. */
|
|
81
|
+
readonly branch: string;
|
|
82
|
+
/** Extra `package.json` paths (beyond root) whose version is stamped, e.g. platform-package manifests. */
|
|
83
|
+
readonly versionFiles: readonly string[];
|
|
84
|
+
/** Commands that sync generated version surfaces after the bump, e.g. `[["bun","run","scripts/prebuild.ts"]]`. */
|
|
85
|
+
readonly syncCommands: readonly (readonly string[])[];
|
|
86
|
+
/** Paths staged into the release commit. */
|
|
87
|
+
readonly commitPaths: readonly string[];
|
|
88
|
+
/** `bracket` → `## [x.y.z] - DATE`; `plain` → `## x.y.z - DATE`. */
|
|
89
|
+
readonly changelogHeading: 'bracket' | 'plain';
|
|
90
|
+
/** `first-separator` inserts the new section after the first `---`; `top` prepends above all sections. */
|
|
91
|
+
readonly changelogInsertMarker: 'first-separator' | 'top';
|
|
92
|
+
}
|
|
93
|
+
/** publish-package parameters. */
|
|
94
|
+
export interface PublishPackageConfig {
|
|
95
|
+
readonly packageName: string;
|
|
96
|
+
readonly defaultRegistry: string;
|
|
97
|
+
/** Tarball paths that must be present (all of them). */
|
|
98
|
+
readonly requiredTarballPaths: readonly string[];
|
|
99
|
+
/** Tarball path prefixes that must be absent. */
|
|
100
|
+
readonly forbiddenTarballPrefixes: readonly string[];
|
|
101
|
+
/** Hard cap on unpacked tarball size in bytes. */
|
|
102
|
+
readonly maxTarballBytes: number;
|
|
103
|
+
}
|
|
104
|
+
/** per-job-green parameters. */
|
|
105
|
+
export interface PerJobGreenConfig {
|
|
106
|
+
readonly owner: string;
|
|
107
|
+
readonly repo: string;
|
|
108
|
+
/** Workflow file whose run is verified, e.g. `ci.yml`. */
|
|
109
|
+
readonly workflow: string;
|
|
110
|
+
/** Triggering event to match, e.g. `push`. */
|
|
111
|
+
readonly event: string;
|
|
112
|
+
readonly pollIntervalMs: number;
|
|
113
|
+
readonly deadlineMs: number;
|
|
114
|
+
/** Bounded retry attempts applied to EVERY GitHub API call before its status is treated as final. */
|
|
115
|
+
readonly retryAttempts: number;
|
|
116
|
+
/** Sleep between retry attempts. */
|
|
117
|
+
readonly retryDelayMs: number;
|
|
118
|
+
}
|
|
119
|
+
/** The complete repo config. All sections are optional so a repo declares only the tools it uses. */
|
|
120
|
+
export interface ToolchainConfig {
|
|
121
|
+
/** npm name of the repo's primary package, e.g. `@pellux/goodvibes-tui`. */
|
|
122
|
+
readonly packageName: string;
|
|
123
|
+
readonly sdkPin?: SdkPinConfig;
|
|
124
|
+
readonly build?: BuildConfig;
|
|
125
|
+
readonly coverage?: CoverageConfig;
|
|
126
|
+
readonly smoke?: SmokeConfig;
|
|
127
|
+
readonly releaseCut?: ReleaseCutConfig;
|
|
128
|
+
readonly publish?: PublishPackageConfig;
|
|
129
|
+
readonly perJobGreen?: PerJobGreenConfig;
|
|
130
|
+
}
|
|
131
|
+
export declare const DEFAULT_SDK_PACKAGE = "@pellux/goodvibes-sdk";
|
|
132
|
+
export declare const DEFAULT_LOCKFILE = "bun.lock";
|
|
133
|
+
export declare const DEFAULT_REGISTRY = "https://registry.npmjs.org";
|
|
134
|
+
export declare const DEFAULT_POLL_INTERVAL_MS = 20000;
|
|
135
|
+
export declare const DEFAULT_DEADLINE_MS = 1800000;
|
|
136
|
+
export declare const DEFAULT_RETRY_ATTEMPTS = 8;
|
|
137
|
+
export declare const DEFAULT_RETRY_DELAY_MS = 7000;
|
|
138
|
+
/** Fill an SdkPinConfig with the conventional defaults for any missing field. */
|
|
139
|
+
export declare function resolveSdkPinConfig(partial: Partial<SdkPinConfig> | undefined): SdkPinConfig;
|
|
140
|
+
/** Fill a PerJobGreenConfig from a partial plus required identity fields. */
|
|
141
|
+
export declare function resolvePerJobGreenConfig(partial: Partial<PerJobGreenConfig> & Pick<PerJobGreenConfig, 'owner' | 'repo'>): PerJobGreenConfig;
|
|
142
|
+
/**
|
|
143
|
+
* Parse a JSON toolchain config. Kept separate from disk I/O so callers can
|
|
144
|
+
* validate a config object from any source (file, env, test fixture).
|
|
145
|
+
*/
|
|
146
|
+
export declare function parseToolchainConfig(raw: string): ToolchainConfig;
|
|
147
|
+
//# sourceMappingURL=config.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"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"}
|