Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 12 additions & 4 deletions .changeset/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,15 @@ pnpm change --bump <none|patch|minor|major> --summary "<changelog entry>" [<pkg>
- This README is NOT a changeset: the gate requires a file whose frontmatter
parses as `"<pkg>": <none|patch|minor|major>`.

Publishing uses npm OIDC trusted publishing from `.github/workflows/release.yml`.
Register `@systemfsoftware/arethetypeswrong-cli` and `@systemfsoftware/arethetypeswrong` as trusted publishers on npmjs.com
pointing at this repository and that workflow filename before the first new
version can ship. OIDC cannot debut a package npm has never seen.
The release pipeline is the shared toolchain in
`systemfsoftware/pnpm-release-management`, consumed as a reusable workflow
(`.github/workflows/release.yml` and `changeset-check.yml` are thin callers
pinned to its `prm/toolchain` ref). On a push to `main` it opens or updates the
version PR when intents are pending, and otherwise tags each released version
`<pkg>@vX.Y.Z` and cuts a GitHub Release from its authored changelog. A version
with no such tag is what the pipeline treats as owed a release.

Distribution is Nix flakes consumed from git refs, not an npm registry: the git
tag, pinned downstream by a consumer's `flake.lock` rev + narHash, is the
durable record that a version shipped. There is no registry step to debut a
package.
6 changes: 6 additions & 0 deletions .changeset/unify-release-tooling.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
"@systemfsoftware/arethetypeswrong-cli": none
"@systemfsoftware/arethetypeswrong": none
---

Release tooling moves to the shared `systemfsoftware/pnpm-release-management` toolchain (CI workflows and root config only); no package code changes and nothing is released
16 changes: 6 additions & 10 deletions .github/workflows/changeset-check.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,9 @@ permissions:

jobs:
require-changeset:
if: github.head_ref != 'changeset-release/main'
name: a publishable-package change needs a changeset
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
with:
fetch-depth: 0
- uses: denoland/setup-deno@v2
- name: Require a changeset for publishable-package changes
run: ./scripts/check-changeset.ts ${{ github.event.pull_request.base.sha }}
uses: systemfsoftware/pnpm-release-management/.github/workflows/changeset-check.yml@prm/toolchain
with:
tools-ref: prm/toolchain
permissions:
contents: read
pull-requests: read
102 changes: 4 additions & 98 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,104 +13,10 @@ permissions:
pull-requests: write

jobs:
plan:
name: plan · derive phase
runs-on: ubuntu-latest
permissions:
contents: read
outputs:
phase: ${{ steps.plan.outputs.phase }}
env:
HUSKY: "0"
steps:
- uses: actions/checkout@v7
with:
fetch-depth: 0
fetch-tags: true
- uses: denoland/setup-deno@v2
- uses: pnpm/action-setup@v6
- uses: actions/setup-node@v7
with:
node-version: 24
cache: pnpm
- run: pnpm install --frozen-lockfile
- name: Preflight intent consumption
run: pnpm version -r --dry-run
- id: plan
name: Decide release phase from repository state
run: ./scripts/plan-release.ts --output "$GITHUB_OUTPUT"

version:
if: needs.plan.outputs.phase == 'version'
needs: [plan]
name: version · open release PR
runs-on: ubuntu-latest
release:
uses: systemfsoftware/pnpm-release-management/.github/workflows/release.yml@prm/toolchain
with:
tools-ref: prm/toolchain
permissions:
contents: write
pull-requests: write
env:
HUSKY: "0"
steps:
- uses: actions/checkout@v7
with:
fetch-depth: 0
- uses: denoland/setup-deno@v2
- uses: pnpm/action-setup@v6
- uses: actions/setup-node@v7
with:
node-version: 24
cache: pnpm
- run: pnpm install --frozen-lockfile
- name: Consume pending change intents
run: pnpm version -r
- name: Capture release set
run: ./scripts/tag-released-packages.ts --dry-run --json --output /tmp/captured.json && cat /tmp/captured.json
- name: Assert release notes
env:
GITHUB_TOKEN: ${{ github.token }}
run: ./scripts/create-github-releases.ts --assert --captured /tmp/captured.json
- name: Open or update the Release PR
env:
GH_TOKEN: ${{ github.token }}
BRANCH: changeset-release/main
BASE: ${{ github.event.repository.default_branch }}
run: ./scripts/open-release-pr.sh

publish:
if: needs.plan.outputs.phase == 'publish'
permissions:
contents: write
id-token: write
name: publish · oidc · tag
needs: [plan]
runs-on: ubuntu-latest
env:
HUSKY: "0"
steps:
- uses: actions/checkout@v7
with:
fetch-depth: 0
fetch-tags: true
- uses: denoland/setup-deno@v2
- uses: pnpm/action-setup@v6
- uses: actions/setup-node@v7
with:
node-version: 24
cache: pnpm
- run: pnpm install --frozen-lockfile
- name: Capture release set
run: ./scripts/tag-released-packages.ts --dry-run --json --output /tmp/captured.json
- name: Assert release notes
env:
GITHUB_TOKEN: ${{ github.token }}
run: ./scripts/create-github-releases.ts --assert --captured /tmp/captured.json
- name: Build
run: pnpm build
- name: Publish unpublished versions via OIDC
run: ./scripts/tag-released-packages.ts --unpublished --captured /tmp/captured.json --publish
- name: Tag released versions
run: ./scripts/tag-released-packages.ts --captured /tmp/captured.json
- name: GitHub Releases from authored changelogs
env:
GITHUB_TOKEN: ${{ github.token }}
run: ./scripts/create-github-releases.ts --captured /tmp/captured.json
16 changes: 4 additions & 12 deletions CONCEPTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,26 +16,18 @@ _Avoid:_ changeset — the file format is a changeset, but the concept here is t

### Release set

The workspace packages owed a release in the current run — those whose manifest version is not yet served by the package registry. Membership is a fact about the registry, not about version control: a package leaves the set when its version is published, never when a branch advances or a tag is written.
The workspace packages owed a release in the current run — those whose manifest version carries no `<pkg>@vX.Y.Z` git tag yet. Membership is a git fact: a package leaves the set when its tag is written (which the shared release pipeline does as it tags the version and cuts its GitHub Release), not when a branch advances. There is no registry to probe — Nix flakes consumed from git refs are the distribution, so the tag is the record that a version shipped.

### Release phase

The stage the release pipeline decides it is in, derived rather than configured. `publish` when the release set is non-empty; `version` when nothing is owed but unconsumed change intents remain; `none` when neither holds. Each phase gates a distinct job, so a phase derived from a wrong signal skips work silently rather than failing. An intent file that still exists after consumption is recorded is not pending.
The stage the release pipeline decides it is in, derived rather than configured. `release` when the release set is non-empty (tag the untagged versions and cut their GitHub Releases); `version` when nothing is owed but unconsumed change intents remain; `none` when neither holds. The shared toolchain derives the phase from repository state on each push to `main`, so a half-finished release resumes on the next push. An intent file that still exists after consumption is recorded is not pending.

### Published version
### Released version

A version the package registry serves for a package. The registry is the authority on this: neither a git tag nor a changelog file establishes it, and both are written downstream of a successful publish.

### Git tag as release evidence

Rejected as a release signal. Tags are written _after_ the step that a missing tag would cause to fail, so a detector reading tag absence cannot make its own precondition true. Recorded here because the term still appears in the pipeline's history and in older workflow steps.
A version that carries its `<pkg>@vX.Y.Z` git tag. The tag is the authority on this: the shared release pipeline writes it as it tags the version and cuts the GitHub Release, and a consumer pins it downstream through `flake.lock` (rev + narHash). An untagged manifest version is owed a release; a tagged one is done. Tagging and the GitHub Release are both idempotent on tag existence, so a half-finished release resumes safely on the next push to main.

## Registry resolution

### Registry probe

A query against the package registry asking whether a given package version is published. It has three outcomes, not two: published, unpublished, and _cannot tell_. The last is a failure, never a "no" — folding it into unpublished reclassifies a published package as owed a release.

### Scoped name

A package name carrying a scope, written `@scope/name`. A scoped name is a single path segment in a registry URL, so the scope separator must be percent-encoded rather than left literal.
Expand Down

This file was deleted.

Loading
Loading