Skip to content

Commit f9147ae

Browse files
authored
Merge pull request #52 from hypercerts-org/copilot/update-release-workflows-to-npm-trusted-publishers
2 parents 98ecd47 + 164bc4f commit f9147ae

File tree

3 files changed

+174
-137
lines changed

3 files changed

+174
-137
lines changed

.github/workflows/release-beta.yml

Lines changed: 0 additions & 84 deletions
This file was deleted.

.github/workflows/release.yml

Lines changed: 82 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,37 +11,113 @@ jobs:
1111
permissions:
1212
contents: write
1313
pull-requests: write
14+
# Required for npm Trusted Publishers via GitHub OIDC
15+
# See: https://docs.npmjs.com/trusted-publishers
1416
id-token: write
1517

1618
steps:
1719
- uses: actions/checkout@v6
1820
with:
1921
fetch-depth: 0
22+
23+
# Branch validation: Only allow develop (beta) or main (stable)
24+
- name: Validate branch
25+
run: |
26+
if [ "${{ github.ref }}" == "refs/heads/develop" ]; then
27+
echo "RELEASE_TYPE=beta" >> $GITHUB_ENV
28+
echo "Detected beta release from develop branch"
29+
elif [ "${{ github.ref }}" == "refs/heads/main" ]; then
30+
echo "RELEASE_TYPE=stable" >> $GITHUB_ENV
31+
echo "Detected stable release from main branch"
32+
else
33+
echo "Error: This workflow can only be run from 'develop' (beta) or 'main' (stable) branches"
34+
exit 1
35+
fi
36+
2037
- uses: actions/setup-node@v6
2138
with:
2239
node-version: 20
2340
cache: "npm"
41+
# registry-url is required for npm Trusted Publishers
2442
registry-url: "https://registry.npmjs.org"
43+
2544
- run: npm ci
2645
- run: npm run check
2746

28-
- name: Create .npmrc
47+
# Stable release: Verify prerelease mode has been exited
48+
# The exit intent should already be set on develop before merging to main
49+
- name: Verify prerelease mode exit
50+
if: env.RELEASE_TYPE == 'stable'
2951
run: |
30-
echo "//registry.npmjs.org/:_authToken=${{ secrets.NPM_TOKEN }}" > .npmrc
31-
echo "@hypercerts-org:registry=https://registry.npmjs.org/" >> .npmrc
52+
if [ -f .changeset/pre.json ]; then
53+
# Check if prerelease mode has been exited (exit intent set)
54+
if ! grep -q '"exit": true' .changeset/pre.json 2>/dev/null; then
55+
echo "Error: Prerelease mode must be exited before merging to main."
56+
echo "Run 'npm run changeset pre exit' on the develop branch and commit the change."
57+
exit 1
58+
fi
59+
echo "Prerelease mode exit intent confirmed - changeset version will handle the exit"
60+
else
61+
echo "No prerelease mode detected (pre.json not present)"
62+
fi
3263
64+
# Beta-specific: Enter prerelease mode (if not already)
65+
- name: Enter prerelease mode (if not already)
66+
if: env.RELEASE_TYPE == 'beta'
67+
run: |
68+
if [ ! -f .changeset/pre.json ]; then
69+
npx changeset pre enter beta
70+
git config user.name "github-actions[bot]"
71+
git config user.email "github-actions[bot]@users.noreply.github.com"
72+
git add .changeset/pre.json
73+
git commit -m "chore: enter beta prerelease mode"
74+
git push
75+
fi
76+
77+
# Beta-specific: Version packages manually
78+
- name: Version packages
79+
if: env.RELEASE_TYPE == 'beta'
80+
run: npm run version-packages
81+
env:
82+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
83+
84+
# No .npmrc creation needed - npm Trusted Publishers uses GitHub OIDC tokens
85+
# automatically via the id-token: write permission and registry-url configuration
86+
87+
# Stable release: Use changesets/action which handles versioning and publishing
3388
- name: Create Release Pull Request or Publish
89+
if: env.RELEASE_TYPE == 'stable'
3490
id: changesets
3591
uses: changesets/action@e0145edc7d9d8679003495b11f87bd8ef63c0cba # v1.5.3
3692
with:
3793
publish: npm run release
3894
version: npm run version-packages
39-
title: "chore: release packages"
40-
commit: "chore: release packages"
95+
title: "chore: release package"
96+
commit: "chore: release package"
4197
env:
4298
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
4399
NPM_CONFIG_PROVENANCE: true
44100

101+
# Beta-specific: Publish beta packages directly
102+
- name: Publish beta packages
103+
if: env.RELEASE_TYPE == 'beta'
104+
# Use npm run release to match regular releases - it runs check before publishing
105+
# to ensure validation after versioning (package.json/changelog changes)
106+
run: npm run release
107+
env:
108+
NPM_CONFIG_PROVENANCE: true
109+
110+
# Beta-specific: Commit and push version changes
111+
- name: Commit and push version changes
112+
if: env.RELEASE_TYPE == 'beta'
113+
run: |
114+
git config user.name "github-actions[bot]"
115+
git config user.email "github-actions[bot]@users.noreply.github.com"
116+
git add -A
117+
git diff --staged --quiet || git commit -m "chore: version packages (beta)"
118+
git push
119+
120+
# Stable release: Log published packages
45121
- name: Log published packages
46-
if: steps.changesets.outputs.published == 'true'
122+
if: env.RELEASE_TYPE == 'stable' && steps.changesets.outputs.published == 'true'
47123
run: echo "Published - ${{ steps.changesets.outputs.publishedPackages }}"

PUBLISHING.md

Lines changed: 92 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -1,33 +1,61 @@
11
# Publishing Guide for Maintainers
22

33
This document describes how to publish the `@hypercerts-org/lexicon`
4-
package to npm using GitHub Actions workflows with Changesets. All
5-
publishing workflows are manually triggered to give you full control
6-
over when releases are made.
4+
package to npm using GitHub Actions workflows with Changesets. The
5+
workflow uses separate branches for beta and stable releases, with all
6+
releases manually triggered to give you full control over when releases
7+
are made.
8+
9+
## Branch Strategy
10+
11+
- **`develop` branch**: Beta/prerelease versions
12+
- **`main` branch**: Stable releases
13+
14+
**Suggested Flow:** `feature``develop` (beta) → `main` (stable)
15+
16+
**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.
17+
18+
### Release Flow
19+
20+
```text
21+
┌──────────────────────────────────────────────────────────────────┐
22+
│ │
23+
│ feature/* ──────────────────┐ │
24+
│ │ │ │
25+
│ │ npm run changeset │ PR │
26+
│ │ (describe changes) │ │
27+
│ ▼ ▼ │
28+
│ ┌─────────┐ merge ┌──────────┐ │
29+
│ │ commit │ ────────► │ develop │ ──► Manual publish @beta │
30+
│ │ + .md │ └────┬─────┘ (0.9.0-beta.1) │
31+
│ └─────────┘ │ │
32+
│ │ PR (after: npm changeset pre exit) │
33+
│ ▼ │
34+
│ ┌────────┐ │
35+
│ │ main │ ──► Creates "Release PR" │
36+
│ └────┬───┘ │
37+
│ │ │
38+
│ │ merge Release PR │
39+
│ ▼ │
40+
│ ┌──────────────┐ │
41+
│ │ npm publish │ ──► @latest (0.9.0) │
42+
│ └──────────────┘ │
43+
│ │
44+
└──────────────────────────────────────────────────────────────────┘
45+
```
746

847
## Prerequisites
948

1049
Before publishing, ensure you have:
1150

12-
1. **GitHub Secrets configured:**
13-
- `RELEASE_PAT`: GitHub Personal Access Token with permissions to bypass
14-
branch protection rules. Required for the beta release workflow that
15-
commits and pushes version changes to protected branches.
16-
- **Recommended:** Fine-grained Personal Access Token
17-
- Create at: <https://github.com/settings/tokens?type=beta>
18-
- Repository access: Select this repository
19-
- Repository permissions: `Contents` (read and write), `Metadata`
20-
(read-only)
21-
- **Alternative:** Classic Personal Access Token (if fine-grained
22-
doesn't work with your branch protection settings)
23-
- Create at: <https://github.com/settings/tokens>
24-
- Required scopes: `repo` (full control of private repositories)
25-
- This is needed because `GITHUB_TOKEN` cannot bypass branch protection
26-
- **Note:** Ensure branch protection rules allow the token to bypass
27-
protection (uncheck "Do not allow bypassing the above settings" or
28-
add the token as an allowed actor)
29-
- `NPM_TOKEN`: npm access token with publish permissions for
30-
`@hypercerts-org` scope
51+
1. **npm Trusted Publisher configured:**
52+
- The workflow uses npm Trusted Publishers via GitHub OIDC for secure,
53+
token-less publishing
54+
- Configure on npmjs.com: Package settings → Publishing access → Add a
55+
GitHub Actions publisher
56+
- Workflow name: `Release`
57+
- See: <https://docs.npmjs.com/trusted-publishers>
58+
- No `NPM_TOKEN` secret is required
3159

3260
## Adding Changesets
3361

@@ -49,66 +77,83 @@ This will:
4977
collisions when multiple contributors create changesets simultaneously.
5078
The filename doesn't affect version ordering—Changesets uses git history
5179
to determine the order of changes. You can rename these files if desired,
52-
but it's not necessary and not recommended in collaborative environments.
80+
but it's not necessary.
5381

5482
## Publishing a Stable Release
5583

84+
**If merging from `develop``main`:** Before merging, you must exit prerelease mode on the `develop` branch:
85+
86+
```bash
87+
# On develop branch
88+
npm run changeset pre exit
89+
git add .changeset/pre.json
90+
git commit -m "chore: exit prerelease mode"
91+
git push
92+
```
93+
94+
This sets the exit intent in `pre.json`, which is required before merging to `main`. The PR check will verify this.
95+
96+
**If working directly on `main`:** You can skip this step since `pre.json` won't exist.
97+
5698
To publish a stable release to npm:
5799

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

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

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

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

76122
## Publishing a Beta Release
77123

78124
To publish a beta/prerelease version:
79125

80-
1. **Navigate to GitHub Actions:**
81-
- Go to the "Actions" tab
82-
83-
2. **Select the workflow:**
84-
- Choose "Release Beta"
85-
86-
3. **Run the workflow:**
126+
1. **Run the workflow:**
127+
- Navigate to: <https://github.com/hypercerts-org/hypercerts-lexicon/actions/workflows/release.yml>
87128
- Click "Run workflow"
88-
- Select the branch (must be `main`)
129+
- Select the branch: **`develop`** (the workflow automatically detects this is a beta release)
89130
- Click "Run workflow"
90131

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

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

101142
## Validating Releases (PRs)
102143

103-
When you open a pull request, the "PR Check" workflow automatically
144+
When you open a pull request to `main`, the "PR Check" workflow automatically
104145
runs to:
105146

106147
- Check if package changes (lexicons, types, package.json) have
107148
corresponding changesets
108149
- Warn if changesets are missing
150+
- **Reject the PR if `pre.json` exists without exit intent** - This only applies
151+
when merging from `develop` to `main`. It ensures prerelease mode has been
152+
properly exited before merging. Direct work on `main` doesn't have `pre.json`,
153+
so this check is skipped.
109154

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

113158
## Version Management
114159

0 commit comments

Comments
 (0)