feat: implement changesets in repo #51
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# ------------------------------------------------------------- | |
# 🚢 Changesets – Create/Update Release PR and Cut Tags | |
# Two paths: | |
# 1) PR path: show a Changesets preview comment on the PR (no publishing) | |
# 2) Main path: on push to main, create/update the Release PR and cut tags/GitHub Releases | |
# ------------------------------------------------------------- | |
name: Changesets – Create/Update Release PR and Cut Tags | |
on: | |
# Run on PRs to main so contributors see what would be released | |
pull_request: | |
branches: ["main"] | |
# Only run if one of the following events occurs | |
types: [opened, synchronize, reopened, ready_for_review] | |
push: | |
branches: ["main"] | |
workflow_dispatch: | |
inputs: | |
action: | |
description: "Choose an action" | |
required: true | |
default: "version" | |
type: choice | |
options: | |
- version | |
- release | |
permissions: | |
# Needed to push version commit + create releases | |
contents: write | |
# Needed to open/update the Release PR | |
pull-requests: write | |
jobs: | |
# PR PATH: Preview only | |
preview: | |
if: github.event_name == 'pull_request' | |
name: 🔎 Changesets Preview (PR) | |
runs-on: ubuntu-24.04 | |
concurrency: | |
group: changesets-preview-${{ github.event.pull_request.number }} | |
cancel-in-progress: true | |
steps: | |
# Check out the PR HEAD commit (not the merge ref) to report the correct plan | |
- name: ⏬ Checkout PR head (not merge ref) | |
uses: actions/checkout@v4 | |
with: | |
fetch-depth: 0 | |
ref: ${{ github.event.pull_request.head.sha }} # Force checkout to the PR head ref, not the merge ref | |
- name: 🔄 Init Node & NPM cache | |
uses: ./.github/actions/npm-cache | |
# Make sure we have full history and the base branch locally for proper diffing | |
- name: 🔁 Ensure full history & fetch base (main) | |
shell: bash | |
run: | | |
set -euo pipefail | |
if [ -f .git/shallow ]; then | |
git fetch --unshallow --no-tags --prune | |
fi | |
git fetch --no-tags --prune origin +refs/heads/main:refs/remotes/origin/main | |
git branch -a | |
git rev-parse HEAD | |
git rev-parse remotes/origin/main | |
# Produce human-readable Changesets status and expose it for the PR comment | |
- name: 📋 Show changeset status (since origin/main) | |
id: status | |
shell: bash | |
run: | | |
set -euo pipefail | |
npx changeset status --since=origin/main --verbose > .changeset-status.txt || { | |
echo "⚠️ changeset status failed; falling back to listing existing changesets" | |
ls -1 .changeset/*.md 2>/dev/null || true | |
: > .changeset-status.txt | |
} | |
{ | |
printf 'STATUS<<EOF\n' | |
cat .changeset-status.txt | |
printf '\nEOF\n' | |
} >> "$GITHUB_OUTPUT" | |
# Upsert a single bot comment on the PR with the status preview | |
- name: 💬 Upsert PR comment with preview | |
uses: actions/github-script@v7 | |
env: | |
BODY: ${{ steps.status.outputs.STATUS }} | |
with: | |
script: | | |
const { owner, repo, number } = context.issue; | |
const marker = '<!-- changesets-preview-marker -->'; | |
const header = '### Changesets Preview (since origin/main)\n\n'; | |
const raw = process.env.BODY || '_No status available._'; | |
const body = `${header}\n\`\`\`\n${raw}\n\`\`\`\n\n${marker}`; | |
const { data: comments } = await github.rest.issues.listComments({ owner, repo, issue_number: number, per_page: 100 }); | |
const existing = comments.find(c => c.user?.type === 'Bot' && c.body?.includes(marker)); | |
if (existing) { | |
await github.rest.issues.updateComment({ owner, repo, comment_id: existing.id, body }); | |
} else { | |
await github.rest.issues.createComment({ owner, repo, issue_number: number, body }); | |
} | |
# MAIN PATH: Create/Update Release PR & cut tags/releases | |
release_pr: | |
# Only run on main pushes (merge to main done) to drive versioning + tags | |
if: github.event_name == 'push' && startsWith(github.ref, 'refs/heads/main') | |
name: 🧩 Create/Update Release PR (and cut tags/releases) | |
runs-on: ubuntu-24.04 | |
concurrency: | |
group: changesets-main-${{ github.ref }} | |
cancel-in-progress: true | |
steps: | |
# Work on the main branch that was just pushed | |
- name: ⏬ Checkout main | |
uses: actions/checkout@v4 | |
with: | |
fetch-depth: 0 | |
- name: 🔄 Init Node & NPM cache | |
uses: ./.github/actions/npm-cache | |
# Let changesets open/update the "Version Packages" PR, commit bumped versions and create GitHub Releases/tags | |
- name: 🧩 Changesets Action | |
id: changesets | |
uses: changesets/action@v1 | |
with: | |
commit: "chore(release): version packages" | |
title: "chore(release): version packages" | |
# Let the action create GitHub releases for you | |
createGithubReleases: true | |
publish: "echo 'Publishing handled by release.yml'" | |
env: | |
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
# Ensure Husky hooks (e.g., branch name patterns) don't block CI commits | |
HUSKY: "0" | |
CI: "true" | |
# Write a brief job summary so reviewers see what happened | |
- name: 🖨️ Summary | |
if: always() | |
run: | | |
if [ "${{ steps.changesets.outputs.hasChangesets }}" = "true" ]; then | |
echo "### Changesets\n- Release PR was created or updated." >> $GITHUB_STEP_SUMMARY | |
fi | |
if [ "${{ steps.changesets.outputs.published }}" = "true" ]; then | |
echo "### Tags & Releases\n- New tags and GitHub Releases were created by Changesets.\n- Your **release.yml** will now build and publish." >> $GITHUB_STEP_SUMMARY | |
fi |