Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
62 changes: 62 additions & 0 deletions .github/scripts/generate-sitemap.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
#!/usr/bin/env node
/**
* Generates a custom sitemap.xml that OVERRIDES Mintlify's auto-generated one.
*
* Why: Mintlify serves and canonicalizes docs URLs without a trailing slash,
* but the site actually serves — and Google indexes — the trailing-slash form
* (and every page now declares a trailing-slash canonical). This emits a sitemap
* whose <loc>s match that trailing-slash form, so canonical + sitemap + served
* URL all agree.
*
* Source of truth: the docs.json navigation (exactly the pages Mintlify indexes).
* Output: sitemap.xml at the repo root, which Mintlify serves in place of its
* auto-generated file (https://www.mintlify.com/docs/optimize/seo).
*
* NOTE: this script lives under .github/ on purpose — Mintlify does not build
* the .github tree, so a stray .mjs here can never break the docs build (a root
* scripts/*.mjs previously did).
*
* Run: npm run generate-sitemap
* CI (static-docs-checks) regenerates and fails if the committed file drifts.
*/
import { readFileSync, writeFileSync } from 'node:fs'
import { fileURLToPath } from 'node:url'
import { dirname, join } from 'node:path'

const ROOT = join(dirname(fileURLToPath(import.meta.url)), '..', '..')
const BASE = 'https://www.checklyhq.com/docs/'

const docs = JSON.parse(readFileSync(join(ROOT, 'docs.json'), 'utf8'))

// Collect every page slug referenced anywhere in the navigation tree.
const slugs = new Set()
function walk(node) {
if (Array.isArray(node)) return node.forEach(walk)
if (node && typeof node === 'object') {
for (const [key, value] of Object.entries(node)) {
if (key === 'pages' && Array.isArray(value)) {
for (const item of value) {
if (typeof item === 'string') slugs.add(item.replace(/^\/+|\/+$/g, ''))
else walk(item)
}
} else {
walk(value)
}
}
}
}
walk(docs.navigation)

const urls = [...slugs]
.sort()
.map((slug) => ` <url>\n <loc>${BASE}${slug}/</loc>\n </url>`)
.join('\n')

const xml = `<?xml version="1.0" encoding="UTF-8"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
${urls}
</urlset>
`

writeFileSync(join(ROOT, 'sitemap.xml'), xml)
console.log(`Wrote sitemap.xml with ${slugs.size} trailing-slash URLs`)
14 changes: 14 additions & 0 deletions .github/workflows/static-docs-checks.yml
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,20 @@ jobs:
if: steps.detect.outputs.docs_changed == 'true'
run: python3 .github/scripts/check_redirect_destinations.py

# sitemap.xml (custom, trailing-slash — overrides Mintlify's auto-gen) is
# generated from docs.json navigation. If a nav page is added/renamed/
# removed without regenerating, the committed sitemap drifts. Regenerate
# and fail if it differs.
- name: Sitemap up to date
if: steps.detect.outputs.docs_changed == 'true'
run: |
node .github/scripts/generate-sitemap.mjs
if ! git diff --quiet -- sitemap.xml; then
echo "::error::sitemap.xml is out of date. Run 'npm run generate-sitemap' and commit the result."
git --no-pager diff -- sitemap.xml | head -60
exit 1
fi

- name: Broken-links scan
if: steps.detect.outputs.docs_changed == 'true'
run: |
Expand Down
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@
"deploy": "checkly deploy",
"test": "checkly test",
"trigger": "checkly trigger",
"login": "checkly login"
"login": "checkly login",
"generate-sitemap": "node .github/scripts/generate-sitemap.mjs"
},
"devDependencies": {
"@playwright/test": "^1.40.0",
Expand Down
Loading
Loading