1+ name : API Breakage Detection
2+
3+ on :
4+ pull_request :
5+ paths :
6+ - ' packages/**'
7+ - ' tools/snapi/**'
8+ push :
9+ branches :
10+ - main
11+ paths :
12+ - ' packages/**'
13+ - ' tools/snapi/**'
14+
15+ concurrency :
16+ group : api-breakage-${{ github.ref }}
17+ cancel-in-progress : true
18+
19+ jobs :
20+ detect-api-changes :
21+ runs-on : ubuntu-latest
22+ permissions :
23+ contents : read
24+ pull-requests : write
25+ statuses : write
26+
27+ env :
28+ GCS_PROJECT_ID : ${{ secrets.GCS_PROJECT_ID }}
29+ GCS_BUCKET : ${{ secrets.GCS_BUCKET }}
30+
31+ steps :
32+ - name : Checkout code
33+ uses : actions/checkout@v4
34+ with :
35+ fetch-depth : 0 # Need full history
36+
37+ - name : Setup
38+ id : config
39+ uses : ./.github/actions/init-blacksmith
40+ with :
41+ turbo-signature : ${{ secrets.TURBO_REMOTE_CACHE_SIGNATURE_KEY }}
42+ turbo-team : ${{ vars.TURBO_TEAM }}
43+ turbo-token : ${{ secrets.TURBO_TOKEN }}
44+
45+ - name : Build packages
46+ run : pnpm build
47+
48+ - name : Build Brkr-Brkr
49+ run : pnpm turbo build --filter=@clerk/snapi
50+
51+ - name : Run API breakage detection (PR)
52+ if : github.event_name == 'pull_request'
53+ id : api-check-pr
54+ run : |
55+ # Generate current snapshots
56+ node tools/snapi/dist/cli.js snapshot --config snapi.config.json
57+
58+ # Run breaking changes detection
59+ echo "🔍 Detecting API changes..."
60+ if node tools/snapi/dist/cli.js detect --config snapi.config.json --format markdown --output api-changes-report.md; then
61+ echo "api_check_passed=true" >> $GITHUB_OUTPUT
62+ else
63+ echo "api_check_passed=false" >> $GITHUB_OUTPUT
64+ fi
65+
66+ - name : Update baseline cache (main branch)
67+ if : github.ref == 'refs/heads/main' && github.event_name == 'push'
68+ run : |
69+ node tools/snapi/dist/cli.js snapshot --config snapi.config.json --force
70+
71+ # Store as artifacts for backup
72+ echo "COMMIT_SHA=$(git rev-parse HEAD)" >> $GITHUB_ENV
73+
74+ - name : Upload baseline snapshots (main branch)
75+ if : github.ref == 'refs/heads/main' && github.event_name == 'push'
76+ uses : actions/upload-artifact@v4
77+ with :
78+ name : api-baseline-${{ env.COMMIT_SHA }}
79+ path : .api-snapshots/
80+ retention-days : 90
81+
82+ - name : Comment on PR
83+ if : github.event_name == 'pull_request'
84+ uses : actions/github-script@v7
85+ with :
86+ script : |
87+ const fs = require('fs');
88+
89+ // Read the generated report
90+ let reportContent = '';
91+ try {
92+ reportContent = fs.readFileSync('api-changes-report.md', 'utf8');
93+ } catch (error) {
94+ reportContent = '❌ **Error**: Could not generate API changes report.\n\n' + error.message;
95+ }
96+
97+ // Find existing comment
98+ const comments = await github.rest.issues.listComments({
99+ owner: context.repo.owner,
100+ repo: context.repo.repo,
101+ issue_number: context.issue.number
102+ });
103+
104+ const existingComment = comments.data.find(
105+ comment => comment.user.login === 'github-actions[bot]' &&
106+ comment.body.includes('API Changes Report')
107+ );
108+
109+ const commentBody = `## 🔍 API Changes Report
110+
111+ ${reportContent}
112+
113+ ---
114+ <sub>🤖 This comment was automatically generated by the API Breakage Detector</sub>`;
115+
116+ if (existingComment) {
117+ // Update existing comment
118+ await github.rest.issues.updateComment({
119+ owner: context.repo.owner,
120+ repo: context.repo.repo,
121+ comment_id: existingComment.id,
122+ body: commentBody
123+ });
124+ } else {
125+ // Create new comment
126+ await github.rest.issues.createComment({
127+ owner: context.repo.owner,
128+ repo: context.repo.repo,
129+ issue_number: context.issue.number,
130+ body: commentBody
131+ });
132+ }
133+
134+ - name : Set commit status
135+ if : github.event_name == 'pull_request'
136+ uses : actions/github-script@v7
137+ with :
138+ script : |
139+ const apiCheckPassed = '${{ steps.api-check-pr.outputs.api_check_passed }}' === 'true';
140+
141+ await github.rest.repos.createCommitStatus({
142+ owner: context.repo.owner,
143+ repo: context.repo.repo,
144+ sha: context.payload.pull_request.head.sha,
145+ state: apiCheckPassed ? 'success' : 'failure',
146+ target_url: `${context.payload.pull_request.html_url}/checks`,
147+ description: apiCheckPassed ?
148+ 'No breaking API changes detected' :
149+ 'Breaking API changes detected - review required',
150+ context: 'snapi'
151+ });
152+
153+ - name : Upload API snapshots and reports
154+ if : always()
155+ uses : actions/upload-artifact@v4
156+ with :
157+ name : api-debug-${{ github.run_id }}
158+ path : |
159+ .api-snapshots/
160+ api-changes-report.md
161+ retention-days : 7
0 commit comments