mirror of
https://github.com/Routstr/routstrd.git
synced 2026-10-30 07:37:40 +00:00
Document the release workflow (validate/build/publish-npm/release), npm trusted publishing via OIDC, the canonical repository.url requirement, and the @routstr/sdk release-ordering rule in docs/CI.md, linked from the README release section and project structure.
130 lines
5.1 KiB
Markdown
130 lines
5.1 KiB
Markdown
# CI and release automation
|
|
|
|
How continuous integration and releases work for `routstrd`. Everything runs on
|
|
GitHub Actions; there are **no repository secrets** — publishing uses OIDC
|
|
trusted publishing and the workflow's own `GITHUB_TOKEN`.
|
|
|
|
## Workflow
|
|
|
|
`.github/workflows/release.yml`.
|
|
|
|
Triggers:
|
|
|
|
| Trigger | What runs |
|
|
| --- | --- |
|
|
| `pull_request` | `validate` + `build` (no publish, no release) |
|
|
| `workflow_dispatch` (manual) | `validate` + `build` only — publish and release jobs are tag-gated |
|
|
| `push` of a `v*` tag | the full pipeline: validate → build → publish npm → GitHub Release |
|
|
|
|
Jobs:
|
|
|
|
- **`validate`** — `bun install --frozen-lockfile`, then (on a tag) asserts the
|
|
tag equals `v` + `package.json` version, then `bun run lint` (`tsc --noEmit`)
|
|
and `bun test`.
|
|
- **`build`** — a 4-way matrix that compiles standalone executables with
|
|
`bun build --compile` and smoke-tests each one (`--version`, `--help`):
|
|
|
|
| runner | target | platform-arch |
|
|
| --- | --- | --- |
|
|
| `ubuntu-24.04` | `bun-linux-x64` | `linux-x64` |
|
|
| `ubuntu-24.04-arm` | `bun-linux-arm64` | `linux-arm64` |
|
|
| `macos-15-intel` | `bun-darwin-x64` | `darwin-x64` |
|
|
| `macos-15` | `bun-darwin-arm64` | `darwin-arm64` |
|
|
|
|
Each binary is packed into `routstrd-v<version>-<platform>-<arch>.tar.gz` and
|
|
uploaded as a build artifact.
|
|
- **`publish-npm`** — tag-gated, `needs: [validate, build]`. Publishes to npm
|
|
with provenance (see below). Skips gracefully if that version already exists.
|
|
- **`release`** — tag-gated, `needs: build`. Collects the artifacts, writes
|
|
`SHA256SUMS`, smoke-tests `install.sh` against the just-built Linux-x64 archive
|
|
over a local HTTP server, then creates the GitHub Release with the four
|
|
archives, `SHA256SUMS`, and `install.sh`.
|
|
|
|
## Cutting a release
|
|
|
|
```sh
|
|
# 1. bump package.json version and commit it
|
|
# 2. push the tag
|
|
git tag v0.4.15
|
|
git push origin main v0.4.15
|
|
```
|
|
|
|
The tag push is the single action that triggers everything. The tag **must**
|
|
equal `v` + `package.json` version (the tag must not already exist — the release
|
|
job uses `gh release create --verify-tag`).
|
|
|
|
After it finishes, confirm `routstrd@<version>` is on npm (`latest`) and that
|
|
the GitHub Release has all four archives plus `install.sh`.
|
|
|
|
## npm publishing (OIDC trusted publishing)
|
|
|
|
`publish-npm` publishes with `npm publish --access public --provenance`. There is
|
|
no `NPM_TOKEN`:
|
|
|
|
- The job grants `id-token: write`; npm exchanges the GitHub OIDC token for a
|
|
short-lived publish token.
|
|
- A one-time trust relationship on npm binds the package to this repo/workflow:
|
|
|
|
```sh
|
|
npm trust github routstrd \
|
|
--file release.yml \
|
|
--repo Routstr/routstrd \
|
|
--allow-publish
|
|
```
|
|
|
|
- The workflow upgrades npm (`npm install -g npm@latest`) because trusted
|
|
publishing requires npm ≥ 11.5.1 while Node 22 ships npm 10.x.
|
|
- `npm publish` runs the package's `prepublishOnly` (`bun run build`), so `dist/`
|
|
is produced in the job.
|
|
|
|
### Gotchas
|
|
|
|
- **`repository.url` must use the canonical casing.** Provenance validates it
|
|
case-sensitively against the repo (`https://github.com/Routstr/routstrd`). A
|
|
lowercase `routstr` fails with `422 … Failed to validate repository
|
|
information`. `bun`/`npm` preserve path case when normalizing, so the fix is to
|
|
spell it exactly `Routstr`.
|
|
- **npm has a post-publish processing delay.** A successful publish reports
|
|
`+ routstrd@x.y.z` and "Your package is being processed and may take a few
|
|
minutes to become available." The version can take several minutes to become
|
|
fetchable. A green workflow does not mean it is instantly installable.
|
|
|
|
## Why tag push, not `release: published`
|
|
|
|
`publish-npm` runs off the **tag push**, not a `release: published` event. The
|
|
`release` job creates the GitHub Release using the default `GITHUB_TOKEN`, and
|
|
GitHub suppresses workflow runs for events generated by that token (loop
|
|
prevention) — so a `release: published` workflow would never fire here.
|
|
|
|
## Idempotency
|
|
|
|
- `npm publish` is guarded: if `routstrd@<version>` already exists, the job logs
|
|
a warning and skips instead of failing.
|
|
- `gh release create --verify-tag` fails if the tag is missing; if the release
|
|
already exists the job errors, so do not create the release by hand.
|
|
|
|
## Dependency ordering with `@routstr/sdk`
|
|
|
|
`routstrd` depends on the **published** `@routstr/sdk` (not a local file link),
|
|
and CI installs with `bun install --frozen-lockfile` from the npm registry. When
|
|
a release needs a newer SDK:
|
|
|
|
1. Release `@routstr/sdk` first (it has its own publish pipeline; see the SDK
|
|
repo's `.github/workflows/publish.yml`).
|
|
2. In the same commit, bump `@routstr/sdk` in `package.json` **and** refresh
|
|
`bun.lock` (`bun install`).
|
|
3. Then tag and push `routstrd`.
|
|
|
|
Failure modes if that order is broken:
|
|
|
|
- Version not yet on npm → `validate` fails with
|
|
`No version matching "^x.y.z" found for specifier "@routstr/sdk"`.
|
|
- `package.json` bumped but `bun.lock` stale → `bun install --frozen-lockfile`
|
|
fails.
|
|
|
|
## Manual runs
|
|
|
|
`gh workflow run release.yml --ref main` runs `validate` + `build` only (the
|
|
publish and release jobs are gated on a `v*` tag ref). Useful for exercising the
|
|
build matrix without releasing.
|