Merge pull request #7 from engineio/docs/agent-reference #14
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: release | |
| # Every push to main releases, if the commits since the last tag warrant one. | |
| # No release PR, no approval step: semantic-release reads the conventional | |
| # commits, works out the next version, tags it, writes the GitHub release notes | |
| # and publishes to npm in a single run. | |
| # | |
| # A push with nothing releasable in it (docs:, chore:, ci:) is a no-op. That is | |
| # the behaviour to expect rather than a failure — see .releaserc.json for which | |
| # commit types earn which bump. | |
| # | |
| # PUBLISHES TO npmjs, PUBLICLY, WITH NO TOKEN. Authentication is npm trusted | |
| # publishing: GitHub Actions mints a short-lived OIDC token, npm verifies it | |
| # against the Trusted Publisher configured on the package, and no long-lived | |
| # secret exists to leak or expire. `id-token: write` below is what makes that | |
| # possible, and @semantic-release/npm@13 detects the OIDC context and skips its | |
| # token check. | |
| # | |
| # Every release goes to BOTH registries: npmjs first (canonical), then mirrored | |
| # to GitHub Packages. npm is what the consumer repos install from, because | |
| # GitHub Packages requires an access token to install even for public packages — | |
| # per GitHub's own docs — which would put a PAT back in every developer's | |
| # ~/.npmrc and every CI job. The mirror exists for the org-internal listing, not | |
| # for consumption. | |
| # | |
| # npm provenance is generated automatically under trusted publishing, so the | |
| # published package carries a verifiable link back to this repo and this | |
| # workflow run. It requires a public repo and a public package; both hold. | |
| # | |
| # ONE-TIME SETUP ON npmjs, which cannot be done from here: | |
| # npmjs.com -> @engineio/ui -> Settings -> Trusted Publisher | |
| # Organization or user: engineio | |
| # Repository: ui | |
| # Workflow filename: release.yml | |
| # Until that exists the publish step fails with an auth error, which is correct | |
| # — it means nothing can publish under this name without going through here. | |
| # | |
| # WHAT THIS DELIBERATELY DOES NOT DO: write anything back to main. The usual | |
| # semantic-release setup adds @semantic-release/changelog and | |
| # @semantic-release/git so the version bump and CHANGELOG land as a commit on | |
| # the release branch. Both are omitted, because main requires pull requests and | |
| # a bot pushing straight to it would need a bypass nobody should hand a CI job. | |
| # So package.json's version stays the placeholder 0.0.0-development — tags are | |
| # the source of truth, and the real version is written in the runner just | |
| # before publishing — and the GitHub release notes are the changelog. | |
| on: | |
| push: | |
| branches: [main] | |
| workflow_dispatch: | |
| concurrency: | |
| group: release | |
| cancel-in-progress: false | |
| permissions: | |
| contents: write # create tags and releases | |
| id-token: write # mint the OIDC token for npm trusted publishing | |
| packages: write # mirror the release to GitHub Packages | |
| issues: write # semantic-release/github verifies these even with | |
| pull-requests: write # comments switched off | |
| jobs: | |
| release: | |
| runs-on: ubuntu-latest | |
| steps: | |
| # fetch-depth: 0 is required, not tidiness — semantic-release works out | |
| # the next version by reading every commit since the last tag, and a | |
| # shallow clone has neither the tags nor the history to do it. | |
| - uses: actions/checkout@v7 | |
| with: | |
| fetch-depth: 0 | |
| - uses: oven-sh/setup-bun@v2 | |
| with: | |
| bun-version: 1.3.14 | |
| # Node 22.14+ is the floor for trusted publishing. registry-url points | |
| # npm at npmjs and writes the .npmrc it reads; there is no token to put | |
| # in it. | |
| - uses: actions/setup-node@v6 | |
| with: | |
| node-version: "22.14" | |
| registry-url: https://registry.npmjs.org | |
| # Node 22 bundles npm 10.x, and trusted publishing needs npm >= 11.5.1. | |
| # Without this the publish fails asking for credentials, which looks like | |
| # a misconfigured Trusted Publisher rather than an old CLI. | |
| - name: Ensure npm supports trusted publishing | |
| run: | | |
| npm install -g npm@^11 | |
| npm --version | |
| - run: bun install --frozen-lockfile | |
| # Gate the release on the same checks CI runs. A broken build must not | |
| # become a published version — once a version exists on the registry it | |
| # cannot be replaced, only superseded. | |
| - name: Verify before releasing | |
| run: | | |
| bun run check | |
| bun run build | |
| - name: Release | |
| run: bunx semantic-release | |
| env: | |
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| # Bootstrap escape hatch, and normally empty. | |
| # | |
| # npm's docs do not say whether a Trusted Publisher can be registered | |
| # for a package name that does not exist yet, and a Trusted Publisher | |
| # is configured in a package's settings — which a package needs to | |
| # exist to have. If pre-registering turns out to be impossible, add an | |
| # npm automation token as the NPM_TOKEN secret, let the first publish | |
| # use it, then configure the Trusted Publisher and DELETE the secret. | |
| # | |
| # Passing it unconditionally is safe either way: an unset secret | |
| # arrives as an empty string, @semantic-release/npm only writes an | |
| # authToken when the value is truthy, and with nothing written it | |
| # falls through to the OIDC path. So this line needs no edit when the | |
| # secret is added or removed. | |
| NPM_TOKEN: ${{ secrets.NPM_TOKEN }} | |
| # BOTH names are required, and leaving this one out is why five | |
| # release attempts failed with `401 GET /-/whoami`. | |
| # | |
| # `setup-node` with `registry-url` writes its own user-level .npmrc | |
| # containing `//registry.npmjs.org/:_authToken=${NODE_AUTH_TOKEN}` | |
| # and points NPM_CONFIG_USERCONFIG at it. With NODE_AUTH_TOKEN unset, | |
| # npm expands it to an empty string and every registry call is | |
| # unauthenticated — the same shadowing bug that an empty | |
| # ${NODE_AUTH_TOKEN} in a project .npmrc causes locally, except | |
| # inflicted by the action rather than by a checked-in file. | |
| # | |
| # NPM_TOKEN is what @semantic-release/npm reads; NODE_AUTH_TOKEN is | |
| # what setup-node's .npmrc dereferences. Both point at the same | |
| # secret. Under trusted publishing this whole block becomes | |
| # unnecessary and can go. | |
| NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} | |
| # npm is the CANONICAL registry — it is what both consumer repos install | |
| # from, and the only one that needs no credential. This mirrors the same | |
| # version to GitHub Packages for the org-internal package listing and for | |
| # anything still pointed there. If the two ever disagree, npm is right. | |
| # | |
| # Runs only when semantic-release actually released: its npm plugin writes | |
| # the real version into package.json in the workspace during prepare, so a | |
| # version other than the placeholder is the signal. A push with nothing | |
| # releasable leaves the placeholder and this no-ops. | |
| # | |
| # --registry overrides publishConfig.registry on the command line, which | |
| # is why package.json stays pinned to npmjs. GITHUB_TOKEN suffices — | |
| # GitHub Packages accepts it for a same-repo publish, so there is no extra | |
| # secret. Note this does NOT make GitHub Packages installable without a | |
| # token: that is a GitHub restriction on reads, not on publishing. | |
| - name: Mirror to GitHub Packages | |
| run: | | |
| set -euo pipefail | |
| version=$(node -p "require('./package.json').version") | |
| if [ "$version" = "0.0.0-development" ]; then | |
| echo "no release this run — nothing to mirror" | |
| exit 0 | |
| fi | |
| # A DEDICATED npmrc, because setup-node's one only carries auth for | |
| # registry.npmjs.org. Publishing with --registry=npm.pkg.github.com | |
| # against it fails ENEEDAUTH: npm finds no credential for that host | |
| # and does not fall back. NPM_CONFIG_USERCONFIG also means the | |
| # runner's ~/.npmrc is ignored, so appending there does nothing. | |
| rc=$(mktemp) | |
| printf '//npm.pkg.github.com/:_authToken=%s\n' "$NODE_AUTH_TOKEN" > "$rc" | |
| echo "mirroring @engineio/ui@$version to GitHub Packages" | |
| # A version already present on the mirror is success, not failure — | |
| # it means a previous run got this far. Anything else is a real error. | |
| if NPM_CONFIG_USERCONFIG="$rc" npm publish \ | |
| --registry=https://npm.pkg.github.com \ | |
| --ignore-scripts --tag latest 2>&1 | tee /tmp/mirror.log; then | |
| echo "mirrored" | |
| elif grep -qE 'EPUBLISHCONFLICT|cannot publish over|already exists' /tmp/mirror.log; then | |
| echo "$version already on GitHub Packages — nothing to do" | |
| else | |
| echo "::error::mirror to GitHub Packages failed" | |
| exit 1 | |
| fi | |
| env: | |
| NODE_AUTH_TOKEN: ${{ secrets.GITHUB_TOKEN }} |