diff --git a/.changeset/readme-endpoint-table-in-openapi-gen.md b/.changeset/readme-endpoint-table-in-openapi-gen.md new file mode 100644 index 00000000..0d889819 --- /dev/null +++ b/.changeset/readme-endpoint-table-in-openapi-gen.md @@ -0,0 +1,5 @@ +--- +'lens': patch +--- + +`npm run openapi:gen` now also regenerates the README endpoint table from `openapi.yaml`, and CI runs `npm run readme:check`, which fails when the committed README differs from a fresh generation (#275). diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 2929c8a9..cf7ebfd7 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -24,6 +24,8 @@ jobs: run: git diff --exit-code -- clients/typescript - name: Typecheck the generated TypeScript client run: npm run client:typecheck + - name: Fail if the committed README tables are out of date with openapi.yaml + run: npm run readme:check - run: npx tsc --noEmit - run: npm run build - run: npm test --if-present diff --git a/README.md b/README.md index 24302db6..ca29daae 100644 --- a/README.md +++ b/README.md @@ -133,6 +133,10 @@ npx @modelcontextprotocol/inspector npm run mcp:server | GET/POST | `/graphql` | GraphQL query/mutation endpoint (GraphiQL IDE at `/graphiql`, docs: [`docs/graphql.md`](docs/graphql.md)) | +The table above is generated from [`openapi.yaml`](openapi.yaml) by `npm run openapi:gen`; +edit the spec rather than the table. CI runs `npm run readme:check` and fails when +the committed table differs from a fresh generation. + Every non-internal route above has a matching entry in [`openapi.yaml`](openapi.yaml); `tests/openapi.test.ts` boots the server and fails if a registered route is missing from the spec. Operator-only and non-HTTP routes (`/admin/keys*`, diff --git a/package.json b/package.json index b4899fe7..5e083709 100644 --- a/package.json +++ b/package.json @@ -10,8 +10,9 @@ "test": "vitest run", "smoke": "node scripts/deployed-smoke.mjs", "test:smoke": "node --test scripts/test-deployed-smoke.mjs", - "openapi:gen": "tsx scripts/generate-openapi.ts", + "openapi:gen": "tsx scripts/generate-openapi.ts && tsx scripts/generate-readme.ts", "readme:gen": "tsx scripts/generate-readme.ts", + "readme:check": "tsx scripts/generate-readme.ts --check", "spec:validate-live": "tsx scripts/validate-live-spec.ts", "client:gen": "openapi-typescript openapi.yaml -o clients/typescript/src/schema.d.ts", "client:typecheck": "tsc -p clients/typescript/tsconfig.json --noEmit", diff --git a/scripts/generate-readme.ts b/scripts/generate-readme.ts index e5a9fde5..f8960cf1 100644 --- a/scripts/generate-readme.ts +++ b/scripts/generate-readme.ts @@ -277,8 +277,30 @@ export function generateReadme(): void { writeFileSync(readmePath, renderReadme()) } -// CLI entry point +/** + * Compares the committed README.md with a fresh render and reports whether it + * has drifted. Line endings are normalised first, as in `tests/readme.test.ts`, + * so a CRLF checkout of an identical table is not reported as drift. + */ +export function readmeIsCurrent(content?: string): boolean { + const committed = (content ?? readFileSync(readmePath, 'utf8')).replace(/\r\n/g, '\n') + return renderReadme(committed) === committed +} + +// CLI entry point. `--check` (`npm run readme:check`, run in CI) writes +// nothing and exits non-zero when README.md differs from a fresh generation. if (process.argv[1] && fileURLToPath(import.meta.url) === process.argv[1]) { - generateReadme() - console.log('Successfully generated README.md venues and endpoints sections') + if (process.argv.includes('--check')) { + if (!readmeIsCurrent()) { + console.error( + 'README.md is out of date: its generated tables (endpoints from openapi.yaml, venues from src/ingest/venues) differ from a fresh generation.\n' + + 'Run `npm run openapi:gen` and commit the result.', + ) + process.exit(1) + } + console.log('README.md venue and endpoint tables are up to date') + } else { + generateReadme() + console.log('Successfully generated README.md venues and endpoints sections') + } } diff --git a/tests/readme.test.ts b/tests/readme.test.ts index 0b0c28e0..c74a54e9 100644 --- a/tests/readme.test.ts +++ b/tests/readme.test.ts @@ -9,6 +9,7 @@ import { renderVenuesTable, renderEndpointsTable, renderReadme, + readmeIsCurrent, VENUES_START_TAG, VENUES_END_TAG, ENDPOINTS_START_TAG, @@ -125,6 +126,15 @@ describe('README venue list and endpoint table generators', () => { expect(currentEndpoints).not.toContain('/fake/nonexistent/endpoint') }) + it('readme:check passes on the committed README and fails once the endpoint table is edited by hand', () => { + const committed = readFileSync(readmePath, 'utf8') + expect(readmeIsCurrent(committed)).toBe(true) + + const edited = committed.replace('| GET | `/tvl` |', '| GET | `/tvl-renamed` |') + expect(edited).not.toBe(committed) + expect(readmeIsCurrent(edited)).toBe(false) + }) + it('renders README deterministically in-place', () => { const initial = readFileSync(readmePath, 'utf8') const rendered = renderReadme(initial)