Skip to content

Commit fc9d33b

Browse files
committed
ci: add refresh-fixtures workflow for contract snapshot auto-update
Provides a manual workflow_dispatch trigger to fetch live responses from openboot.dev and open a PR updating fixtures/config-v1.json. Validates the updated fixture against the schema before opening the PR, so a breaking API change surfaces immediately rather than silently drifting. snapshot-v1.json is intentionally skipped — it is CLI-generated and must be updated manually when the snapshot format changes.
1 parent b029767 commit fc9d33b

File tree

1 file changed

+110
-0
lines changed

1 file changed

+110
-0
lines changed
Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
name: Refresh Fixtures
2+
3+
# Run on demand to update contract fixtures from the live API.
4+
# Use this after intentionally changing the API format so fixtures stay
5+
# in sync with the schema and real server responses.
6+
7+
on:
8+
workflow_dispatch:
9+
inputs:
10+
api_url:
11+
description: 'API base URL (defaults to https://openboot.dev)'
12+
required: false
13+
default: 'https://openboot.dev'
14+
15+
jobs:
16+
refresh:
17+
runs-on: ubuntu-latest
18+
permissions:
19+
contents: write
20+
pull-requests: write
21+
steps:
22+
- uses: actions/checkout@v4
23+
24+
- uses: actions/setup-python@v5
25+
with:
26+
python-version: '3.12'
27+
28+
- name: Install dependencies
29+
run: pip install jsonschema requests
30+
31+
- name: Fetch live responses and update fixtures
32+
env:
33+
API_URL: ${{ inputs.api_url }}
34+
run: |
35+
python3 - <<'EOF'
36+
import json, os, sys, urllib.request
37+
38+
api_url = os.environ.get('API_URL', 'https://openboot.dev').rstrip('/')
39+
40+
def fetch(url):
41+
with urllib.request.urlopen(url, timeout=15) as r:
42+
return json.loads(r.read())
43+
44+
updates = []
45+
46+
# Fetch a real config response for config-v1.json fixture
47+
# Use the openbootdotdev/default config which should always exist
48+
try:
49+
config = fetch(f'{api_url}/openbootdotdev/default/config')
50+
with open('fixtures/config-v1.json', 'w') as f:
51+
json.dump(config, f, indent=2)
52+
f.write('\n')
53+
updates.append('fixtures/config-v1.json')
54+
print(f' ✓ Updated fixtures/config-v1.json from {api_url}')
55+
except Exception as e:
56+
print(f' ✗ Could not fetch config: {e}', file=sys.stderr)
57+
sys.exit(1)
58+
59+
# snapshot-v1.json is a CLI-generated file, not a live API response.
60+
# It is intentionally not auto-refreshed here — update it manually
61+
# when the snapshot format changes (run: openboot snapshot --json).
62+
print(' - Skipping snapshot-v1.json (CLI-generated, update manually)')
63+
64+
print(f'\nUpdated: {updates}')
65+
EOF
66+
67+
- name: Validate updated fixtures still match schemas
68+
run: |
69+
python3 -c "
70+
import json, jsonschema, sys
71+
72+
checks = [
73+
('schemas/remote-config.json', 'fixtures/config-v1.json'),
74+
('schemas/snapshot.json', 'fixtures/snapshot-v1.json'),
75+
]
76+
77+
failed = 0
78+
for schema_path, fixture_path in checks:
79+
schema = json.load(open(schema_path))
80+
data = json.load(open(fixture_path))
81+
try:
82+
jsonschema.validate(data, schema)
83+
print(f' ✓ {fixture_path} matches {schema_path}')
84+
except jsonschema.ValidationError as e:
85+
print(f' ✗ {fixture_path}: {e.message}')
86+
failed += 1
87+
88+
if failed:
89+
print()
90+
print('Schema validation failed — the live response no longer matches the schema.')
91+
print('Update the schema in schemas/ before merging.')
92+
sys.exit(1 if failed else 0)
93+
"
94+
95+
- name: Open PR with updated fixtures
96+
uses: peter-evans/create-pull-request@v6
97+
with:
98+
commit-message: "chore: refresh contract fixtures from live API"
99+
title: "chore: refresh contract fixtures"
100+
body: |
101+
Auto-generated by the **Refresh Fixtures** workflow.
102+
103+
Fetched live responses from `${{ inputs.api_url }}` and updated
104+
`fixtures/config-v1.json`.
105+
106+
Review the diff to confirm the changes are intentional. Once
107+
merged, the contract CI will re-validate both consumer repos.
108+
branch: chore/refresh-fixtures
109+
delete-branch: true
110+
labels: "contract,automated"

0 commit comments

Comments
 (0)