Skip to content

Commit c6c390e

Browse files
jrolfsclaude
andcommitted
ci: replace release workflow with Changesets
The hand-rolled workflow_dispatch release script bumped one package at a time, hand-synced inter-package ^ ranges with jq, and omitted @pascal-app/nodes entirely (it could never be released). Replace it with Changesets driving an automated "Version Packages" PR flow. - Add @changesets/cli and .changeset/config.json. Set onlyUpdatePeerDependentsWhenOutOfRange so a core patch doesn't force a major bump on its four peer-dependents. - Convert internal @pascal-app/* refs to the workspace: protocol, preserving range semantics (^x.y.z -> workspace:^, * -> workspace:*). - Rewrite .github/workflows/release.yml to use changesets/action on push to main: opens/updates the Version PR, publishes on merge. - Publish via scripts/publish.ts: changeset publish does not resolve the workspace: protocol under Bun, so publish each package with `bun publish` (which does) and tag with `changeset tag`. - Document the changeset flow in README and CONTRIBUTING. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent 46f94b9 commit c6c390e

13 files changed

Lines changed: 273 additions & 248 deletions

File tree

.changeset/README.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
# Changesets
2+
3+
Hello and welcome! This folder has been automatically generated by `@changesets/cli`, a build tool that works
4+
with multi-package repos, or single-package repos to help you version and publish your code. You can
5+
find the full documentation for it [in our repository](https://github.com/changesets/changesets).
6+
7+
We have a quick list of common questions to get you started engaging with this project in
8+
[our documentation](https://github.com/changesets/changesets/blob/main/docs/common-questions.md).

.changeset/config.json

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
{
2+
"$schema": "https://unpkg.com/@changesets/config@3.1.4/schema.json",
3+
"changelog": "@changesets/cli/changelog",
4+
"commit": false,
5+
"fixed": [],
6+
"linked": [],
7+
"access": "public",
8+
"baseBranch": "main",
9+
"updateInternalDependencies": "patch",
10+
"ignore": [],
11+
"___experimentalUnsafeOptions_WILL_CHANGE_IN_PATCH": {
12+
"onlyUpdatePeerDependentsWhenOutOfRange": true
13+
}
14+
}

.github/workflows/release.yml

Lines changed: 20 additions & 195 deletions
Original file line numberDiff line numberDiff line change
@@ -1,40 +1,19 @@
11
name: Release
22

33
on:
4-
workflow_dispatch:
5-
inputs:
6-
package:
7-
description: "Package to release"
8-
required: true
9-
type: choice
10-
options:
11-
- core
12-
- viewer
13-
- editor
14-
- mcp
15-
- ifc-converter
16-
- all
17-
bump:
18-
description: "Version bump (use 'none' to publish current version as-is)"
19-
required: true
20-
type: choice
21-
options:
22-
- patch
23-
- minor
24-
- major
25-
- none
26-
dry-run:
27-
description: "Dry run (no publish)"
28-
required: false
29-
type: boolean
30-
default: false
4+
push:
5+
branches: [main]
6+
7+
concurrency: ${{ github.workflow }}-${{ github.ref }}
8+
9+
permissions:
10+
contents: write
11+
pull-requests: write
3112

3213
jobs:
3314
release:
3415
runs-on: ubuntu-latest
3516
environment: npm
36-
permissions:
37-
contents: write
3817
steps:
3918
- uses: actions/checkout@v4
4019
with:
@@ -50,172 +29,18 @@ jobs:
5029
- name: Install dependencies
5130
run: bun install --frozen-lockfile
5231

53-
- name: Configure git
54-
run: |
55-
git config user.name "github-actions[bot]"
56-
git config user.email "github-actions[bot]@users.noreply.github.com"
57-
58-
- name: Bump versions and sync inter-package references
59-
run: |
60-
BUMP=${{ inputs.bump }}
61-
TARGET=${{ inputs.package }}
62-
63-
bump_version() {
64-
local v=$1
65-
IFS='.' read -r MAJ MIN PAT <<< "$v"
66-
if [ "$BUMP" = "major" ]; then MAJ=$((MAJ+1)); MIN=0; PAT=0; fi
67-
if [ "$BUMP" = "minor" ]; then MIN=$((MIN+1)); PAT=0; fi
68-
if [ "$BUMP" = "patch" ]; then PAT=$((PAT+1)); fi
69-
if [ "$BUMP" = "none" ]; then echo "$v"; return; fi
70-
echo "$MAJ.$MIN.$PAT"
71-
}
72-
73-
# Track new versions in shell-local vars.
74-
# NOTE: $GITHUB_ENV writes don't surface within the same step, so the
75-
# peerDeps sync below must use shell vars, not env indirection.
76-
declare -A NEW_VERSIONS
77-
78-
for pkg in core viewer editor mcp ifc-converter; do
79-
if [ "$TARGET" = "$pkg" ] || [ "$TARGET" = "all" ]; then
80-
CUR=$(jq -r '.version' packages/$pkg/package.json)
81-
NEW=$(bump_version "$CUR")
82-
jq --arg v "$NEW" '.version = $v' packages/$pkg/package.json > tmp.json && mv tmp.json packages/$pkg/package.json
83-
NEW_VERSIONS[$pkg]=$NEW
84-
UPPER=$(echo "$pkg" | tr '[:lower:]' '[:upper:]' | tr '-' '_')
85-
# Also export for the publish/commit/tag steps that follow.
86-
echo "${UPPER}_VERSION=$NEW" >> $GITHUB_ENV
87-
echo "Bumped @pascal-app/$pkg: $CUR → $NEW"
88-
fi
89-
done
90-
91-
# Sync inter-package references in peerDependencies and devDependencies.
92-
# Anything that references a bumped @pascal-app/* package is updated to ^NEW.
93-
for pkg in core viewer editor mcp ifc-converter; do
94-
FILE=packages/$pkg/package.json
95-
for dep in core viewer editor mcp ifc-converter; do
96-
VAL="${NEW_VERSIONS[$dep]}"
97-
[ -z "$VAL" ] && continue
98-
jq --arg name "@pascal-app/$dep" --arg v "^$VAL" '
99-
if .peerDependencies[$name] then .peerDependencies[$name] = $v else . end
100-
| if .devDependencies[$name] then .devDependencies[$name] = $v else . end
101-
' "$FILE" > tmp.json && mv tmp.json "$FILE"
102-
done
103-
done
104-
105-
echo "=== @pascal-app/* refs after sync ==="
106-
for pkg in core viewer editor mcp ifc-converter; do
107-
echo "--- packages/$pkg/package.json ---"
108-
jq '{ peerDependencies: (.peerDependencies // {} | with_entries(select(.key | startswith("@pascal-app/")))), devDependencies: (.devDependencies // {} | with_entries(select(.key | startswith("@pascal-app/")))) }' packages/$pkg/package.json
109-
done
110-
111-
- name: Build & publish core
112-
if: inputs.package == 'core' || inputs.package == 'all'
113-
working-directory: packages/core
114-
env:
115-
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
116-
run: |
117-
bun run build
118-
if [ "${{ inputs.dry-run }}" = "true" ]; then
119-
echo "🏜️ Dry run — would publish @pascal-app/core@$CORE_VERSION"
120-
npm publish --dry-run --access public
121-
else
122-
npm publish --access public
123-
echo "📦 Published @pascal-app/core@$CORE_VERSION"
124-
fi
125-
126-
- name: Build & publish viewer
127-
if: inputs.package == 'viewer' || inputs.package == 'all'
128-
working-directory: packages/viewer
129-
env:
130-
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
131-
run: |
132-
bun run build
133-
if [ "${{ inputs.dry-run }}" = "true" ]; then
134-
echo "🏜️ Dry run — would publish @pascal-app/viewer@$VIEWER_VERSION"
135-
npm publish --dry-run --access public
136-
else
137-
npm publish --access public
138-
echo "📦 Published @pascal-app/viewer@$VIEWER_VERSION"
139-
fi
140-
141-
- name: Publish editor
142-
if: inputs.package == 'editor' || inputs.package == 'all'
143-
working-directory: packages/editor
144-
env:
145-
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
146-
run: |
147-
if [ "${{ inputs.dry-run }}" = "true" ]; then
148-
echo "🏜️ Dry run — would publish @pascal-app/editor@$EDITOR_VERSION"
149-
npm publish --dry-run --access public
150-
else
151-
npm publish --access public
152-
echo "📦 Published @pascal-app/editor@$EDITOR_VERSION"
153-
fi
154-
155-
- name: Build & publish mcp
156-
if: inputs.package == 'mcp' || inputs.package == 'all'
157-
working-directory: packages/mcp
158-
env:
159-
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
160-
run: |
161-
bun run build
162-
if [ "${{ inputs.dry-run }}" = "true" ]; then
163-
echo "🏜️ Dry run — would publish @pascal-app/mcp@$MCP_VERSION"
164-
npm publish --dry-run --access public
165-
else
166-
npm publish --access public
167-
echo "📦 Published @pascal-app/mcp@$MCP_VERSION"
168-
fi
32+
- name: Build packages
33+
run: bun run build
16934

170-
- name: Build & publish ifc-converter
171-
if: inputs.package == 'ifc-converter' || inputs.package == 'all'
35+
# Opens/updates a "Version Packages" PR when changesets are pending, and
36+
# publishes (via the `publish` command) once that PR is merged to main.
37+
- name: Create release PR or publish
38+
uses: changesets/action@v1
39+
with:
40+
version: bun run ci:version
41+
publish: bun run ci:publish
42+
commit: "chore: version packages"
43+
title: "📦 Version Packages"
17244
env:
45+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
17346
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
174-
run: |
175-
# ifc-converter depends on @pascal-app/core (workspace) — build it first
176-
bun run build --filter @pascal-app/core 2>/dev/null || (cd packages/core && bun run build)
177-
cd packages/ifc-converter
178-
bun run build
179-
if [ "${{ inputs.dry-run }}" = "true" ]; then
180-
echo "🏜️ Dry run — would publish @pascal-app/ifc-converter@$IFC_CONVERTER_VERSION"
181-
npm publish --dry-run --access public
182-
else
183-
npm publish --access public
184-
echo "📦 Published @pascal-app/ifc-converter@$IFC_CONVERTER_VERSION"
185-
fi
186-
187-
- name: Commit version bumps & tag
188-
if: inputs.dry-run == false
189-
run: |
190-
git add -A
191-
PKGS=""
192-
TAGS=""
193-
194-
if [ -n "$CORE_VERSION" ]; then
195-
PKGS="$PKGS @pascal-app/core@$CORE_VERSION"
196-
TAGS="$TAGS @pascal-app/core@$CORE_VERSION"
197-
fi
198-
if [ -n "$VIEWER_VERSION" ]; then
199-
PKGS="$PKGS @pascal-app/viewer@$VIEWER_VERSION"
200-
TAGS="$TAGS @pascal-app/viewer@$VIEWER_VERSION"
201-
fi
202-
if [ -n "$EDITOR_VERSION" ]; then
203-
PKGS="$PKGS @pascal-app/editor@$EDITOR_VERSION"
204-
TAGS="$TAGS @pascal-app/editor@$EDITOR_VERSION"
205-
fi
206-
if [ -n "$MCP_VERSION" ]; then
207-
PKGS="$PKGS @pascal-app/mcp@$MCP_VERSION"
208-
TAGS="$TAGS @pascal-app/mcp@$MCP_VERSION"
209-
fi
210-
if [ -n "$IFC_CONVERTER_VERSION" ]; then
211-
PKGS="$PKGS @pascal-app/ifc-converter@$IFC_CONVERTER_VERSION"
212-
TAGS="$TAGS @pascal-app/ifc-converter@$IFC_CONVERTER_VERSION"
213-
fi
214-
215-
git commit -m "release:${PKGS}"
216-
217-
for TAG in $TAGS; do
218-
git tag "$TAG"
219-
done
220-
221-
git push --follow-tags

CONTRIBUTING.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -49,8 +49,9 @@ A key rule: **`packages/viewer` must never import from `apps/editor`**. The view
4949
1. **Fork the repo** and create a branch from `main`
5050
2. **Make your changes** and test locally with `bun dev`
5151
3. **Run `bun check`** to make sure linting passes
52-
4. **Open a PR** with a clear description of what changed and why
53-
5. **Link related issues** if applicable (e.g., "Fixes #42")
52+
4. **Add a changeset** if you changed a published `@pascal-app/*` package — run `bun changeset`, pick the affected packages and bump type, and commit the generated file. This drives the automated release; see [Publishing Packages](README.md#publishing-packages).
53+
5. **Open a PR** with a clear description of what changed and why
54+
6. **Link related issues** if applicable (e.g., "Fixes #42")
5455

5556
### PR tips
5657

README.md

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -382,14 +382,19 @@ turbo build --filter=@pascal-app/core
382382

383383
### Publishing Packages
384384

385-
```bash
386-
# Build packages
387-
turbo build --filter=@pascal-app/core --filter=@pascal-app/viewer
385+
Releases are managed with [Changesets](https://changesets.dev). Publishing is automated — you don't run `npm publish` by hand.
388386

389-
# Publish to npm
390-
npm publish --workspace=@pascal-app/core --access public
391-
npm publish --workspace=@pascal-app/viewer --access public
392-
```
387+
1. In any PR that changes a published package, add a changeset describing the change:
388+
389+
```bash
390+
bun changeset
391+
```
392+
393+
Pick the affected packages and bump type (patch/minor/major); commit the generated file in `.changeset/`.
394+
395+
2. When the PR merges to `main`, the `Release` workflow opens (or updates) a **Version Packages** PR that applies the pending changesets — bumping versions and writing changelogs.
396+
397+
3. Merging that **Version Packages** PR publishes the affected packages to npm and tags the release.
393398

394399
---
395400

0 commit comments

Comments
 (0)