Skip to content

ci: publish releases from GitHub Actions via npm trusted publishing - #13

Merged
daniellee-ux merged 2 commits into
mainfrom
chore/release-workflow
Jul 31, 2026
Merged

daniellee-ux merged 2 commits into
mainfrom
chore/release-workflow

Conversation

@daniellee-ux

Copy link
Copy Markdown
Owner

Why

Releasing required an interactive npm 2FA OTP, so publishing could not be automated. npm trusted publishing (OIDC, GA since July 2025) removes both the OTP and any stored NPM_TOKEN — the workflow exchanges a short-lived GitHub OIDC token for publish rights, and provenance is attested automatically.

What

Pushing a v* tag cut from main runs .github/workflows/release.yml, which:

  1. re-runs the CI gates (core/demo typecheck, check:sync) — a publish can't be undone, so it doesn't assume the tag points at a green commit;
  2. verifies the tag matches the package versions and that create-opencanva's caret range covers the core version being published;
  3. publishes @opencanva/core, waits for it to resolve on the registry, then publishes create-opencanva.

Three helper scripts, each covering a failure this repo has actually hit or is exposed to:

Script Guards against
check-release-versions.mjs tag/package version drift; a stale ^0.3.0 wrapper dep stranding npm create opencanva@latest on the old core
publish-if-needed.mjs a half-finished release — skips versions already on the registry so workflow_dispatch can re-run it
wait-for-registry.mjs publishing the wrapper before core resolves → ETARGET for anyone scaffolding in that window

Required one-time setup (outside this repo)

npmjs.com needs a Trusted Publisher on both packages — @opencanva/core and create-opencanva — each with: organization/user daniellee-ux, repository open-canva, workflow filename release.yml, environment blank. All fields are case-sensitive and must match exactly.

Requirements met by the workflow: id-token: write permission, Node 22, npm upgraded to latest (trusted publishing needs npm ≥ 11.5.1; Node 22 ships npm 10.x).

Verification

check-release-versions.mjs and wait-for-registry.mjs were run locally: the version guard passes on v0.4.0, fails with a clear message on a mismatched tag, and the registry wait correctly reports @opencanva/core@0.4.0 as not yet published. The publish step itself is exercised by the first real tag.

🤖 Generated with Claude Code

daniellee-ux and others added 2 commits August 1, 2026 01:11
Releases needed an interactive 2FA OTP at the keyboard, so publishing could
not be automated. npm's trusted publishing (OIDC) removes both the OTP and
any stored NPM_TOKEN: the workflow exchanges a short-lived GitHub OIDC token
for publish rights, and provenance is attested automatically.

Pushing a `v*` tag cut from main now runs `.github/workflows/release.yml`,
which re-runs the CI gates (publishing can't be undone, so it doesn't trust
that the tag points at a green commit) and then publishes both packages in
the required order, with three small scripts:

- check-release-versions.mjs — tag == package versions, core and wrapper in
  lockstep, and the wrapper's caret range actually covers the core version
  being published (a stale ^0.3.0 would strand `npm create opencanva@latest`
  on the old core).
- publish-if-needed.mjs — skips a version already on the registry, so a run
  that failed halfway can be re-run via workflow_dispatch.
- wait-for-registry.mjs — gates the wrapper publish on core being resolvable,
  or `npm create opencanva@latest` fails with ETARGET in that window.

One-time setup outside this repo: npmjs.com needs a Trusted Publisher on
BOTH @opencanva/core and create-opencanva, pointing at this repository and
the workflow filename release.yml.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
npm documents provenance as automatic under trusted publishing, but it is
reported often enough as not applied that stating it beats discovering the
gap in a failed release. Both packages' `repository` fields already point at
this repo, which is what provenance verifies against.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@daniellee-ux
daniellee-ux merged commit bce92da into main Jul 31, 2026
1 check passed
@daniellee-ux
daniellee-ux deleted the chore/release-workflow branch July 31, 2026 17:13

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: e4f7ce9cfc

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

run: npm run check:sync

- name: Check the tag matches the package versions
run: node scripts/check-release-versions.mjs "${{ github.ref_type == 'tag' && github.ref_name || '' }}"

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Require manual releases to target a version tag

When workflow_dispatch is launched with a branch selected, github.ref_type is not tag, so this expression passes an empty string and check-release-versions.mjs deliberately skips tag validation; checkout and both publish steps then operate on that branch's HEAD. The suggested half-finished-release path can therefore publish an untagged version—or the next version after main has advanced—instead of resuming the intended release. Require a tag ref or explicit version-tag input and validate it before publishing.

Useful? React with 👍 / 👎.

Comment on lines +55 to +56
- name: Check committed skills/template/lockfile are in sync
run: npm run check:sync

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Run the template release gates before publishing

When a tag points at a commit whose init template is invalid, this sequence proceeds from the demo typecheck directly to check:sync and publishing, even though .github/workflows/ci.yml also runs the template TypeScript check and scripts/check-template-scaffold.mjs isolation check. Those checks catch template dependency and packed-scaffold failures that the core/demo checks cannot, so the workflow's stated defense against releasing an unverified tag still permits publishing a broken opencanva init; include both existing CI steps before the publish steps.

Useful? React with 👍 / 👎.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant