From 0a7453ae07d65de19b30a0c8159b0f80ec5f7c10 Mon Sep 17 00:00:00 2001 From: konojunya Date: Thu, 3 Sep 2026 18:34:57 +0900 Subject: [PATCH] Add npm release automation --- .github/workflows/release.yaml | 76 ++++++++++++++++++++++++++++++++++ README.md | 2 + RELEASING.md | 36 ++++++++++++++++ 3 files changed, 114 insertions(+) create mode 100644 .github/workflows/release.yaml create mode 100644 RELEASING.md diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml new file mode 100644 index 0000000..ae27e3a --- /dev/null +++ b/.github/workflows/release.yaml @@ -0,0 +1,76 @@ +name: Release + +on: + release: + types: + - published + +permissions: + contents: read + id-token: write + +jobs: + npm: + name: npm + runs-on: ubuntu-latest + steps: + - name: Check out repository + uses: actions/checkout@v7 + with: + fetch-depth: 0 + + - name: Set up Node.js + uses: actions/setup-node@v7 + with: + node-version: 24.18.0 + registry-url: https://registry.npmjs.org + package-manager-cache: false + + - name: Install current npm CLI + run: npm install --global npm@12.0.2 + + - name: Install JavaScript development dependencies + run: npm ci + + - name: Install stable Rust toolchain + run: rustup toolchain install stable --profile minimal --target wasm32-unknown-unknown + + - name: Install WebAssembly binding generator + uses: taiki-e/install-action@e67fa11c4b9316fa714ddf0abed07a0c3143b95b + with: + tool: wasm-bindgen-cli@0.2.127 + fallback: none + + - name: Verify release version + run: | + package_version=$(node --print "require('./packages/engine/package.json').version") + workspace_version=$(node --print "require('./package.json').version") + test "$package_version" = "$workspace_version" + test "$GITHUB_REF_NAME" = "v$package_version" + git merge-base --is-ancestor "$GITHUB_SHA" origin/main + + - name: Build browser WebAssembly package + run: npm run build:wasm + + - name: Test browser package + run: npm test + + - name: Check browser package types + run: npm run typecheck + + - name: Verify browser package contents + run: npm run pack:check + + - name: Check whether version is already published + id: package + run: | + package_version=$(node --print "require('./packages/engine/package.json').version") + if npm view "@stack-sh/engine@$package_version" version >/dev/null 2>&1; then + echo "published=true" >> "$GITHUB_OUTPUT" + else + echo "published=false" >> "$GITHUB_OUTPUT" + fi + + - name: Publish package + if: steps.package.outputs.published != 'true' + run: npm publish --workspace @stack-sh/engine --access public diff --git a/README.md b/README.md index b3ba84a..cf5c05e 100644 --- a/README.md +++ b/README.md @@ -49,6 +49,8 @@ The renderer emits fixed-dimension standalone SVG with embedded catalog icons, l The npm package exports synchronous `format`, `check`, and `render` functions after asynchronous module initialization. Each operation accepts `string | Uint8Array` and returns a specific typed result with camel-case metadata and portable diagnostics. Invalid UTF-8 remains a normal `STK1001` result. Unsupported JavaScript input types and internal operational failures throw at the adapter boundary. Shared fixtures compare complete native and WebAssembly results, including formatted source, diagnostics, SVG, and metadata. Artifact validation audits WebAssembly imports and package contents; browser consumers retain responsibility for loading the module and performing any DOM, filesystem, network, or clock work. +Public npm releases are produced from GitHub Releases after the repository checks pass. See [RELEASING.md](./RELEASING.md) for the first-release bootstrap and subsequent trusted-publishing flow. + ## Architecture - [`docs/decisions/0001-build-the-formatter-from-compiler-models.md`](./docs/decisions/0001-build-the-formatter-from-compiler-models.md) diff --git a/RELEASING.md b/RELEASING.md new file mode 100644 index 0000000..cc33d3c --- /dev/null +++ b/RELEASING.md @@ -0,0 +1,36 @@ +# Releasing `@stack-sh/engine` + +The package is public and is released from this public repository. Release artifacts must come from a merged `main` revision whose `baseline` and `Minimum supported Rust` checks have passed. + +## First release + +The npm package must exist before its trusted publisher can be configured. An authenticated maintainer with publish access to the `@stack-sh` scope performs the one-time bootstrap from a clean `main` checkout: + +```sh +npm ci +rustup target add wasm32-unknown-unknown --toolchain stable +npm run build:wasm +npm test +npm run typecheck +npm run pack:check +npm publish --workspace @stack-sh/engine --access public +``` + +After `@stack-sh/engine` exists on npm, configure its trusted publisher with these exact values: + +- Provider: GitHub Actions +- Organization: `stack-sh` +- Repository: `engine` +- Workflow filename: `release.yaml` +- Allowed action: `npm publish` + +Then create the `v0.1.0` GitHub Release from the same merged revision. The release workflow recognizes that the package version already exists and completes without publishing it twice. + +## Subsequent releases + +1. Update the workspace and package versions in a pull request. +2. Run the complete repository checks and merge the pull request. +3. Create a GitHub Release whose tag is exactly `v` and targets the merged commit. +4. Verify that the release workflow publishes the package through npm trusted publishing. + +The workflow rejects a tag that does not match the package version. It builds and validates the package again on the tagged revision, uses no long-lived npm token, and leaves npm provenance enabled.