From cf7ab09898c59003432a99da02a10506484e94f6 Mon Sep 17 00:00:00 2001 From: Spiros Martzoukos Date: Tue, 11 Aug 2026 10:47:43 +0300 Subject: [PATCH 1/2] fix(ci): make generated endpoint pages pass the static docs gates MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Auto-generated endpoint pages have failed CI since the July SEO audit added check_frontmatter.py: the generator writes only an `openapi:` key, while every built page must declare a self-referential trailing-slash canonical. The stub now derives that canonical from the same page path it hands docs.json, so the two cannot drift. The sync workflow also left sitemap.xml stale — it is generated from docs.json navigation, which the generator mutates — so the "Sitemap up to date" gate failed on the same PRs. Regenerate it after page generation and stage it. Verified by running the generator against the current live spec: it reproduces the four /v2 pages from PR #476 with correct canonicals, and the frontmatter, redirect-destination, and sitemap checks all pass. Co-Authored-By: Claude Opus 5 --- .github/scripts/detect-new-endpoints.mjs | 8 +++++++- .github/workflows/update-api-spec.yml | 8 +++++++- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/.github/scripts/detect-new-endpoints.mjs b/.github/scripts/detect-new-endpoints.mjs index f4901627..fa40b68e 100644 --- a/.github/scripts/detect-new-endpoints.mjs +++ b/.github/scripts/detect-new-endpoints.mjs @@ -21,6 +21,8 @@ const ROOT = process.cwd(); const SPEC_PATH = join(ROOT, 'api-reference', 'openapi.json'); const DOCS_JSON_PATH = join(ROOT, 'docs.json'); const IGNORE_PATH = join(ROOT, '.api-doc-ignore'); +// Must match BASE in .github/scripts/check_frontmatter.py. +const SITE_BASE = 'https://www.checklyhq.com/docs/'; // --------------------------------------------------------------------------- // Tag → directory + docs.json group mapping. @@ -305,7 +307,11 @@ function main() { } // Minimal frontmatter — Mintlify derives the title from the spec summary. - const mdxContent = `---\nopenapi: ${ep.method.toLowerCase()} ${ep.path}\n---\n`; + // The canonical is self-referential and derived from the same page path + // docs.json gets, so it can never drift from what check_frontmatter.py + // expects (that check fails the PR when the key is missing or wrong). + const canonical = `${SITE_BASE}${docsJsonPagePath}/`; + const mdxContent = `---\nopenapi: ${ep.method.toLowerCase()} ${ep.path}\ncanonical: '${canonical}'\n---\n`; if (!DRY_RUN) { writeFileSync(join(ROOT, mdxRelPath), mdxContent); diff --git a/.github/workflows/update-api-spec.yml b/.github/workflows/update-api-spec.yml index 1da32f8f..eaf20ce7 100644 --- a/.github/workflows/update-api-spec.yml +++ b/.github/workflows/update-api-spec.yml @@ -54,6 +54,12 @@ jobs: - name: Generate pages for any new endpoints run: node .github/scripts/detect-new-endpoints.mjs + # New pages land in docs.json navigation, which is what the sitemap is + # generated from. Without this the static-docs-checks "Sitemap up to date" + # gate fails on every PR that adds an endpoint page. + - name: Regenerate sitemap + run: node .github/scripts/generate-sitemap.mjs + - name: Create or update API spec PR if anything changed env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} @@ -61,7 +67,7 @@ jobs: set -euo pipefail # Stage everything that might have changed - git add api-reference docs.json + git add api-reference docs.json sitemap.xml if git diff --cached --quiet; then echo "✅ No changes — nothing to do" From d6ebd72e77ff317220a6ee124b678c46f40d85f7 Mon Sep 17 00:00:00 2001 From: Spiros Martzoukos Date: Tue, 11 Aug 2026 10:55:35 +0300 Subject: [PATCH 2/2] fix(docs): disambiguate the v2 endpoint pages in the sidebar MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Mintlify falls back to the OpenAPI summary when a page declares no title, so each /v2 page rendered under the same sidebar label as its /v1 counterpart — two "Retrieve a check" entries, with the v2 one stranded at the bottom of the group behind the SSL monitor pages. Give the version-suffixed pages an explicit "… (v2)" title and move each one directly after the /v1 page it supersedes. Check-group pages already carry a "(V2)" marker in their spec summary and keep it. The generator does both from now on, so the next versioned endpoint lands correctly without a follow-up. Co-Authored-By: Claude Opus 5 --- .github/scripts/detect-new-endpoints.mjs | 43 +++++++++++++++---- ...t-the-completion-of-a-check-session-v2.mdx | 1 + .../retrieve-a-check-session-v2.mdx | 1 + .../trigger-a-new-check-session-v2.mdx | 1 + api-reference/checks/list-all-checks-v2.mdx | 1 + api-reference/checks/retrieve-a-check-v2.mdx | 1 + docs.json | 14 +++--- 7 files changed, 47 insertions(+), 15 deletions(-) diff --git a/.github/scripts/detect-new-endpoints.mjs b/.github/scripts/detect-new-endpoints.mjs index fa40b68e..a0ca7988 100644 --- a/.github/scripts/detect-new-endpoints.mjs +++ b/.github/scripts/detect-new-endpoints.mjs @@ -165,6 +165,19 @@ function getVersionSuffix(path) { return match ? `-${match[1]}` : ''; } +// A page whose filename needed a version suffix sits next to another version of +// the same operation, and both would render the same sidebar label — Mintlify +// falls back to the spec summary when a page declares no title. Give the +// suffixed page an explicit versioned title, unless the summary already names +// the version itself (several /v2 operations end in "(V2)"). +function versionedTitle(ep) { + const suffix = getVersionSuffix(ep.path); + if (!suffix || ep.filename !== `${generateFilename(ep.summary)}${suffix}`) return null; + const version = suffix.slice(1); + if (new RegExp(`\\b${version}\\b`, 'i').test(ep.summary)) return null; + return `${ep.summary} (${version})`; +} + function getUniqueFilename(ep) { const baseFilename = generateFilename(ep.summary); const candidates = [ @@ -193,6 +206,21 @@ function getUniqueFilename(ep) { // docs.json updater // --------------------------------------------------------------------------- +// Append, except for a version-suffixed page whose unsuffixed sibling is +// already in the list — that one goes directly after the sibling so the pair +// reads together in the sidebar instead of stranding the new version at the +// bottom of the group. +function insertPage(pages, pagePath) { + if (pages.includes(pagePath)) return; + const sibling = pagePath.replace(/-v\d+$/, ''); + const siblingIdx = sibling === pagePath ? -1 : pages.indexOf(sibling); + if (siblingIdx === -1) { + pages.push(pagePath); + return; + } + pages.splice(siblingIdx + 1, 0, pagePath); +} + function addToDocsJson(docsJson, pagePath, groupName, subgroupName) { // Structure: docsJson.navigation.tabs[] → { tab: "API Reference", pages: [...] } const tabs = docsJson.navigation?.tabs ?? []; @@ -233,13 +261,9 @@ function addToDocsJson(docsJson, pagePath, groupName, subgroupName) { targetGroup.pages.push(subgroup); console.log(` + Created new subgroup "${subgroupName}" in docs.json`); } - if (!subgroup.pages.includes(pagePath)) { - subgroup.pages.push(pagePath); - } + insertPage(subgroup.pages, pagePath); } else { - if (!targetGroup.pages.includes(pagePath)) { - targetGroup.pages.push(pagePath); - } + insertPage(targetGroup.pages, pagePath); } return true; @@ -306,12 +330,15 @@ function main() { console.log(` + Created directory: api-reference/${ep.dir}/`); } - // Minimal frontmatter — Mintlify derives the title from the spec summary. + // Minimal frontmatter — Mintlify derives the title from the spec summary, + // so only version-suffixed pages need an explicit one. // The canonical is self-referential and derived from the same page path // docs.json gets, so it can never drift from what check_frontmatter.py // expects (that check fails the PR when the key is missing or wrong). const canonical = `${SITE_BASE}${docsJsonPagePath}/`; - const mdxContent = `---\nopenapi: ${ep.method.toLowerCase()} ${ep.path}\ncanonical: '${canonical}'\n---\n`; + const title = versionedTitle(ep); + const titleLine = title ? `title: '${title.replace(/'/g, "''")}'\n` : ''; + const mdxContent = `---\nopenapi: ${ep.method.toLowerCase()} ${ep.path}\n${titleLine}canonical: '${canonical}'\n---\n`; if (!DRY_RUN) { writeFileSync(join(ROOT, mdxRelPath), mdxContent); diff --git a/api-reference/check-sessions/await-the-completion-of-a-check-session-v2.mdx b/api-reference/check-sessions/await-the-completion-of-a-check-session-v2.mdx index c8bf5a2e..88886fb2 100644 --- a/api-reference/check-sessions/await-the-completion-of-a-check-session-v2.mdx +++ b/api-reference/check-sessions/await-the-completion-of-a-check-session-v2.mdx @@ -1,4 +1,5 @@ --- openapi: get /v2/check-sessions/{checkSessionId}/completion +title: 'Await the completion of a check session (v2)' canonical: 'https://www.checklyhq.com/docs/api-reference/check-sessions/await-the-completion-of-a-check-session-v2/' --- diff --git a/api-reference/check-sessions/retrieve-a-check-session-v2.mdx b/api-reference/check-sessions/retrieve-a-check-session-v2.mdx index 88681871..a49dc380 100644 --- a/api-reference/check-sessions/retrieve-a-check-session-v2.mdx +++ b/api-reference/check-sessions/retrieve-a-check-session-v2.mdx @@ -1,4 +1,5 @@ --- openapi: get /v2/check-sessions/{checkSessionId} +title: 'Retrieve a check session (v2)' canonical: 'https://www.checklyhq.com/docs/api-reference/check-sessions/retrieve-a-check-session-v2/' --- diff --git a/api-reference/check-sessions/trigger-a-new-check-session-v2.mdx b/api-reference/check-sessions/trigger-a-new-check-session-v2.mdx index 62a4937d..88a86bd0 100644 --- a/api-reference/check-sessions/trigger-a-new-check-session-v2.mdx +++ b/api-reference/check-sessions/trigger-a-new-check-session-v2.mdx @@ -1,4 +1,5 @@ --- openapi: post /v2/check-sessions/trigger +title: 'Trigger a new check session (v2)' canonical: 'https://www.checklyhq.com/docs/api-reference/check-sessions/trigger-a-new-check-session-v2/' --- diff --git a/api-reference/checks/list-all-checks-v2.mdx b/api-reference/checks/list-all-checks-v2.mdx index 889e0b87..3058473a 100644 --- a/api-reference/checks/list-all-checks-v2.mdx +++ b/api-reference/checks/list-all-checks-v2.mdx @@ -1,4 +1,5 @@ --- openapi: get /v2/checks +title: 'List all checks (v2)' canonical: 'https://www.checklyhq.com/docs/api-reference/checks/list-all-checks-v2/' --- diff --git a/api-reference/checks/retrieve-a-check-v2.mdx b/api-reference/checks/retrieve-a-check-v2.mdx index 386f2aff..14d9f670 100644 --- a/api-reference/checks/retrieve-a-check-v2.mdx +++ b/api-reference/checks/retrieve-a-check-v2.mdx @@ -1,4 +1,5 @@ --- openapi: get /v2/checks/{id} +title: 'Retrieve a check (v2)' canonical: 'https://www.checklyhq.com/docs/api-reference/checks/retrieve-a-check-v2/' --- diff --git a/docs.json b/docs.json index 9ef5cbce..d3ce24c6 100644 --- a/docs.json +++ b/docs.json @@ -695,7 +695,9 @@ "group": "Checks and Monitors", "pages": [ "api-reference/checks/list-all-checks", + "api-reference/checks/list-all-checks-v2", "api-reference/checks/retrieve-a-check", + "api-reference/checks/retrieve-a-check-v2", "api-reference/checks/delete-a-check", "api-reference/checks/create-a-check", "api-reference/checks/update-a-check", @@ -759,9 +761,7 @@ ] }, "api-reference/monitors/create-an-ssl-monitor", - "api-reference/monitors/update-an-ssl-monitor", - "api-reference/checks/list-all-checks-v2", - "api-reference/checks/retrieve-a-check-v2" + "api-reference/monitors/update-an-ssl-monitor" ] }, { @@ -775,16 +775,16 @@ "group": "Check Groups", "pages": [ "api-reference/check-groups/list-all-check-groups", - "api-reference/check-groups/create-a-check-group-v2", - "api-reference/check-groups/update-a-check-group-v2", "api-reference/check-groups/retrieve-a-check-group", "api-reference/check-groups/delete-a-check-group", "api-reference/check-groups/retrieve-one-check-in-a-specific-group-with-group-settings-applied", + "api-reference/check-groups/retrieve-one-check-in-a-specific-group-with-group-settings-applied-v2", "api-reference/check-groups/retrieve-all-checks-in-a-specific-group-with-group-settings-applied", + "api-reference/check-groups/retrieve-all-checks-in-a-specific-group-with-group-settings-applied-v2", "api-reference/check-groups/create-a-check-group", + "api-reference/check-groups/create-a-check-group-v2", "api-reference/check-groups/update-a-check-group", - "api-reference/check-groups/retrieve-one-check-in-a-specific-group-with-group-settings-applied-v2", - "api-reference/check-groups/retrieve-all-checks-in-a-specific-group-with-group-settings-applied-v2" + "api-reference/check-groups/update-a-check-group-v2" ] }, {