Skip to content

Commit 40bda26

Browse files
committed
attempt SDK automation
1 parent 339de83 commit 40bda26

1 file changed

Lines changed: 226 additions & 0 deletions

File tree

Lines changed: 226 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,226 @@
1+
name: Generate SDKs
2+
3+
on:
4+
# push:
5+
# branches: [main]
6+
# paths:
7+
# - 'spec/**'
8+
workflow_dispatch:
9+
10+
concurrency:
11+
group: sdk-generation
12+
cancel-in-progress: true
13+
14+
jobs:
15+
generate:
16+
runs-on: ubuntu-latest
17+
strategy:
18+
fail-fast: false
19+
matrix:
20+
include:
21+
- language: node
22+
sdk_repo: workos/workos-node
23+
sdk_checkout_path: backend/workos-node
24+
25+
steps:
26+
- name: Generate GitHub App token
27+
id: app-token
28+
uses: actions/create-github-app-token@fee1f7d63c2ff003460e3d139729b119787bc349 # v2.2.2
29+
with:
30+
app-id: ${{ secrets.SDK_BOT_APP_ID }}
31+
private-key: ${{ secrets.SDK_BOT_PRIVATE_KEY }}
32+
33+
- name: Checkout openapi-spec
34+
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1
35+
with:
36+
path: openapi-spec
37+
38+
- name: Checkout oagen-emitters
39+
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1
40+
with:
41+
repository: workos/oagen-emitters
42+
token: ${{ steps.app-token.outputs.token }}
43+
path: oagen-emitters
44+
45+
- name: Checkout live SDK
46+
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1
47+
with:
48+
repository: ${{ matrix.sdk_repo }}
49+
token: ${{ steps.app-token.outputs.token }}
50+
path: ${{ matrix.sdk_checkout_path }}
51+
52+
- name: Setup Node
53+
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
54+
with:
55+
node-version: '24'
56+
cache: 'npm'
57+
cache-dependency-path: oagen-emitters/package-lock.json
58+
59+
- name: Install dependencies
60+
working-directory: oagen-emitters
61+
run: npm ci
62+
63+
- name: Extract baseline
64+
working-directory: oagen-emitters
65+
run: npm run sdk:extract:${{ matrix.language }}
66+
67+
- name: Generate
68+
working-directory: oagen-emitters
69+
run: npm run sdk:generate:${{ matrix.language }}
70+
71+
- name: Verify
72+
working-directory: oagen-emitters
73+
run: npm run sdk:verify:${{ matrix.language }}
74+
75+
- name: Diff report
76+
id: diff-report
77+
working-directory: oagen-emitters
78+
run: |
79+
# --report outputs JSON to stdout and exits with:
80+
# 0 = no changes, 1 = additive/modified, 2 = breaking
81+
REPORT=$(npx oagen diff \
82+
--old ./sdk/spec-snapshot.yaml \
83+
--new ../openapi-spec/spec/open-api-spec.yaml \
84+
--report 2>&1) || true
85+
86+
# Save full report to file for the classify step
87+
echo "$REPORT" > /tmp/diff-report.json
88+
89+
# Extract summary counts
90+
ADDED=$(echo "$REPORT" | jq -r '.summary.added')
91+
REMOVED=$(echo "$REPORT" | jq -r '.summary.removed')
92+
MODIFIED=$(echo "$REPORT" | jq -r '.summary.modified')
93+
BREAKING=$(echo "$REPORT" | jq -r '.summary.breaking')
94+
95+
{
96+
echo "added=$ADDED"
97+
echo "removed=$REMOVED"
98+
echo "modified=$MODIFIED"
99+
echo "breaking=$BREAKING"
100+
} >> "$GITHUB_OUTPUT"
101+
102+
- name: Classify changes with Claude
103+
id: classify
104+
env:
105+
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
106+
run: |
107+
DIFF_REPORT=$(cat /tmp/diff-report.json)
108+
109+
RESPONSE=$(curl -s https://api.anthropic.com/v1/messages \
110+
-H "content-type: application/json" \
111+
-H "x-api-key: $ANTHROPIC_API_KEY" \
112+
-H "anthropic-version: 2023-06-01" \
113+
-d "$(jq -n \
114+
--arg report "$DIFF_REPORT" \
115+
'{
116+
model: "claude-haiku-4-5-20251001",
117+
max_tokens: 256,
118+
messages: [{
119+
role: "user",
120+
content: ("Given this OpenAPI spec diff report, do two things:\n1. Classify the change type: respond with ONLY one of `feat!` (breaking — has removed models/endpoints or changed types), `feat` (additive — new endpoints, new models), or `fix` (modification only — field changes, description updates).\n2. Write a short summary (under 50 chars) of what changed, suitable for a commit message scope.\n\nRespond in exactly this format, two lines:\nPREFIX: <feat!|feat|fix>\nSUMMARY: <short description>\n\nDiff report:\n" + $report)
121+
}]
122+
}')")
123+
124+
# Parse Claude response
125+
CLAUDE_TEXT=$(echo "$RESPONSE" | jq -r '.content[0].text')
126+
PREFIX=$(echo "$CLAUDE_TEXT" | grep '^PREFIX:' | sed 's/^PREFIX: *//')
127+
SUMMARY=$(echo "$CLAUDE_TEXT" | grep '^SUMMARY:' | sed 's/^SUMMARY: *//')
128+
129+
# Fallback classification from diff summary counts
130+
if [ -z "$PREFIX" ]; then
131+
BREAKING='${{ steps.diff-report.outputs.breaking }}'
132+
ADDED='${{ steps.diff-report.outputs.added }}'
133+
if [ "$BREAKING" -gt 0 ] 2>/dev/null; then
134+
PREFIX="feat!"
135+
elif [ "$ADDED" -gt 0 ] 2>/dev/null; then
136+
PREFIX="feat"
137+
else
138+
PREFIX="fix"
139+
fi
140+
fi
141+
if [ -z "$SUMMARY" ]; then
142+
SUMMARY="update generated SDK from spec changes"
143+
fi
144+
145+
{
146+
echo "prefix=$PREFIX"
147+
echo "summary=$SUMMARY"
148+
} >> "$GITHUB_OUTPUT"
149+
150+
- name: Create PR on live SDK
151+
if: success()
152+
working-directory: ${{ matrix.sdk_checkout_path }}
153+
env:
154+
GH_TOKEN: ${{ steps.app-token.outputs.token }}
155+
run: |
156+
PREFIX="${{ steps.classify.outputs.prefix }}"
157+
SUMMARY="${{ steps.classify.outputs.summary }}"
158+
ADDED="${{ steps.diff-report.outputs.added }}"
159+
REMOVED="${{ steps.diff-report.outputs.removed }}"
160+
MODIFIED="${{ steps.diff-report.outputs.modified }}"
161+
BREAKING="${{ steps.diff-report.outputs.breaking }}"
162+
BRANCH="oagen/spec-update-${{ github.sha }}"
163+
164+
git config user.name "workos-sdk-automation[bot]"
165+
git config user.email "255426317+workos-sdk-automation[bot]@users.noreply.github.com"
166+
167+
git checkout -b "$BRANCH"
168+
git add -A
169+
170+
# Exit early if nothing to commit
171+
if git diff --cached --quiet; then
172+
echo "No changes to commit"
173+
exit 0
174+
fi
175+
176+
git commit -m "${PREFIX}(generated): ${SUMMARY}"
177+
git push origin "$BRANCH"
178+
179+
BODY="## Summary
180+
${SUMMARY}
181+
182+
## Spec Diff
183+
- Added: ${ADDED} | Removed: ${REMOVED} | Modified: ${MODIFIED} | Breaking: ${BREAKING}
184+
185+
Triggered by openapi-spec commit: ${{ github.sha }}
186+
187+
🤖 Generated by [oagen](https://github.com/workos/oagen)"
188+
189+
gh pr create \
190+
--title "${PREFIX}(generated): ${SUMMARY}" \
191+
--body "$BODY"
192+
193+
- name: Create issue on failure
194+
if: failure()
195+
env:
196+
GH_TOKEN: ${{ steps.app-token.outputs.token }}
197+
run: |
198+
BODY="## Failure
199+
- Language: ${{ matrix.language }}
200+
- Triggered by: ${{ github.sha }}
201+
202+
## Details
203+
- Exit 1: compat violations or smoke test findings
204+
- Exit 2: generated SDK failed to compile
205+
206+
## Next Steps
207+
- Check workflow run artifacts for smoke-diff-findings.json or smoke-compile-errors.json
208+
- The emitter may need a targeted fix for a new spec pattern
209+
210+
Run: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}"
211+
212+
gh issue create \
213+
--repo workos/openapi-spec \
214+
--title "SDK generation failed for ${{ matrix.language }}" \
215+
--body "$BODY"
216+
217+
- name: Upload diagnostics
218+
if: always()
219+
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
220+
with:
221+
name: oagen-diagnostics-${{ matrix.language }}
222+
path: |
223+
oagen-emitters/sdk/
224+
oagen-emitters/sdk-*-surface.json
225+
oagen-emitters/smoke-*.json
226+
retention-days: 14

0 commit comments

Comments
 (0)