From 9fc257957abdcd86fb60c0f8e4bfcd979f1ed8c6 Mon Sep 17 00:00:00 2001 From: Miya Date: Fri, 17 Jul 2026 13:33:23 +0200 Subject: [PATCH 1/3] ci: fix publish verification matrix --- .github/workflows/verify-publish.yml | 13 ++++-- package.json | 2 +- scripts/release-workflows.test.mjs | 59 ++++++++++++++++++++++++++++ 3 files changed, 69 insertions(+), 5 deletions(-) create mode 100644 scripts/release-workflows.test.mjs diff --git a/.github/workflows/verify-publish.yml b/.github/workflows/verify-publish.yml index 8d2a4609..1fda5860 100644 --- a/.github/workflows/verify-publish.yml +++ b/.github/workflows/verify-publish.yml @@ -15,12 +15,18 @@ on: type: choice options: - '@agentworkforce/events' + - '@agentworkforce/persona-kit' + - '@agentworkforce/runtime' - '@agentworkforce/compose' - - '@agentworkforce/cli' - - 'agentworkforce' + - '@agentworkforce/delivery' - '@agentworkforce/workload-router' - - '@agentworkforce/persona-kit' + - '@agentworkforce/deploy' - '@agentworkforce/review-kit' + - '@agentworkforce/mcp-workforce' + - '@agentworkforce/daytona-runner' + - '@agentworkforce/local-surface' + - '@agentworkforce/cli' + - 'agentworkforce' version: description: 'Version to verify (defaults to the "latest" dist-tag)' required: false @@ -104,7 +110,6 @@ jobs: const mod = await import('./node_modules/@agentworkforce/cli/dist/cli.js'); assert.equal(typeof mod.main, 'function'); - assert.equal(typeof mod.CLI_VERSION, 'string'); console.log(`scoped CLI package smoke passed for ${pkg.name}@${pkg.version}`); EOF diff --git a/package.json b/package.json index 1f7e37bc..3771cff7 100644 --- a/package.json +++ b/package.json @@ -33,7 +33,7 @@ "dev:cli": "node packages/cli/dist/cli.js", "typecheck": "pnpm -r typecheck && pnpm run typecheck:examples", "typecheck:examples": "tsc -p examples/tsconfig.json --noEmit", - "test": "pnpm -r test", + "test": "node --test scripts/release-workflows.test.mjs && pnpm -r test", "lint": "pnpm -r lint", "check": "pnpm run lint && pnpm run typecheck && pnpm run test" } diff --git a/scripts/release-workflows.test.mjs b/scripts/release-workflows.test.mjs new file mode 100644 index 00000000..b273dea4 --- /dev/null +++ b/scripts/release-workflows.test.mjs @@ -0,0 +1,59 @@ +import assert from 'node:assert/strict'; +import { readFileSync } from 'node:fs'; +import test from 'node:test'; + +const publishWorkflow = readFileSync('.github/workflows/publish.yml', 'utf8'); +const verifyWorkflow = readFileSync('.github/workflows/verify-publish.yml', 'utf8'); + +function publishPackageNames() { + const match = publishWorkflow.match(/echo "packages=([^"]+)"/); + assert.ok(match, 'publish workflow must declare its package targets'); + + return match[1].split(/\s+/).map((directory) => { + const packageJson = JSON.parse(readFileSync(`packages/${directory}/package.json`, 'utf8')); + return packageJson.name; + }); +} + +function verifyPackageChoices() { + const lines = verifyWorkflow.split('\n'); + const packageInput = lines.findIndex((line) => line === ' package:'); + assert.notEqual(packageInput, -1, 'verify workflow must declare the package input'); + + const options = lines.findIndex( + (line, index) => index > packageInput && line === ' options:' + ); + assert.notEqual(options, -1, 'verify package input must declare choices'); + + const choices = []; + for (const line of lines.slice(options + 1)) { + const match = line.match(/^ - '([^']+)'$/); + if (!match) break; + choices.push(match[1]); + } + return choices; +} + +test('Verify Publish exposes every lockstep package exactly once', () => { + const published = publishPackageNames(); + const verified = verifyPackageChoices(); + + assert.equal(new Set(published).size, published.length, 'publish targets must be unique'); + assert.equal(new Set(verified).size, verified.length, 'verify choices must be unique'); + assert.deepEqual(verified, published); +}); + +test('scoped CLI verification checks only the supported thin-entry contract', () => { + const match = verifyWorkflow.match( + /- name: Scoped CLI package smoke test([\s\S]*?)\n - name: Library smoke test/ + ); + assert.ok(match, 'scoped CLI smoke step must exist'); + + const step = match[1]; + assert.match(step, /assert\.equal\(pkg\.name, '@agentworkforce\/cli'\)/); + assert.match(step, /assert\.equal\(pkg\.version, '\$\{\{ steps\.resolve\.outputs\.version \}\}'\)/); + assert.match(step, /assert\.equal\(pkg\.bin, undefined\)/); + assert.match(step, /@agentworkforce\/cli\/dist\/cli\.js/); + assert.match(step, /assert\.equal\(typeof mod\.main, 'function'\)/); + assert.doesNotMatch(step, /CLI_VERSION|cli-impl/); +}); From 12d2c0eed729595533bcce6c6aa8e2c10bb0c0aa Mon Sep 17 00:00:00 2001 From: Miya Date: Fri, 17 Jul 2026 13:34:57 +0200 Subject: [PATCH 2/3] test: harden release workflow parsing --- scripts/release-workflows.test.mjs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/release-workflows.test.mjs b/scripts/release-workflows.test.mjs index b273dea4..0727e476 100644 --- a/scripts/release-workflows.test.mjs +++ b/scripts/release-workflows.test.mjs @@ -9,14 +9,14 @@ function publishPackageNames() { const match = publishWorkflow.match(/echo "packages=([^"]+)"/); assert.ok(match, 'publish workflow must declare its package targets'); - return match[1].split(/\s+/).map((directory) => { + return match[1].trim().split(/\s+/).map((directory) => { const packageJson = JSON.parse(readFileSync(`packages/${directory}/package.json`, 'utf8')); return packageJson.name; }); } function verifyPackageChoices() { - const lines = verifyWorkflow.split('\n'); + const lines = verifyWorkflow.replaceAll('\r\n', '\n').split('\n'); const packageInput = lines.findIndex((line) => line === ' package:'); assert.notEqual(packageInput, -1, 'verify workflow must declare the package input'); From 886a73fa252a5fddb5272006d9b3b5ab6fb43e2d Mon Sep 17 00:00:00 2001 From: Miya Date: Fri, 17 Jul 2026 13:36:05 +0200 Subject: [PATCH 3/3] test: cover release workflow formatting variants --- scripts/release-workflows.test.mjs | 25 ++++++++++++++++++++----- 1 file changed, 20 insertions(+), 5 deletions(-) diff --git a/scripts/release-workflows.test.mjs b/scripts/release-workflows.test.mjs index 0727e476..6969c11a 100644 --- a/scripts/release-workflows.test.mjs +++ b/scripts/release-workflows.test.mjs @@ -5,18 +5,22 @@ import test from 'node:test'; const publishWorkflow = readFileSync('.github/workflows/publish.yml', 'utf8'); const verifyWorkflow = readFileSync('.github/workflows/verify-publish.yml', 'utf8'); -function publishPackageNames() { - const match = publishWorkflow.match(/echo "packages=([^"]+)"/); +function publishTargetDirectories(workflow) { + const match = workflow.match(/echo "packages=([^"]+)"/); assert.ok(match, 'publish workflow must declare its package targets'); - return match[1].trim().split(/\s+/).map((directory) => { + return match[1].trim().split(/\s+/); +} + +function publishPackageNames(workflow = publishWorkflow) { + return publishTargetDirectories(workflow).map((directory) => { const packageJson = JSON.parse(readFileSync(`packages/${directory}/package.json`, 'utf8')); return packageJson.name; }); } -function verifyPackageChoices() { - const lines = verifyWorkflow.replaceAll('\r\n', '\n').split('\n'); +function verifyPackageChoices(workflow = verifyWorkflow) { + const lines = workflow.replaceAll('\r\n', '\n').split('\n'); const packageInput = lines.findIndex((line) => line === ' package:'); assert.notEqual(packageInput, -1, 'verify workflow must declare the package input'); @@ -43,6 +47,17 @@ test('Verify Publish exposes every lockstep package exactly once', () => { assert.deepEqual(verified, published); }); +test('release workflow parsing tolerates CRLF and target whitespace', () => { + assert.deepEqual(publishTargetDirectories('echo "packages= events persona-kit "'), [ + 'events', + 'persona-kit', + ]); + assert.deepEqual( + verifyPackageChoices(verifyWorkflow.replaceAll('\n', '\r\n')), + verifyPackageChoices() + ); +}); + test('scoped CLI verification checks only the supported thin-entry contract', () => { const match = verifyWorkflow.match( /- name: Scoped CLI package smoke test([\s\S]*?)\n - name: Library smoke test/