Skip to content

Commit 77048cb

Browse files
feat: pin reference generation to release versions, add schemas reference page
- schemas/anolis-version.json: add anolis_protocol_version pin (v1.1.3) - generate-reference.mjs: - Runtime HTTP API page now reads the already-injected OpenAPI file from docs/public/schemas/anolis/http/ instead of cloning anolis at floating main. Attribution shows the pinned release tag rather than a live SHA. - ADPP Protocol page now clones anolis-protocol at the pinned tag (v1.1.3) instead of floating main. File links point to the tagged commit rather than /blob/main/. - Added generateSchemasReference(): generates docs/reference/schemas.md listing all 4 published schema URLs with descriptions, values, editor usage snippet (yaml.schemas), and Python usage snippet. - Reference index Inputs section now shows pin versions rather than specPath entries. Reference pages are now fully consistent with the schema version served at https://anolishq.github.io/schemas/anolis/...
1 parent c9b8095 commit 77048cb

2 files changed

Lines changed: 153 additions & 55 deletions

File tree

schemas/anolis-version.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
{
2-
"anolis_version": "0.1.4"
2+
"anolis_version": "0.1.4",
3+
"anolis_protocol_version": "1.1.3"
34
}

scripts/generate-reference.mjs

Lines changed: 151 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,15 @@ import { fileURLToPath } from "url";
77
const ROOT = path.resolve(fileURLToPath(import.meta.url), "../../");
88
const TMP = path.join(ROOT, ".tmp");
99
const OUT = path.join(ROOT, "docs/reference");
10+
const SCHEMAS_PUBLIC = path.join(ROOT, "docs/public/schemas/anolis");
11+
const PIN_FILE = path.join(ROOT, "schemas/anolis-version.json");
12+
13+
const pin = JSON.parse(await fs.readFile(PIN_FILE, "utf-8"));
14+
const ANOLIS_VERSION = pin.anolis_version;
15+
const ANOLIS_PROTOCOL_VERSION = pin.anolis_protocol_version;
16+
17+
if (!ANOLIS_VERSION) { console.error(`ERROR: anolis_version missing in ${PIN_FILE}`); process.exit(1); }
18+
if (!ANOLIS_PROTOCOL_VERSION) { console.error(`ERROR: anolis_protocol_version missing in ${PIN_FILE}`); process.exit(1); }
1019

1120
const repos = JSON.parse(
1221
await fs.readFile(path.join(ROOT, "data/repos.json"), "utf-8")
@@ -53,13 +62,16 @@ function relPosix(base, absPath) {
5362
return path.relative(base, absPath).split(path.sep).join("/");
5463
}
5564

56-
function ensureCheckout(repo) {
57-
const checkout = path.join(TMP, repo.name);
65+
function ensureCheckout(repo, tag) {
66+
const dirName = tag ? `${repo.name}-${tag}` : repo.name;
67+
const checkout = path.join(TMP, dirName);
5868
if (!existsSync(checkout)) {
59-
console.log(`Reference: cloning ${repo.repo}`);
60-
execFileSync("git", ["clone", "--depth=1", `https://github.com/${repo.repo}.git`, checkout], {
61-
stdio: "inherit",
62-
});
69+
const label = tag ? `${repo.repo}@v${tag}` : repo.repo;
70+
console.log(`Reference: cloning ${label}`);
71+
const cloneArgs = tag
72+
? ["clone", "--depth=1", "--branch", `v${tag}`, `https://github.com/${repo.repo}.git`, checkout]
73+
: ["clone", "--depth=1", `https://github.com/${repo.repo}.git`, checkout];
74+
execFileSync("git", cloneArgs, { stdio: "inherit" });
6375
}
6476
return checkout;
6577
}
@@ -160,36 +172,23 @@ function protoSymbols(text) {
160172
}
161173

162174
async function generateAnolisRuntimeReference(repo) {
163-
const repoRoot = ensureCheckout(repo);
164-
const specRoot = path.join(repoRoot, repo.specPath);
165-
166-
if (!(await exists(specRoot))) {
167-
fail(`Reference input missing for ${repo.name}: ${repo.specPath}`);
168-
}
169-
170-
const allFiles = await walkFiles(repoRoot);
171-
const openApiCandidates = allFiles
172-
.filter((f) => /\.(ya?ml|json)$/i.test(f))
173-
.filter((f) => /openapi/i.test(path.basename(f)))
174-
.filter((f) => /schemas[\\/]+http|docs[\\/]+http/i.test(f))
175-
.sort((a, b) => a.localeCompare(b));
175+
// Use the already-injected OpenAPI file rather than cloning anolis at floating
176+
// main. This keeps the reference page consistent with the pinned schema version
177+
// declared in schemas/anolis-version.json.
178+
const injectedOpenApi = path.join(SCHEMAS_PUBLIC, "http", "runtime-http.openapi.v0.yaml");
176179

177-
if (openApiCandidates.length === 0) {
180+
if (!(await exists(injectedOpenApi))) {
178181
fail(
179-
`No OpenAPI file found for ${repo.name}. Checked repo for filenames containing 'openapi' under HTTP-related paths.`
182+
`Injected OpenAPI file not found at ${injectedOpenApi}.\n` +
183+
`Run scripts/inject-schemas.sh first (or check schemas/anolis-version.json).`
180184
);
181185
}
182186

183-
const openApiFile = openApiCandidates[0];
184-
const openApiText = await fs.readFile(openApiFile, "utf-8");
187+
const openApiText = await fs.readFile(injectedOpenApi, "utf-8");
185188
const summary = parseOpenApi(openApiText);
186189

187-
const specFiles = (await walkFiles(specRoot))
188-
.filter((f) => /\.(md|ya?ml|json)$/i.test(f))
189-
.sort((a, b) => a.localeCompare(b));
190-
191-
const repoSha = shortSha(repoRoot);
192-
const openApiRel = relPosix(repoRoot, openApiFile);
190+
const openApiRel = "schemas/http/runtime-http.openapi.v0.yaml";
191+
const releaseTag = `v${ANOLIS_VERSION}`;
193192

194193
const endpointRows =
195194
summary.pathMethods.length === 0
@@ -209,29 +208,21 @@ async function generateAnolisRuntimeReference(repo) {
209208
...summary.schemas.map((s) => `| \`${s}\` |`),
210209
].join("\n");
211210

212-
const sourcesRows =
213-
specFiles.length === 0
214-
? "_No files found under configured spec path._"
215-
: [
216-
"| Source File |",
217-
"|---|",
218-
...specFiles.map((f) => {
219-
const rel = relPosix(repoRoot, f);
220-
return `| [\`${rel}\`](https://github.com/${repo.repo}/blob/main/${rel}) |`;
221-
}),
222-
].join("\n");
211+
const sourcesRows = `[\`${openApiRel}\`](https://github.com/${repo.repo}/blob/${releaseTag}/${openApiRel})`;
223212

224213
const body = [
225214
"# Runtime HTTP API Reference",
226215
"",
227-
`Generated from [${repo.repo}](https://github.com/${repo.repo}) @ \`${repoSha}\`. ` +
228-
`Configured spec path: \`${repo.specPath}\`.`,
216+
`Schema version: [${releaseTag}](https://github.com/${repo.repo}/releases/tag/${releaseTag}) — ` +
217+
`source [\`${openApiRel}\`](https://github.com/${repo.repo}/blob/${releaseTag}/${openApiRel}).`,
218+
"",
219+
`> Pin: \`schemas/anolis-version.json\` → \`anolis_version: ${ANOLIS_VERSION}\``,
229220
"",
230221
"## OpenAPI Summary",
231222
"",
232223
`- Title: **${summary.title}**`,
233224
`- Version: **${summary.version}**`,
234-
`- Source: [\`${openApiRel}\`](https://github.com/${repo.repo}/blob/main/${openApiRel})`,
225+
`- Source: ${sourcesRows}`,
235226
"",
236227
"## Endpoints",
237228
"",
@@ -241,10 +232,6 @@ async function generateAnolisRuntimeReference(repo) {
241232
"",
242233
schemaRows,
243234
"",
244-
"## Reference Sources",
245-
"",
246-
sourcesRows,
247-
"",
248235
].join("\n");
249236

250237
return {
@@ -256,8 +243,11 @@ async function generateAnolisRuntimeReference(repo) {
256243
}
257244

258245
async function generateAdppReference(repo) {
259-
const repoRoot = ensureCheckout(repo);
246+
// Clone at the pinned tag rather than floating main so the reference stays
247+
// consistent with schemas/anolis-version.json.
248+
const repoRoot = ensureCheckout(repo, ANOLIS_PROTOCOL_VERSION);
260249
const specRoot = path.join(repoRoot, repo.specPath);
250+
const releaseTag = `v${ANOLIS_PROTOCOL_VERSION}`;
261251

262252
if (!(await exists(specRoot))) {
263253
fail(`Reference input missing for ${repo.name}: ${repo.specPath}`);
@@ -297,14 +287,12 @@ async function generateAdppReference(repo) {
297287
});
298288
}
299289

300-
const repoSha = shortSha(repoRoot);
301-
302290
const fileTable = [
303291
"| Proto File | Package | Messages | Enums | Services | RPCs |",
304292
"|---|---|---:|---:|---:|---:|",
305293
...fileSummaries.map(
306294
(f) =>
307-
`| [\`${f.rel}\`](https://github.com/${repo.repo}/blob/main/${f.rel}) | \`${f.packageName}\` | ` +
295+
`| [\`${f.rel}\`](https://github.com/${repo.repo}/blob/${releaseTag}/${f.rel}) | \`${f.packageName}\` | ` +
308296
`${f.messages.length} | ${f.enums.length} | ${f.services.length} | ${f.rpcs.length} |`
309297
),
310298
].join("\n");
@@ -315,8 +303,10 @@ async function generateAdppReference(repo) {
315303
const body = [
316304
"# ADPP Protocol Reference",
317305
"",
318-
`Generated from [${repo.repo}](https://github.com/${repo.repo}) @ \`${repoSha}\`. ` +
319-
`Configured spec path: \`${repo.specPath}\`.`,
306+
`Schema version: [${releaseTag}](https://github.com/${repo.repo}/releases/tag/${releaseTag}) — ` +
307+
`proto path \`${repo.specPath}\`.`,
308+
"",
309+
`> Pin: \`schemas/anolis-version.json\` → \`anolis_protocol_version: ${ANOLIS_PROTOCOL_VERSION}\``,
320310
"",
321311
"## Overview",
322312
"",
@@ -389,6 +379,108 @@ async function generateGenericSpecReference(repo) {
389379
};
390380
}
391381

382+
async function generateSchemasReference() {
383+
// Read each injected schema file to extract $id, title, and description.
384+
const schemas = [
385+
{
386+
file: path.join(SCHEMAS_PUBLIC, "runtime", "runtime-config.schema.json"),
387+
url: "/schemas/anolis/runtime/runtime-config.schema.json",
388+
label: "Runtime Config",
389+
fallbackDesc: "JSON Schema for anolis-runtime YAML config files.",
390+
},
391+
{
392+
file: path.join(SCHEMAS_PUBLIC, "machine", "machine-profile.schema.json"),
393+
url: "/schemas/anolis/machine/machine-profile.schema.json",
394+
label: "Machine Profile",
395+
fallbackDesc: "JSON Schema for machine package manifests.",
396+
},
397+
{
398+
file: path.join(SCHEMAS_PUBLIC, "telemetry", "telemetry-timeseries.schema.v1.json"),
399+
url: "/schemas/anolis/telemetry/telemetry-timeseries.schema.v1.json",
400+
label: "Telemetry Timeseries",
401+
fallbackDesc: "JSON Schema for telemetry export time-series records.",
402+
},
403+
];
404+
405+
const rows = [];
406+
for (const s of schemas) {
407+
let desc = s.fallbackDesc;
408+
let id = s.url;
409+
if (await exists(s.file)) {
410+
try {
411+
const raw = JSON.parse(await fs.readFile(s.file, "utf-8"));
412+
if (raw.description) desc = raw.description;
413+
if (raw.$id) id = raw.$id;
414+
} catch {
415+
// keep fallback
416+
}
417+
} else {
418+
console.warn(` Warning: injected schema not found: ${s.file}`);
419+
}
420+
rows.push({ label: s.label, id, desc });
421+
}
422+
423+
const releaseTag = `v${ANOLIS_VERSION}`;
424+
const baseUrl = "https://anolishq.github.io";
425+
426+
const tableRows = rows.map(
427+
(r) => `| [${r.label}](${r.id}) | \`${r.id}\` | ${r.desc} |`
428+
);
429+
430+
const body = [
431+
"# Schema Reference",
432+
"",
433+
`Canonical JSON Schemas published from [anolishq/anolis](https://github.com/anolishq/anolis) ` +
434+
`[${releaseTag}](https://github.com/anolishq/anolis/releases/tag/${releaseTag}).`,
435+
"",
436+
`> Pin: \`schemas/anolis-version.json\` → \`anolis_version: ${ANOLIS_VERSION}\``,
437+
"",
438+
"These URLs are stable and resolve to the current pinned schema version:",
439+
"",
440+
"| Schema | \`$id\` / URL | Description |",
441+
"|---|---|---|",
442+
...tableRows,
443+
"",
444+
"## OpenAPI Contract",
445+
"",
446+
`| Contract | URL | Description |`,
447+
`|---|---|---|`,
448+
`| Runtime HTTP API | [${baseUrl}/schemas/anolis/http/runtime-http.openapi.v0.yaml](${baseUrl}/schemas/anolis/http/runtime-http.openapi.v0.yaml) | OpenAPI 3.x contract for the runtime \`/v0\` HTTP surface. |`,
449+
"",
450+
"## Usage",
451+
"",
452+
"### VS Code / editor tooling",
453+
"",
454+
"Add a `yaml.schemas` entry to `.vscode/settings.json`:",
455+
"",
456+
"```json",
457+
"{",
458+
` "yaml.schemas": {`,
459+
` "${baseUrl}/schemas/anolis/runtime/runtime-config.schema.json": "config/anolis-runtime*.yaml",`,
460+
` "${baseUrl}/schemas/anolis/machine/machine-profile.schema.json": "config/**/machine-profile.yaml"`,
461+
" }",
462+
"}",
463+
"```",
464+
"",
465+
"### Python / jsonschema",
466+
"",
467+
"```python",
468+
"import urllib.request, json, jsonschema",
469+
`url = \"${baseUrl}/schemas/anolis/runtime/runtime-config.schema.json\"`,
470+
"schema = json.loads(urllib.request.urlopen(url).read())",
471+
"jsonschema.validate(instance=config, schema=schema)",
472+
"```",
473+
"",
474+
].join("\n");
475+
476+
return {
477+
fileName: "schemas.md",
478+
title: "Schemas",
479+
description: `JSON Schema and OpenAPI contracts published from anolishq/anolis ${releaseTag}.`,
480+
body,
481+
};
482+
}
483+
392484
if (specRepos.length === 0) {
393485
fail("No repositories with specPath configured in data/repos.json");
394486
}
@@ -398,6 +490,10 @@ await fs.mkdir(OUT, { recursive: true });
398490

399491
const pages = [];
400492

493+
// Schema reference is not repo-specific — built from injected files + pin.
494+
console.log("Reference: generating schemas page");
495+
pages.push(await generateSchemasReference());
496+
401497
for (const repo of specRepos) {
402498
console.log(`Reference: generating for ${repo.name}`);
403499
if (repo.name === "anolis") {
@@ -427,7 +523,8 @@ const indexBody = [
427523
"",
428524
"## Inputs",
429525
"",
430-
...specRepos.map((r) => `- \`${r.name}\`: repo \`${r.repo}\`, specPath \`${r.specPath}\``),
526+
`- \`anolis\`: pinned to \`v${ANOLIS_VERSION}\` via \`schemas/anolis-version.json\``,
527+
`- \`anolis-protocol\`: pinned to \`v${ANOLIS_PROTOCOL_VERSION}\` via \`schemas/anolis-version.json\``,
431528
"",
432529
].join("\n");
433530

0 commit comments

Comments
 (0)