diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index d40b306..cf80fe4 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -4,10 +4,28 @@ name: Publish to npm # not a decision to make it public and immutable, and npm's 72-hour unpublish window makes the # second one hard to take back. Run this workflow from the Actions tab when you mean it. # -# Before the first real run: -# 1. Create an npm automation token and add it as the NPM_TOKEN repository secret. -# 2. Run once with dry_run left as `true` and read the file list it prints. -# 3. Run again with dry_run set to `false`. +# Authentication is by **trusted publishing** (OIDC): npm exchanges the workflow's +# short-lived GitHub identity token for a publish token scoped to this package, so no +# long-lived npm token has to exist. To enable it, on npmjs.com → the cereale package → +# Settings → Trusted publisher → GitHub Actions, set: +# +# Organization or user: avalon-vanguard +# Repository: cereale +# Workflow filename: release.yml +# Environment: (leave blank — this workflow does not use one) +# +# The workflow filename must match this file's name, and the Environment field must be +# blank unless a matching `environment:` is added to the publish job; npm checks both +# against the OIDC claims and refuses the exchange if either disagrees. +# +# NPM_TOKEN is kept as a fallback. npm's OIDC step is deliberately non-throwing — if the +# trusted publisher is not configured, or the CLI is too old, it logs and falls through +# to token auth. That makes the switch safe to land before the registry side is set up. +# Once a real run shows OIDC working, the NPM_TOKEN secret can be deleted. +# +# Before a real run: +# 1. Run once with dry_run left as `true` and read the file list it prints. +# 2. Run again with dry_run set to `false`. on: workflow_dispatch: inputs: @@ -18,7 +36,7 @@ on: permissions: contents: read - id-token: write # required for npm provenance + id-token: write # the OIDC identity npm exchanges, and provenance jobs: publish: @@ -37,6 +55,21 @@ jobs: cache: 'npm' registry-url: 'https://registry.npmjs.org' + # Node 22 bundles npm 10.x, which has no OIDC support at all — it would skip + # trusted publishing in silence and fall back to token auth. Trusted publishing + # landed in npm 11.5.0 (lib/utils/oidc.js), so the version is raised and then + # asserted rather than assumed. + - name: Use an npm that can do trusted publishing + run: | + npm install -g npm@latest + V=$(npm --version) + echo "npm $V" + MAJ=${V%%.*}; REST=${V#*.}; MIN=${REST%%.*} + if [ "$MAJ" -lt 11 ] || { [ "$MAJ" -eq 11 ] && [ "$MIN" -lt 5 ]; }; then + echo "::error::npm $V has no OIDC support; trusted publishing needs >= 11.5.0" + exit 1 + fi + - name: Install dependencies run: npm ci @@ -76,8 +109,14 @@ jobs: - name: Publish if: ${{ inputs.dry_run == false }} + # --provenance stays explicit. Under OIDC npm would enable it on its own for a + # public repo, but only when the flag was left at its default; asking for it + # directly just skips that auto-enable and reaches the same place. On the token + # fallback path it is the only thing that produces an attestation at all. run: npm publish --provenance --access public env: + # Fallback only. Ignored once the trusted publisher is configured, because npm + # sets its own short-lived token before reading credentials. NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} - name: Summary diff --git a/README.md b/README.md index eaa6bf0..bde6f87 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,6 @@ # Cereale +[![npm](https://img.shields.io/npm/v/cereale?color=a0784a&label=npm)](https://www.npmjs.com/package/cereale) [![CI](https://github.com/avalon-vanguard/cereale/actions/workflows/ci.yml/badge.svg?branch=main)](https://github.com/avalon-vanguard/cereale/actions/workflows/ci.yml) [![Docs](https://github.com/avalon-vanguard/cereale/actions/workflows/pages.yml/badge.svg)](https://avalon-vanguard.github.io/cereale/) [![Node ≥20](https://img.shields.io/badge/node-%E2%89%A520-a0784a)](https://github.com/avalon-vanguard/cereale/blob/main/package.json) @@ -79,20 +80,13 @@ entity, anything with behaviour attached. Reach for Zod when you just want the d ## Installation -Not on npm yet: `npm install cereale` does **not** resolve to this library — the name is -unclaimed on the registry. Installing straight from GitHub will not work either, because the -build output is not committed. Until the first publish, install from a clone: - ```bash -git clone https://github.com/avalon-vanguard/cereale -cd cereale -npm install && npm run build -npm pack # → cereale-0.4.0.tgz - -# then, from your own project -npm install ../cereale/cereale-0.4.0.tgz +npm install cereale ``` +Published with [provenance](https://www.npmjs.com/package/cereale), so the registry carries a +verified attestation linking the tarball to the commit it was built from. + Cereale uses **TC39 standard decorators** (since 0.2.0), so no `experimentalDecorators` flag: ```json diff --git a/docs/index.html b/docs/index.html index a2a42ba..ff19890 100644 --- a/docs/index.html +++ b/docs/index.html @@ -814,22 +814,15 @@ export default defineConfig({
-

not on npm yet installing it today

+

on npm installing it

shell
-
git clone https://github.com/avalon-vanguard/cereale
-cd cereale
-npm install && npm run build
-npm pack                    # → cereale-0.4.0.tgz
-
-# then, from your own project
-npm install ../cereale/cereale-0.4.0.tgz
+
npm install cereale

- npm install cereale does not resolve to - this library — the name is unclaimed on the registry. Installing straight from GitHub - will not work either: the build output is not committed, so the package would arrive - without its dist/. + Published from CI with provenance, so the registry carries a verified + attestation linking the tarball to the commit it was built from — visible on the + package page.

@@ -998,9 +991,11 @@ npm install ../cereale/cereale-0.4.0.tgz cannot reach. Both are errors rather than silent no-ops.

-

It is not on npm yet

-

0.4.0 lives in the repository. npm install cereale does not - resolve to this library — build it from source until it is published.

+

It is still 0.x

+

0.4.0 is published, and under semver a 0.x minor bump + is allowed to break you. Pin the version until 1.0; the + changelog + says what moved and why.

diff --git a/docs/page.js b/docs/page.js index e1bc546..c0a903f 100644 --- a/docs/page.js +++ b/docs/page.js @@ -43,12 +43,6 @@ Array.prototype.forEach.call(document.querySelectorAll('.js-version'), function (el) { el.textContent = meta.version; }); - // The install snippet names the tarball npm pack produces; keep it tied to the - // same package.json fact the badge uses instead of hand-bumping it each release. - var shell = document.getElementById('install-shell'); - if (shell) { - shell.textContent = shell.textContent.replace(/cereale-[\d.]+\.tgz/g, 'cereale-' + meta.version + '.tgz'); - } } if (decoratorCount) { Array.prototype.forEach.call(document.querySelectorAll('.js-dec-count'), function (el) {