Skip to content

Commit 99fe989

Browse files
authored
Enforce changesets on all PRs (#2629)
1 parent 27f7dc9 commit 99fe989

1 file changed

Lines changed: 190 additions & 0 deletions

File tree

Lines changed: 190 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,190 @@
1+
# Changeset Enforcement Action
2+
#
3+
# This workflow ensures that when developers make changes to package code,
4+
# they include a "changeset" file that describes what changed and how it
5+
# should affect the version number (major/minor/patch).
6+
#
7+
# Changesets are used by the @changesets/cli tool to automatically:
8+
# - Generate changelogs
9+
# - Determine version bumps
10+
# - Coordinate releases across multiple packages in this monorepo
11+
#
12+
# The check can be bypassed by adding the "skip-changeset" label to a PR
13+
# (useful for docs-only changes, CI fixes, etc. that don't need version bumps)
14+
15+
name: Changeset Check
16+
17+
on:
18+
pull_request:
19+
20+
jobs:
21+
changeset-check:
22+
name: Check for Changeset
23+
runs-on: ubuntu-latest
24+
# Skip draft PRs - only run on PRs ready for review
25+
if: github.event.pull_request.draft == false
26+
steps:
27+
- name: Checkout
28+
uses: actions/checkout@v4
29+
with:
30+
# Need full history to compare against base branch
31+
fetch-depth: 0
32+
33+
- name: Setup Node.js
34+
uses: actions/setup-node@v4
35+
with:
36+
node-version: "18"
37+
cache: "npm"
38+
39+
- name: Install dependencies
40+
run: npm ci
41+
42+
# Check if PR has the skip-changeset label to bypass this check
43+
- name: Check for opt-out label
44+
id: check-labels
45+
uses: actions/github-script@v7
46+
with:
47+
script: |
48+
const labels = context.payload.pull_request.labels.map(label => label.name);
49+
const hasOptOut = labels.includes('skip-changeset');
50+
51+
core.setOutput('skip-changeset', hasOptOut);
52+
53+
if (hasOptOut) {
54+
core.info('Changeset check skipped due to skip-changeset label');
55+
}
56+
57+
# Use changesets CLI to check if changeset is needed for this PR
58+
- name: Check changeset status
59+
id: changeset-check
60+
if: steps.check-labels.outputs.skip-changeset != 'true'
61+
run: |
62+
# First check what packages have changed
63+
echo "Checking for package changes..."
64+
CHANGED_FILES=$(git diff --name-only origin/${{ github.base_ref }}...HEAD)
65+
PACKAGE_CHANGES=$(echo "$CHANGED_FILES" | grep -E "(packages/.*/src/|packages/.*/package\.json)" || true)
66+
67+
if [ -z "$PACKAGE_CHANGES" ]; then
68+
echo "status=no-changes" >> $GITHUB_OUTPUT
69+
echo "ℹ️ No package changes detected, changeset not required"
70+
exit 0
71+
fi
72+
73+
echo "📦 Package changes detected:"
74+
echo "$PACKAGE_CHANGES"
75+
76+
# Now check changeset status - capture both output and exit code
77+
echo "Checking changeset status..."
78+
set +e # Don't exit on error
79+
npx changeset status --since=origin/${{ github.base_ref }} --verbose > changeset-output.txt 2>&1
80+
EXIT_CODE=$?
81+
set -e # Re-enable exit on error
82+
83+
echo "Changeset status output:"
84+
cat changeset-output.txt
85+
86+
if [ $EXIT_CODE -eq 0 ]; then
87+
echo "status=valid" >> $GITHUB_OUTPUT
88+
echo "✅ Changeset requirements satisfied"
89+
else
90+
echo "status=missing" >> $GITHUB_OUTPUT
91+
echo "❌ Missing changeset for package changes"
92+
fi
93+
94+
# Clean up old changeset comments to avoid PR pollution
95+
- name: Clean up old comments
96+
uses: actions/github-script@v7
97+
with:
98+
script: |
99+
// Find and delete old changeset comments from this bot
100+
const comments = await github.rest.issues.listComments({
101+
issue_number: context.issue.number,
102+
owner: context.repo.owner,
103+
repo: context.repo.repo,
104+
});
105+
106+
const botComments = comments.data.filter(comment =>
107+
comment.user.type === 'Bot' &&
108+
comment.body.includes('📦 Changeset Required')
109+
);
110+
111+
for (const comment of botComments) {
112+
await github.rest.issues.deleteComment({
113+
owner: context.repo.owner,
114+
repo: context.repo.repo,
115+
comment_id: comment.id,
116+
});
117+
console.log(`Deleted old changeset comment: ${comment.id}`);
118+
}
119+
120+
# Add a helpful comment when changeset is missing
121+
- name: Comment on PR - Missing Changeset
122+
if: |
123+
steps.check-labels.outputs.skip-changeset != 'true' &&
124+
steps.changeset-check.outputs.status == 'missing'
125+
uses: actions/github-script@v7
126+
with:
127+
script: |
128+
const comment = `## 📦 Changeset Required
129+
130+
This PR appears to modify package code but doesn't include a changeset.
131+
132+
A changeset helps track version changes and generate release notes.
133+
134+
### To add a changeset:
135+
\`\`\`bash
136+
npm run changeset
137+
\`\`\`
138+
139+
### To skip this check (if no version bump is needed):
140+
Add the \`skip-changeset\` label to this PR`;
141+
142+
github.rest.issues.createComment({
143+
issue_number: context.issue.number,
144+
owner: context.repo.owner,
145+
repo: context.repo.repo,
146+
body: comment
147+
});
148+
149+
# Determine final status and fail the check if needed
150+
- name: Set final status
151+
id: final-status
152+
run: |
153+
if [ "${{ steps.check-labels.outputs.skip-changeset }}" = "true" ]; then
154+
echo "status=skipped" >> $GITHUB_OUTPUT
155+
echo "✅ Changeset check skipped (skip-changeset label detected)"
156+
elif [ "${{ steps.changeset-check.outputs.status }}" = "no-changes" ]; then
157+
echo "status=not-required" >> $GITHUB_OUTPUT
158+
echo "✅ Changeset not required (no package changes)"
159+
elif [ "${{ steps.changeset-check.outputs.status }}" = "valid" ]; then
160+
echo "status=success" >> $GITHUB_OUTPUT
161+
echo "✅ Changeset requirements satisfied"
162+
else
163+
echo "status=missing" >> $GITHUB_OUTPUT
164+
echo "❌ Missing required changeset"
165+
exit 1
166+
fi
167+
168+
# Create a summary for the GitHub Actions UI
169+
- name: Summary
170+
run: |
171+
case "${{ steps.final-status.outputs.status }}" in
172+
"skipped")
173+
echo "### ✅ Changeset Check: Skipped" >> $GITHUB_STEP_SUMMARY
174+
echo "The changeset requirement was bypassed using the skip-changeset label." >> $GITHUB_STEP_SUMMARY
175+
;;
176+
"not-required")
177+
echo "### ✅ Changeset Check: Not Required" >> $GITHUB_STEP_SUMMARY
178+
echo "No package changes detected, so no changeset is needed." >> $GITHUB_STEP_SUMMARY
179+
;;
180+
"success")
181+
echo "### ✅ Changeset Check: Passed" >> $GITHUB_STEP_SUMMARY
182+
echo "Changeset requirements satisfied for this PR." >> $GITHUB_STEP_SUMMARY
183+
;;
184+
"missing")
185+
echo "### ❌ Changeset Check: Failed" >> $GITHUB_STEP_SUMMARY
186+
echo "Package changes detected but no valid changeset found." >> $GITHUB_STEP_SUMMARY
187+
echo "" >> $GITHUB_STEP_SUMMARY
188+
echo "**To fix:** Run \`npm run changeset\` or add the \`skip-changeset\` label if no version bump is needed." >> $GITHUB_STEP_SUMMARY
189+
;;
190+
esac

0 commit comments

Comments
 (0)