Releases
This page is for contributors who check or publish Graft.
Checks
Section titled “Checks”Run every command in the project’s devenv shell:
devenv shell -- pnpm installdevenv shell -- pnpm builddevenv shell -- pnpm typecheckdevenv shell -- pnpm testdevenv shell -- pnpm test:packagesEntering the shell formats the code with treefmt. pnpm test:packages tests the
release script.
The CLI’s build also writes packages/cli/schema.json, the
JSON Schema of the config file, from the
loader’s entry fields and the intercepts the CLI registers. It is not committed;
each release packs the one its build generated.
pnpm test checks every published entry for types that cannot be named through
public exports, and emits declarations for unannotated consumer examples. If it
fails, remove implementation types from the public signature or export a type
that belongs to the public contract; do not add annotations to the examples to
hide the problem.
GitHub Actions
Section titled “GitHub Actions”The workflow .github/workflows/ci.yml runs for pull requests, for pushes to
main, and for tags like v1.2.3 or v1.2.3-beta.0.
| Job | When | What it does |
|---|---|---|
check |
Always | Checks formatting, builds, typechecks, tests, and packs every public package. |
github-packages |
Pushes and tags | Publishes the packed files to GitHub Packages. |
npm |
Tags | Publishes the packed files to npm with trusted publishing. |
The publishing jobs run only after check passes. They publish exactly the
files that check packed and tested, and never build again.
Publishing queues
Section titled “Publishing queues”Each registry has a fixed job concurrency group, with
cancel-in-progress: false and queue: max.
| Registry | Group |
|---|---|
| npm | publish-npm |
| GitHub Packages | publish-github-packages |
- Scope. The group covers the whole publishing job: reading tags, choosing a tag, uploading packages and handling retries. Different versions cannot mix these operations on the same registry.
- Independence. The two registries have separate queues and can publish at the same time.
- Order. The queue order follows the time when a job starts waiting, not the version order, so the version comparison is still needed.
- Capacity.
GitHub concurrency queues
keep up to 100 pending jobs with
queue: max. When the queue is full, GitHub cancels extra jobs, so check the Actions runs for cancelled publishing jobs and run them again after the queue is empty. Withoutqueue: max, GitHub keeps only one pending job, and a third job replaces it.
Versions
Section titled “Versions”All packages are released together with one version. pnpm packages:pack sets
that version in every public package, pins the dependencies between them to it,
packs the packages into .release/ and writes the hash of each file to
.release/release.json.
| Event | Version | npm tag | Published to |
|---|---|---|---|
A push to main |
0.0.0-commit-<full hash> |
sha-<full hash> |
GitHub Packages |
A tag like v1.2.3-beta.0 |
1.2.3-beta.0 |
alpha, beta, or rc |
GitHub Packages and npm |
A tag like v1.2.3 |
1.2.3 |
latest |
GitHub Packages and npm |
Install a prerelease or snapshot
Section titled “Install a prerelease or snapshot”A prerelease tag names its channel, alpha, beta or rc, followed by a
number. Its packages go under the npm tag of that channel and never become
latest, so npm install @graftland/graft keeps installing the last stable
release.
| You want… | Run |
|---|---|
| A prerelease | npm install @graftland/graft@beta |
Any commit of main |
npm install @graftland/graft@0.0.0-commit-<full hash> --registry=https://npm.pkg.github.com |
For commit snapshots, use the version from the workflow run. GitHub Packages
needs a token with read:packages, even for public packages. A package
published there for the first time may be private; set its visibility once in
the package settings on GitHub.
Publishing guarantees
Section titled “Publishing guarantees”Retries
Section titled “Retries”You can run the publishing again under the same registry queue.
| Situation | npm | GitHub Packages |
|---|---|---|
| Version exists with the same contents | Skipped. Tags are not changed. | Skipped. It can repair its channel tag if that does not move the tag backward. |
| Version exists with different contents | The script stops. | The script stops. |
| A tag is missing or was changed from outside | A maintainer with tag permission repairs it separately, under the same exclusive publication rule. Running the job again does not repair it. | Repaired as above. |
The npm trusted publisher has publish permission only, so retries never call
npm dist-tag.
Channels never move backward
Section titled “Channels never move backward”Because publishing runs one at a time, latest, alpha, beta and rc never
move back to an older version.
- A first upload and a GitHub tag repair use the same SemVer precedence comparison against the current version of the target channel.
- Numeric prerelease identifiers compare as numbers (
beta.10is newer thanbeta.2). Major, minor and patch come before the prerelease sequence. Build metadata does not affect precedence. - The script reads the complete
dist-tagsobject with an explicit@<channel>selector, so packages that have only prereleases do not need alatesttag. - A missing channel (E404) can be set up. A package that has only prereleases
has no
latestyet; its tags are listed withnpm dist-tag ls, so its first stable release setslatestup too. A successful response that is empty or malformed stops publishing. - Running the version already on a channel again leaves the tag unchanged.
Late uploads
Section titled “Late uploads”If a new upload is older than the current version of the channel, Graft still publishes the package under a fallback tag:
| Release kind | Fallback tag |
|---|---|
| Stable | backport |
| Prerelease | alpha-backport, beta-backport or rc-backport |
These tags mark late uploads and do not promise to follow the newest version.
Install an older release by its exact version. Running an older existing version
again does not move its channel or repair a fallback tag. For example, if you
publish 0.2.0-beta.2 before 0.2.0-beta.1, or run beta.1 again afterward,
beta stays on beta.2.
Release a version
Section titled “Release a version”-
Make sure
mainpasses CI. -
Tag the commit and push the tag, such as
v1.2.3. While Graft is on 0.x, release plain versions such asv0.3.0: a 0.x version already says the API may change, so it needs no-betasuffix. Prerelease tags such asv1.2.3-beta.0remain available:Terminal window git tag v1.2.3git push origin v1.2.3 -
The
npmjob waits for thenpmenvironment, if it needs approval, and then publishes.
Trusted publishing
Section titled “Trusted publishing”The npm job uses npm’s
trusted publishing: npm accepts the
OIDC token of the workflow run instead of an access token, and adds a provenance
statement to every package. The repository stores no secret. Each package trusts
the ci.yml workflow of graftland/graft in the npm environment:
npm trust github @graftland/graft --file ci.yml --repo graftland/graft --env npm --allow-publishnpm trust list @graftland/graftPublish a new package
Section titled “Publish a new package”npm can trust only a package that already exists. A maintainer publishes a new package once from the files that CI packed, and trusts it afterwards:
gh run download <run id> --name packages-<commit hash> --dir .releasenpm logindevenv shell -- pnpm packages:publish npmDocs site
Section titled “Docs site”The workflow .github/workflows/docs.yml builds this site from packages/docs
on every push to main and deploys it to GitHub Pages at
graftland.github.io/graft. You can also
start it by hand from the Actions tab. Only one deploy runs at a time.
| Task | How |
|---|---|
| Pages source | Set the repository’s Pages source to GitHub Actions. |
| Work on the site locally | devenv shell -- pnpm --filter @graftland/docs dev |
Writing pages
Section titled “Writing pages”| Language | Folder |
|---|---|
| English | packages/docs/content/docs |
| Simplified Chinese | packages/docs/content/docs/zh-cn |
| Traditional Chinese | packages/docs/content/docs/zh-tw |
- Use the same file names in every folder.
- Link between pages with relative links, such as
../plugins/, so that links work under the site’s base path. - Prefer tables, numbered steps and short lists over long paragraphs. Open a page with what it covers, and end it with where to go next.
- Every README has
README.zh-CN.mdandREADME.zh-TW.mdtranslations next to it, written from the English README. If you change an English README or an English docs page, update the translations in the same change.