diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index ab02297..40df43a 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -35,5 +35,31 @@ jobs: node --check bin/multiagent-safety.js npm pack --dry-run + - name: Resolve package metadata + id: pkg + run: | + echo "name=$(node -p "require('./package.json').name")" >> "$GITHUB_OUTPUT" + echo "version=$(node -p "require('./package.json').version")" >> "$GITHUB_OUTPUT" + + - name: Check npm registry for current version + id: registry + env: + PACKAGE_NAME: ${{ steps.pkg.outputs.name }} + PACKAGE_VERSION: ${{ steps.pkg.outputs.version }} + run: | + if npm view "${PACKAGE_NAME}@${PACKAGE_VERSION}" version >/dev/null 2>&1; then + echo "already_published=true" >> "$GITHUB_OUTPUT" + else + echo "already_published=false" >> "$GITHUB_OUTPUT" + fi + - name: Publish with provenance + if: ${{ steps.registry.outputs.already_published != 'true' }} run: npm publish --provenance --access public + + - name: Skip already-published npm version + if: ${{ steps.registry.outputs.already_published == 'true' }} + env: + PACKAGE_NAME: ${{ steps.pkg.outputs.name }} + PACKAGE_VERSION: ${{ steps.pkg.outputs.version }} + run: echo "${PACKAGE_NAME}@${PACKAGE_VERSION} is already on npm; skipping publish." diff --git a/openspec/changes/agent-codex-make-release-workflow-idempotent-when-ve-2026-04-21-16-30/.openspec.yaml b/openspec/changes/agent-codex-make-release-workflow-idempotent-when-ve-2026-04-21-16-30/.openspec.yaml new file mode 100644 index 0000000..4b8c565 --- /dev/null +++ b/openspec/changes/agent-codex-make-release-workflow-idempotent-when-ve-2026-04-21-16-30/.openspec.yaml @@ -0,0 +1,2 @@ +schema: spec-driven +created: 2026-04-21 diff --git a/openspec/changes/agent-codex-make-release-workflow-idempotent-when-ve-2026-04-21-16-30/proposal.md b/openspec/changes/agent-codex-make-release-workflow-idempotent-when-ve-2026-04-21-16-30/proposal.md new file mode 100644 index 0000000..fe6eb1a --- /dev/null +++ b/openspec/changes/agent-codex-make-release-workflow-idempotent-when-ve-2026-04-21-16-30/proposal.md @@ -0,0 +1,14 @@ +## Why + +- The failing `Release to npm (provenance)` runs in GitHub Actions are stale release-tag executions; current `main` is green locally, and `@imdeadpool/guardex@7.0.16` is already published on npm. +- Today the workflow always executes `npm publish`, so manual verification runs or backfill GitHub releases for an already-published version would fail even when the code under test is healthy. + +## What Changes + +- Teach `.github/workflows/release.yml` to resolve the package name/version from `package.json`, check npm for that exact version, and skip `npm publish` when it already exists. +- Add a metadata regression test in `test/metadata.test.js` that locks the workflow's already-published skip behavior. + +## Impact + +- Makes the release workflow idempotent for already-published versions while preserving the existing verify steps and normal publish path for new versions. +- Lets maintainers run `workflow_dispatch` on a healthy `main` release commit without re-breaking on an already-published npm version. diff --git a/openspec/changes/agent-codex-make-release-workflow-idempotent-when-ve-2026-04-21-16-30/specs/release-workflow/spec.md b/openspec/changes/agent-codex-make-release-workflow-idempotent-when-ve-2026-04-21-16-30/specs/release-workflow/spec.md new file mode 100644 index 0000000..0e3ae90 --- /dev/null +++ b/openspec/changes/agent-codex-make-release-workflow-idempotent-when-ve-2026-04-21-16-30/specs/release-workflow/spec.md @@ -0,0 +1,10 @@ +## ADDED Requirements + +### Requirement: Release workflow skips already-published package versions +The `Release to npm (provenance)` workflow SHALL verify the package on every run, but it SHALL skip `npm publish` when the exact `package.json` version is already present on npm. + +#### Scenario: workflow_dispatch on an already-published version +- **GIVEN** the workflow is running against a commit whose `package.json` version already exists on npm +- **WHEN** the verify steps complete successfully +- **THEN** the workflow SHALL detect that published version before the publish step +- **AND** it SHALL report that publish is being skipped instead of failing on `npm publish`. diff --git a/openspec/changes/agent-codex-make-release-workflow-idempotent-when-ve-2026-04-21-16-30/tasks.md b/openspec/changes/agent-codex-make-release-workflow-idempotent-when-ve-2026-04-21-16-30/tasks.md new file mode 100644 index 0000000..b326bbc --- /dev/null +++ b/openspec/changes/agent-codex-make-release-workflow-idempotent-when-ve-2026-04-21-16-30/tasks.md @@ -0,0 +1,21 @@ +## 1. Specification + +- [x] 1.1 Finalize proposal scope and acceptance criteria for `agent-codex-make-release-workflow-idempotent-when-ve-2026-04-21-16-30`. +- [x] 1.2 Define normative requirements in `specs/release-workflow/spec.md`. + +## 2. Implementation + +- [x] 2.1 Update `.github/workflows/release.yml` so it skips `npm publish` when the current package version already exists on npm. +- [x] 2.2 Add/update `test/metadata.test.js` regression coverage for the release-workflow skip behavior. + +## 3. Verification + +- [x] 3.1 Run `npm test`, `node --check bin/multiagent-safety.js`, and `npm pack --dry-run`. Result: `npm test` passed `152/152`; `node --check bin/multiagent-safety.js` passed; `npm pack --dry-run` produced `imdeadpool-guardex-7.0.16.tgz`. +- [x] 3.2 Run `openspec validate agent-codex-make-release-workflow-idempotent-when-ve-2026-04-21-16-30 --type change --strict`. Result: `Change 'agent-codex-make-release-workflow-idempotent-when-ve-2026-04-21-16-30' is valid`. +- [x] 3.3 Run `openspec validate --specs`. Result: `No items found to validate.` + +## 4. Completion + +- [ ] 4.1 Finish the agent branch via PR merge + cleanup (`gx finish --via-pr --wait-for-merge --cleanup` or `bash scripts/agent-branch-finish.sh --branch --base --via-pr --wait-for-merge --cleanup`). +- [ ] 4.2 Record PR URL + final `MERGED` state in the completion handoff. +- [ ] 4.3 Confirm sandbox cleanup (`git worktree list`, `git branch -a`) or capture a `BLOCKED:` handoff if merge/cleanup is pending. diff --git a/test/metadata.test.js b/test/metadata.test.js index e2b91a6..4d63748 100644 --- a/test/metadata.test.js +++ b/test/metadata.test.js @@ -31,6 +31,17 @@ test('release workflow publishes with provenance in CI', () => { assert.match(workflow, /npm publish --provenance --access public/); }); +test('release workflow skips publish when the current version is already on npm', () => { + const workflowPath = path.join(repoRoot, '.github', 'workflows', 'release.yml'); + const workflow = fs.readFileSync(workflowPath, 'utf8'); + assert.match(workflow, /name:\s+Resolve package metadata/); + assert.match(workflow, /name:\s+Check npm registry for current version/); + assert.match(workflow, /npm view "\$\{PACKAGE_NAME\}@\$\{PACKAGE_VERSION\}" version/); + assert.match(workflow, /if:\s+\$\{\{\s*steps\.registry\.outputs\.already_published != 'true'\s*\}\}/); + assert.match(workflow, /if:\s+\$\{\{\s*steps\.registry\.outputs\.already_published == 'true'\s*\}\}/); + assert.match(workflow, /skipping publish\./); +}); + test('release workflow only publishes from published releases or manual dispatch', () => { const workflowPath = path.join(repoRoot, '.github', 'workflows', 'release.yml'); const workflow = fs.readFileSync(workflowPath, 'utf8');