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
84 changes: 0 additions & 84 deletions .github/workflows/release-beta.yml

This file was deleted.

88 changes: 82 additions & 6 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,37 +11,113 @@ jobs:
permissions:
contents: write
pull-requests: write
# Required for npm Trusted Publishers via GitHub OIDC
# See: https://docs.npmjs.com/trusted-publishers
id-token: write

steps:
- uses: actions/checkout@v6
with:
fetch-depth: 0

# Branch validation: Only allow develop (beta) or main (stable)
- name: Validate branch
run: |
if [ "${{ github.ref }}" == "refs/heads/develop" ]; then
echo "RELEASE_TYPE=beta" >> $GITHUB_ENV
echo "Detected beta release from develop branch"
elif [ "${{ github.ref }}" == "refs/heads/main" ]; then
echo "RELEASE_TYPE=stable" >> $GITHUB_ENV
echo "Detected stable release from main branch"
else
echo "Error: This workflow can only be run from 'develop' (beta) or 'main' (stable) branches"
exit 1
fi

- uses: actions/setup-node@v6
with:
node-version: 20
cache: "npm"
# registry-url is required for npm Trusted Publishers
registry-url: "https://registry.npmjs.org"

- run: npm ci
- run: npm run check

- name: Create .npmrc
# Stable release: Verify prerelease mode has been exited
# The exit intent should already be set on develop before merging to main
- name: Verify prerelease mode exit
if: env.RELEASE_TYPE == 'stable'
run: |
echo "//registry.npmjs.org/:_authToken=${{ secrets.NPM_TOKEN }}" > .npmrc
echo "@hypercerts-org:registry=https://registry.npmjs.org/" >> .npmrc
if [ -f .changeset/pre.json ]; then
# Check if prerelease mode has been exited (exit intent set)
if ! grep -q '"exit": true' .changeset/pre.json 2>/dev/null; then
echo "Error: Prerelease mode must be exited before merging to main."
echo "Run 'npm run changeset pre exit' on the develop branch and commit the change."
exit 1
fi
echo "Prerelease mode exit intent confirmed - changeset version will handle the exit"
else
echo "No prerelease mode detected (pre.json not present)"
fi

# Beta-specific: Enter prerelease mode (if not already)
- name: Enter prerelease mode (if not already)
if: env.RELEASE_TYPE == 'beta'
run: |
if [ ! -f .changeset/pre.json ]; then
npx changeset pre enter beta
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
git add .changeset/pre.json
git commit -m "chore: enter beta prerelease mode"
git push
fi

# Beta-specific: Version packages manually
- name: Version packages
if: env.RELEASE_TYPE == 'beta'
run: npm run version-packages
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

# No .npmrc creation needed - npm Trusted Publishers uses GitHub OIDC tokens
# automatically via the id-token: write permission and registry-url configuration

# Stable release: Use changesets/action which handles versioning and publishing
- name: Create Release Pull Request or Publish
if: env.RELEASE_TYPE == 'stable'
id: changesets
uses: changesets/action@e0145edc7d9d8679003495b11f87bd8ef63c0cba # v1.5.3
with:
publish: npm run release
version: npm run version-packages
title: "chore: release packages"
commit: "chore: release packages"
title: "chore: release package"
commit: "chore: release package"
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
NPM_CONFIG_PROVENANCE: true

# Beta-specific: Publish beta packages directly
- name: Publish beta packages
if: env.RELEASE_TYPE == 'beta'
# Use npm run release to match regular releases - it runs check before publishing
# to ensure validation after versioning (package.json/changelog changes)
run: npm run release
env:
NPM_CONFIG_PROVENANCE: true

# Beta-specific: Commit and push version changes
- name: Commit and push version changes
if: env.RELEASE_TYPE == 'beta'
run: |
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
git add -A
git diff --staged --quiet || git commit -m "chore: version packages (beta)"
git push

# Stable release: Log published packages
- name: Log published packages
if: steps.changesets.outputs.published == 'true'
if: env.RELEASE_TYPE == 'stable' && steps.changesets.outputs.published == 'true'
run: echo "Published - ${{ steps.changesets.outputs.publishedPackages }}"
139 changes: 92 additions & 47 deletions PUBLISHING.md
Original file line number Diff line number Diff line change
@@ -1,33 +1,61 @@
# Publishing Guide for Maintainers

This document describes how to publish the `@hypercerts-org/lexicon`
package to npm using GitHub Actions workflows with Changesets. All
publishing workflows are manually triggered to give you full control
over when releases are made.
package to npm using GitHub Actions workflows with Changesets. The
workflow uses separate branches for beta and stable releases, with all
releases manually triggered to give you full control over when releases
are made.

## Branch Strategy

- **`develop` branch**: Beta/prerelease versions
- **`main` branch**: Stable releases

**Suggested Flow:** `feature` → `develop` (beta) → `main` (stable)

**Note:** Direct development on `main` is also supported. The `develop` → `main` flow is suggested when you want to publish beta versions for testing before stable releases. If you work directly on `main`, you can skip the beta testing phase and go straight to stable releases.

### Release Flow

```text
┌──────────────────────────────────────────────────────────────────┐
│ │
│ feature/* ──────────────────┐ │
│ │ │ │
│ │ npm run changeset │ PR │
│ │ (describe changes) │ │
│ ▼ ▼ │
│ ┌─────────┐ merge ┌──────────┐ │
│ │ commit │ ────────► │ develop │ ──► Manual publish @beta │
│ │ + .md │ └────┬─────┘ (0.9.0-beta.1) │
│ └─────────┘ │ │
│ │ PR (after: npm changeset pre exit) │
│ ▼ │
│ ┌────────┐ │
│ │ main │ ──► Creates "Release PR" │
│ └────┬───┘ │
│ │ │
│ │ merge Release PR │
│ ▼ │
│ ┌──────────────┐ │
│ │ npm publish │ ──► @latest (0.9.0) │
│ └──────────────┘ │
│ │
└──────────────────────────────────────────────────────────────────┘
```

## Prerequisites

Before publishing, ensure you have:

1. **GitHub Secrets configured:**
- `RELEASE_PAT`: GitHub Personal Access Token with permissions to bypass
branch protection rules. Required for the beta release workflow that
commits and pushes version changes to protected branches.
- **Recommended:** Fine-grained Personal Access Token
- Create at: <https://github.com/settings/tokens?type=beta>
- Repository access: Select this repository
- Repository permissions: `Contents` (read and write), `Metadata`
(read-only)
- **Alternative:** Classic Personal Access Token (if fine-grained
doesn't work with your branch protection settings)
- Create at: <https://github.com/settings/tokens>
- Required scopes: `repo` (full control of private repositories)
- This is needed because `GITHUB_TOKEN` cannot bypass branch protection
- **Note:** Ensure branch protection rules allow the token to bypass
protection (uncheck "Do not allow bypassing the above settings" or
add the token as an allowed actor)
- `NPM_TOKEN`: npm access token with publish permissions for
`@hypercerts-org` scope
1. **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
- Workflow name: `Release`
- See: <https://docs.npmjs.com/trusted-publishers>
- No `NPM_TOKEN` secret is required

## Adding Changesets

Expand All @@ -49,66 +77,83 @@ This will:
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 and not recommended in collaborative environments.
but it's not necessary.

## Publishing a Stable Release

**If merging from `develop` → `main`:** Before merging, you must exit prerelease mode on the `develop` branch:

```bash
# On develop branch
npm run changeset pre exit
git add .changeset/pre.json
git commit -m "chore: exit prerelease mode"
git push
```

This sets the exit intent in `pre.json`, which is required before merging to `main`. The PR check will verify this.

**If working directly on `main`:** You can skip this step since `pre.json` won't exist.

To publish a stable release to npm:

1. **Navigate to GitHub Actions:**
- Go to the repository on GitHub
- Click on the "Actions" tab
1. **If merging from `develop` → `main`:**
- Run `npm run changeset pre exit` on `develop` branch
- Commit and push the change
- Merge `develop` → `main` (the PR check will verify exit intent)

2. **Select the workflow:**
- Choose "Release" from the workflow list
**If working directly on `main`:**
- Skip this step and proceed to step 2

3. **Run the workflow:**
2. **Run the workflow:**
- Navigate to: <https://github.com/hypercerts-org/hypercerts-lexicon/actions/workflows/release.yml>
- Click "Run workflow"
- Select the branch (typically `main`)
- Select the branch: **`main`** (the workflow automatically detects this is a stable release)
- Click "Run workflow" to start

4. **What happens:**
3. **What happens:**
- The workflow validates the code and regenerates TypeScript types
- If `pre.json` exists (from `develop`), verifies that prerelease mode has been exited
- If there are pending changesets, it creates a "Release Pull Request"
- The Release PR will remove prerelease tags (if any) and exit prerelease mode
- Merge the Release PR to publish to npm with the `latest` tag
- If no changesets exist, nothing is published

## Publishing a Beta Release

To publish a beta/prerelease version:

1. **Navigate to GitHub Actions:**
- Go to the "Actions" tab

2. **Select the workflow:**
- Choose "Release Beta"

3. **Run the workflow:**
1. **Run the workflow:**
- Navigate to: <https://github.com/hypercerts-org/hypercerts-lexicon/actions/workflows/release.yml>
- Click "Run workflow"
- Select the branch (must be `main`)
- Select the branch: **`develop`** (the workflow automatically detects this is a beta release)
- Click "Run workflow"

4. **What happens:**
- The workflow enters beta prerelease mode (if not already)
2. **What happens:**
- The workflow validates the code and regenerates TypeScript types
- Enters beta prerelease mode (if not already) and commits `pre.json` to `develop`
- Versions packages based on pending changesets
- Publishes to npm with the `beta` tag
- Version format: `0.9.0-beta.1`, `0.9.0-beta.2`, etc.
- Commits and pushes version changes back to the repository
- Commits and pushes version changes back to the `develop` branch

**Note:** This workflow requires the `RELEASE_PAT` secret to bypass
branch protection rules when pushing version changes.
**Note:** Beta releases run on the `develop` branch, so no `RELEASE_PAT` is needed (unlike protected `main` branch).

## Validating Releases (PRs)

When you open a pull request, the "PR Check" workflow automatically
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 `pre.json` exists without exit intent** - This only applies
when merging from `develop` to `main`. It ensures prerelease mode has been
properly exited before merging. Direct work on `main` doesn't have `pre.json`,
so this check is skipped.

This helps ensure all user-facing changes are properly documented
before merging.
This helps ensure all user-facing changes are properly documented and that
prerelease mode is correctly managed when using the `develop` → `main` flow.

## Version Management

Expand Down