Skip to content

Commit 6f70aaf

Browse files
committed
ci: regenerate from the committed spec so the drift check is hermetic
1 parent b0fd43b commit 6f70aaf

2 files changed

Lines changed: 28 additions & 3 deletions

File tree

.github/workflows/ci.yml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,12 @@ jobs:
2222

2323
- run: bun install --frozen-lockfile
2424

25+
# Hermetic: regenerates from the committed spec, so this verifies that the committed
26+
# generated code still reproduces from it, with no network dependency. Refreshing the
27+
# spec itself is the release workflow's job.
2528
- name: Codegen drift check
29+
env:
30+
ROXYAPI_SPEC_FILE: specs/openapi.json
2631
run: |
2732
bun run generate
2833
git diff --exit-code -- specs/openapi.json src/client.gen.ts src/sdk.gen.ts src/types.gen.ts src/client src/core \

scripts/generate.ts

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
* Run with: bun run generate
55
*/
66
import { execSync } from 'node:child_process';
7-
import { mkdirSync, writeFileSync } from 'node:fs';
7+
import { mkdirSync, readFileSync, writeFileSync } from 'node:fs';
88

99
const SPEC_URL = 'https://roxyapi.com/api/v2/openapi.json';
1010
const SPEC_PATH = 'specs/openapi.json';
@@ -30,8 +30,28 @@ async function fetchSpec(url: string, attempts = 5): Promise<string> {
3030
}
3131
}
3232

33-
console.log('Fetching OpenAPI spec from', SPEC_URL);
34-
const spec = JSON.parse(await fetchSpec(SPEC_URL));
33+
/**
34+
* Load the spec from disk when `ROXYAPI_SPEC_FILE` is set, from the API otherwise.
35+
*
36+
* @remarks
37+
* Reading from a file keeps generation offline and byte-reproducible, which is what the codegen
38+
* drift check in CI relies on.
39+
*/
40+
async function loadSpec(): Promise<string> {
41+
const file = process.env.ROXYAPI_SPEC_FILE;
42+
if (file) {
43+
console.log(
44+
'Reading OpenAPI spec from',
45+
file,
46+
'(offline, ROXYAPI_SPEC_FILE)',
47+
);
48+
return readFileSync(file, 'utf8');
49+
}
50+
console.log('Fetching OpenAPI spec from', SPEC_URL);
51+
return fetchSpec(SPEC_URL);
52+
}
53+
54+
const spec = JSON.parse(await loadSpec());
3555

3656
// Patch server URL to absolute production URL so the generated default client
3757
// works out of the box without users specifying baseUrl

0 commit comments

Comments
 (0)