Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 26 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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."
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
schema: spec-driven
created: 2026-04-21
Original file line number Diff line number Diff line change
@@ -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.
Original file line number Diff line number Diff line change
@@ -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`.
Original file line number Diff line number Diff line change
@@ -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 <agent-branch> --base <base-branch> --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.
11 changes: 11 additions & 0 deletions test/metadata.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -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');
Expand Down