This document describes how to publish the @hypercerts-org/lexicon
package to npm using GitHub Actions workflows with Changesets. The
workflow uses main as the branch from which normal releases are
published, with ephemeral prerelease branches for beta testing.
All releases are manually triggered to give you full control over when releases are made.
Clearly stability and predictability for users and developers are essential.
Unfortunately AT Protocol doesn't support any kind of native
versioning or migrations which could support lexicon schema changes.
Our strategy for dealing with this is documented in the "Maintenance
and publishing releases" section of
README.md.
mainbranch: Preparation for stable releases, which will be tagged and published from this branch. This is the only evergreen branch.prerelease/*branches: Ephemeral branches for beta/prerelease versions (created frommain, merged back when done)feature/*(orfix/*) branches: Short-lived branches for development work, targeting and merged tomainor aprerelease/*branch via PR depending on whether a beta or prerelease is required.
Flow:
feature/*orfix/*→mainorprerelease/*prerelease/*→main(beta cycle)
┌──────────────────────────────────────────────────────────────────┐
│ │
│ Stable releases: │
│ │
│ feature/* ───► main ───► Manual publish @latest (1.0.0) │
│ │
│ Beta releases: │
│ │
│ prerelease/beta │
│ │ │
│ │ npx changeset pre enter beta │
│ ▼ │
│ prerelease/beta ──► Manual publish @beta │
│ │ (1.0.0-beta.0, 1.0.0-beta.1) │
│ │ │
│ │ npx changeset pre exit │
│ │ (when ready for stable) │
│ ▼ │
│ PR to main ──► Manual publish @latest (1.0.0) │
│ │
└──────────────────────────────────────────────────────────────────┘
Before publishing, ensure you have:
-
npm Trusted Publisher configured:
- The workflow uses npm Trusted Publishers via GitHub OIDC for secure, token-less publishing
- Configure on npmjs.com: Package settings → Publishing access → Add a GitHub Actions publisher
- Required settings:
- Organization/User:
hypercerts-org - Repository:
hypercerts-lexicon - Workflow filename:
release.yml(must match exactly, case-sensitive)
- Organization/User:
- The npm CLI (v11.5.1+) automatically detects OIDC and uses it
- See: https://docs.npmjs.com/trusted-publishers
- No
NPM_TOKENsecret is required - Trusted Publishers eliminates the need for long-lived tokens
-
Repository URL in package.json:
package.jsonmust have arepository.urlfield- This is required for npm Trusted Publishers to verify the package source
- The repo already has this configured correctly
Before publishing, you need to create changesets for any user-facing changes:
npx changesetThis will:
- Prompt you to select which packages changed
- Ask for the version bump type (major/minor/patch)
- Create a markdown file in
.changeset/with your changes
Note: Changesets generates files with random names (e.g.,
beige-clowns-relax.md). This is intentional to prevent filename
collisions when multiple contributors create changesets simultaneously.
The filename doesn't affect version ordering—Changesets uses git history
to determine the order of changes. You can rename these files if desired,
but it's not necessary.
To publish a stable release to npm:
-
Run the workflow:
- Navigate to: https://github.com/hypercerts-org/hypercerts-lexicon/actions/workflows/release.yml
- Click "Run workflow"
- Select the branch:
main - Click "Run workflow" to start
-
What happens:
- The workflow validates the code and regenerates TypeScript types
- If there are pending changesets, it creates a "Release Pull Request"
- Merge the Release PR to publish to npm with the
latesttag - If no changesets exist, nothing is published
Beta releases use ephemeral prerelease branches.
-
Create a prerelease branch from
main:git checkout main git pull git checkout -b prerelease/beta
-
Enter prerelease mode:
npx changeset pre enter beta git add .changeset/pre.json git commit -m "chore: enter beta prerelease mode" git push -u origin prerelease/beta -
Publish the first beta:
- Navigate to: https://github.com/hypercerts-org/hypercerts-lexicon/actions/workflows/release.yml
- Click "Run workflow"
- Select the branch:
prerelease/beta - Click "Run workflow"
-
What happens:
- The workflow validates the code
- Versions packages based on pending changesets
- Commits and pushes version changes to the prerelease branch
- Publishes to npm with the
betatag - Version format:
1.0.0-beta.0,1.0.0-beta.1, etc.
To add changes and publish more betas:
- Create feature branches from
prerelease/beta(or cherry-pick frommain) - Add changesets as normal
- Merge to
prerelease/beta - Run the release workflow on
prerelease/beta
When you're ready to promote to stable:
-
Exit prerelease mode on the prerelease branch:
git checkout prerelease/beta npx changeset pre exit git add .changeset/pre.json git commit -m "chore: exit prerelease mode" git push
-
Open a PR from
prerelease/beta→main- The PR check will verify prerelease mode is not active
-
Merge the PR to
main -
Run the release workflow on
main:- The workflow detects the exit intent in
pre.json changeset versionstrips prerelease tags and removespre.json- A "Release Pull Request" is created with the stable versions
- Merge the Release PR to publish to npm with the
latesttag
- The workflow detects the exit intent in
-
Delete the prerelease branch (it's ephemeral)
When you open a pull request to main, the "PR Check" workflow
automatically runs to:
- Check if package changes (lexicons, types, package.json) have corresponding changesets
- Warn if changesets are missing
- Reject the PR if prerelease mode is still active - This ensures
npx changeset pre exithas been run before merging a prerelease branch tomain
Versions are determined by Changesets:
- Patch: Bug fixes and minor updates (1.0.0 → 1.0.1)
- Minor: New features (1.0.0 → 1.1.0)
- Major: Breaking changes (1.0.0 → 2.0.0)
You specify the version bump type when creating a changeset with
npx changeset.
The prepublishOnly script ensures types are regenerated before
publishing, so the published package always includes the latest
generated TypeScript types.
Changesets' prerelease mode (pre.json) is designed for a
single-branch workflow. Using a long-lived develop branch in pre
mode causes problems when merging between branches:
pre.jsonstate conflicts on merge (changesets#239)- Merging
mainback todevelop(e.g. for CHANGELOG updates) disrupts pre mode - No upstream solution exists for multi-branch prerelease workflows
Ephemeral prerelease branches avoid these issues entirely.