From 48a15fce90ae4eac74006639c3203b5e1966f14c Mon Sep 17 00:00:00 2001 From: steckes Date: Mon, 7 Sep 2026 23:00:22 +0200 Subject: [PATCH 1/2] Take the GitHub release notes from CHANGELOG.md `napi prepublish` creates the release with neither a name nor a body, so every release it made was untitled and empty. Extract the section for the version being released and set it on the release after publishing. The tag pattern now requires the `v` prefix. napi derives the release tag as `v${version}` regardless of what was pushed, so a bare `0.24.0` tag made it create a second `v0.24.0` tag and release pointing at whatever the default branch happened to be. Co-Authored-By: Claude Opus 5 (1M context) --- .github/workflows/CI.yml | 8 +++++++- DEVELOPMENT.md | 11 +++++++++-- scripts/changelog-section.mjs | 27 +++++++++++++++++++++++++++ 3 files changed, 43 insertions(+), 3 deletions(-) create mode 100644 scripts/changelog-section.mjs diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index 0fa69b0..8de7589 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -12,7 +12,6 @@ permissions: branches: - main tags: - - '[0-9]+.[0-9]+.[0-9]+*' - 'v[0-9]+.[0-9]+.[0-9]+*' pull_request: null concurrency: @@ -397,3 +396,10 @@ jobs: env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} NPM_TOKEN: ${{ secrets.NPM_TOKEN }} + - name: Update the release notes + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + VERSION="${GITHUB_REF_NAME#v}" + node scripts/changelog-section.mjs "$VERSION" > release-notes.md + gh release edit "$GITHUB_REF_NAME" --title "$VERSION" --notes-file release-notes.md diff --git a/DEVELOPMENT.md b/DEVELOPMENT.md index d1b009d..fdfd112 100644 --- a/DEVELOPMENT.md +++ b/DEVELOPMENT.md @@ -26,10 +26,17 @@ The npm version tracks the version of the `aic-sdk` Rust crate it wraps. Bump merge commit: ```bash -git tag x.x.x && git push origin x.x.x +git tag vx.x.x && git push origin vx.x.x ``` +The `v` prefix is required: `napi prepublish` names the GitHub release `v`, so a +bare tag makes it create a second one alongside yours. + Pushing the tag builds all six targets, runs the tests and examples, and publishes to npm. The tag must match the version in `package.json` or the publish step fails. A tag with a -prerelease suffix, such as `0.24.0-rc.1`, publishes under the `next` dist-tag instead of +prerelease suffix, such as `v0.24.0-rc.1`, publishes under the `next` dist-tag instead of `latest`. + +The GitHub release notes are the matching `CHANGELOG.md` section, extracted by +`scripts/changelog-section.mjs`. A release whose version has no section there fails rather +than publishing empty notes. diff --git a/scripts/changelog-section.mjs b/scripts/changelog-section.mjs new file mode 100644 index 0000000..8668dd7 --- /dev/null +++ b/scripts/changelog-section.mjs @@ -0,0 +1,27 @@ +import { readFileSync } from 'node:fs' + +const version = process.argv[2] + +if (!version) { + console.error('usage: changelog-section.mjs ') + process.exit(1) +} + +const lines = readFileSync('CHANGELOG.md', 'utf8').split('\n') +const start = lines.findIndex((line) => line === `## ${version}` || line.startsWith(`## ${version} `)) + +if (start === -1) { + console.error(`CHANGELOG.md has no section for ${version}`) + process.exit(1) +} + +const rest = lines.slice(start + 1) +const end = rest.findIndex((line) => line.startsWith('## ')) +const body = (end === -1 ? rest : rest.slice(0, end)).join('\n').trim() + +if (!body) { + console.error(`The CHANGELOG.md section for ${version} is empty`) + process.exit(1) +} + +process.stdout.write(`${body}\n`) From 6b49d020b74995e2c03f01926baf139f8b557f64 Mon Sep 17 00:00:00 2001 From: steckes Date: Mon, 7 Sep 2026 23:36:06 +0200 Subject: [PATCH 2/2] Publish over OIDC instead of an npm token 0.23.1 and every package before it were published by trusted publishing: npm records the publisher as `GitHub Actions `, and the old workflow carried no token, only `id-token: write` and an npm new enough to use it. The rewrite replaced that with an `_authToken` line, so the release ran unauthenticated and npm answered the `PUT` with a 404 for a package that plainly exists. Drop the token, install an npm that supports trusted publishing, and let the OIDC identity authenticate. Provenance is no longer set explicitly because trusted publishing attaches it anyway, which is how 0.23.1 got its attestation. Also skip the root publish when that version is already on the registry, so a release that failed partway can be re-run. `napi prepublish` already does this for the platform packages. Co-Authored-By: Claude Opus 5 (1M context) --- .github/workflows/CI.yml | 24 ++++++++++++++++++------ DEVELOPMENT.md | 21 +++++++++++++-------- package.json | 2 +- 3 files changed, 32 insertions(+), 15 deletions(-) diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index 8de7589..d307a37 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -12,7 +12,7 @@ permissions: branches: - main tags: - - 'v[0-9]+.[0-9]+.[0-9]+*' + - '[0-9]+.[0-9]+.[0-9]+*' pull_request: null concurrency: group: ${{ github.workflow }}-${{ github.ref }} @@ -379,6 +379,8 @@ jobs: - name: List packages run: ls -R ./npm shell: bash + - name: Install an npm with trusted publishing support + run: npm install -g npm@latest - name: Publish run: | VERSION="${GITHUB_REF_NAME#v}" @@ -387,19 +389,29 @@ jobs: echo "::error::Tag $GITHUB_REF_NAME does not match package.json version $PACKAGE_VERSION" exit 1 fi - npm config set provenance true - echo "//registry.npmjs.org/:_authToken=$NPM_TOKEN" >> ~/.npmrc + PACKAGE_NAME="$(node -p "require('./package.json').name")" + if npm view "$PACKAGE_NAME@$VERSION" version >/dev/null 2>&1; then + echo "$PACKAGE_NAME@$VERSION is already published, skipping" + exit 0 + fi case "$VERSION" in *-*) npm publish --tag next --access public ;; *) npm publish --access public ;; esac env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - NPM_TOKEN: ${{ secrets.NPM_TOKEN }} - - name: Update the release notes + - name: Create the GitHub release env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: | VERSION="${GITHUB_REF_NAME#v}" node scripts/changelog-section.mjs "$VERSION" > release-notes.md - gh release edit "$GITHUB_REF_NAME" --title "$VERSION" --notes-file release-notes.md + case "$VERSION" in + *-*) PRERELEASE=--prerelease ;; + *) PRERELEASE= ;; + esac + if gh release view "$GITHUB_REF_NAME" >/dev/null 2>&1; then + gh release edit "$GITHUB_REF_NAME" --title "$VERSION" --notes-file release-notes.md $PRERELEASE + else + gh release create "$GITHUB_REF_NAME" --title "$VERSION" --notes-file release-notes.md $PRERELEASE + fi diff --git a/DEVELOPMENT.md b/DEVELOPMENT.md index fdfd112..eaac992 100644 --- a/DEVELOPMENT.md +++ b/DEVELOPMENT.md @@ -26,17 +26,22 @@ The npm version tracks the version of the `aic-sdk` Rust crate it wraps. Bump merge commit: ```bash -git tag vx.x.x && git push origin vx.x.x +git tag x.x.x && git push origin x.x.x ``` -The `v` prefix is required: `napi prepublish` names the GitHub release `v`, so a -bare tag makes it create a second one alongside yours. - Pushing the tag builds all six targets, runs the tests and examples, and publishes to npm. The tag must match the version in `package.json` or the publish step fails. A tag with a -prerelease suffix, such as `v0.24.0-rc.1`, publishes under the `next` dist-tag instead of +prerelease suffix, such as `0.24.0-rc.1`, publishes under the `next` dist-tag instead of `latest`. -The GitHub release notes are the matching `CHANGELOG.md` section, extracted by -`scripts/changelog-section.mjs`. A release whose version has no section there fails rather -than publishing empty notes. +The GitHub release is created from the tag with the matching `CHANGELOG.md` section as its +notes, extracted by `scripts/changelog-section.mjs`. A release whose version has no section +there fails rather than publishing empty notes. `napi prepublish` runs with +`--no-gh-release` because it would otherwise create its own release under a `v`-prefixed +tag. + +Publishing uses npm trusted publishing over OIDC, so there is no npm token in the +repository. Each of the seven published packages (`@ai-coustics/aic-sdk` and its six +platform packages) needs a trusted publisher on npmjs.com naming this repository and the +workflow file `CI.yml`. npm rejects a publish whose workflow file does not match, with a +404 on the `PUT` rather than a permission error. diff --git a/package.json b/package.json index 230ea40..20a5519 100644 --- a/package.json +++ b/package.json @@ -63,7 +63,7 @@ "typecheck:test": "tsc --noEmit -p __test__/tsconfig.json", "typecheck:bench": "tsc --noEmit -p benchmark/tsconfig.json", "typecheck:examples": "tsc --noEmit -p examples/tsconfig.json", - "prepublishOnly": "napi prepublish -t npm", + "prepublishOnly": "napi prepublish -t npm --no-gh-release", "pretest": "node --import @oxc-node/core/register scripts/fetch-test-models.mjs", "test": "ava", "version": "napi version"