chore: push public docs to the soulcraft.com ingest door on release
scripts/push-docs.js collects docs/**/*.md with public:true frontmatter and POSTs them (batches of 10, idempotent per slug) to the docs ingest door after npm publish — release.sh step 12. Absent secret = loud skip (publish already happened; the serving side can interim-sync); a failed push exits non-zero so the docs site never silently trails npm. The combined /docs landing index is deliberately NOT pushed per-repo — it spans both engine corpora and is authored on the serving side.
This commit is contained in:
parent
a544225872
commit
42037d0cd0
2 changed files with 128 additions and 0 deletions
116
scripts/push-docs.js
Normal file
116
scripts/push-docs.js
Normal file
|
|
@ -0,0 +1,116 @@
|
|||
#!/usr/bin/env node
|
||||
/**
|
||||
* @module scripts/push-docs
|
||||
* @description Push this repo's PUBLIC docs to the soulcraft.com docs ingest
|
||||
* door after an npm publish (VENUE-DOCS-RELEASE-PUSH — retires the old
|
||||
* build-time docs sync).
|
||||
*
|
||||
* Contract (mirrors the reference implementation on the serving side):
|
||||
* POST {base}/api/docs/ingest
|
||||
* headers: x-service-secret: $DOCS_INGEST_SECRET, Content-Type: application/json
|
||||
* body: { docs: [{ slug, title, markdown, nav: { order, section } }] }
|
||||
* batches of 10, idempotent per slug.
|
||||
*
|
||||
* A doc is public iff its frontmatter has `public: true` AND a `slug`. The
|
||||
* frontmatter is stripped; `category` → nav.section, `order` → nav.order.
|
||||
*
|
||||
* Deliberately NOT pushed: the combined /docs landing index. It spans BOTH
|
||||
* engine corpora (this repo's and the native accelerator's), so a per-repo
|
||||
* push would clobber the union — the index is authored on the serving side.
|
||||
*
|
||||
* Env: DOCS_INGEST_SECRET (required), DOCS_INGEST_BASE (default
|
||||
* https://soulcraft.com). Exits 0 with a LOUD warning when the secret is
|
||||
* absent (the npm publish has already happened; the serving side runs its
|
||||
* interim sync on request) and exits 1 when a push actually fails — the docs
|
||||
* site would silently trail npm otherwise, and that must be visible.
|
||||
*/
|
||||
import * as fs from 'node:fs'
|
||||
import * as path from 'node:path'
|
||||
|
||||
const BASE = (process.env.DOCS_INGEST_BASE || 'https://soulcraft.com').replace(/\/+$/, '')
|
||||
const SECRET = process.env.DOCS_INGEST_SECRET
|
||||
const DOCS_DIR = path.join(path.dirname(new URL(import.meta.url).pathname), '..', 'docs')
|
||||
const BATCH = 10
|
||||
|
||||
if (!SECRET) {
|
||||
console.warn(
|
||||
'⚠️ DOCS PUSH SKIPPED: DOCS_INGEST_SECRET is not set.\n' +
|
||||
' soulcraft.com/docs now TRAILS this npm release until docs are pushed.\n' +
|
||||
' Either export DOCS_INGEST_SECRET and re-run `node scripts/push-docs.js`,\n' +
|
||||
' or ping venue on VENUE-DOCS-RELEASE-PUSH for the interim sync.'
|
||||
)
|
||||
process.exit(0)
|
||||
}
|
||||
|
||||
/** Minimal frontmatter split — returns [meta, body] or [null, raw]. */
|
||||
function parseFrontmatter(raw) {
|
||||
const m = raw.match(/^---\n([\s\S]*?)\n---\n([\s\S]*)$/)
|
||||
if (!m) return [null, raw]
|
||||
const meta = {}
|
||||
for (const line of m[1].split('\n')) {
|
||||
const kv = line.match(/^(\w[\w-]*):\s*(.*)$/)
|
||||
if (kv) meta[kv[1]] = kv[2].trim().replace(/^["']|["']$/g, '')
|
||||
}
|
||||
return [meta, m[2]]
|
||||
}
|
||||
|
||||
const docs = []
|
||||
;(function walk(dir) {
|
||||
for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
|
||||
const full = path.join(dir, entry.name)
|
||||
if (entry.isDirectory()) walk(full)
|
||||
else if (entry.name.endsWith('.md')) {
|
||||
const [meta, body] = parseFrontmatter(fs.readFileSync(full, 'utf-8'))
|
||||
if (!meta || meta.public !== 'true' || !meta.slug) continue
|
||||
docs.push({
|
||||
slug: meta.slug,
|
||||
title: meta.title || meta.slug,
|
||||
markdown: body.trim(),
|
||||
nav: {
|
||||
order: Number.parseInt(meta.order || '99', 10) || 99,
|
||||
section: meta.category || 'guides'
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
})(DOCS_DIR)
|
||||
|
||||
if (docs.length === 0) {
|
||||
console.error('❌ DOCS PUSH FAILED: zero public docs collected — refusing to push an empty corpus.')
|
||||
process.exit(1)
|
||||
}
|
||||
docs.sort((a, b) => a.slug.localeCompare(b.slug))
|
||||
console.log(`Pushing ${docs.length} public docs to ${BASE}/api/docs/ingest …`)
|
||||
|
||||
let failed = false
|
||||
for (let i = 0; i < docs.length; i += BATCH) {
|
||||
const batch = docs.slice(i, i + BATCH)
|
||||
try {
|
||||
const res = await fetch(`${BASE}/api/docs/ingest`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'x-service-secret': SECRET,
|
||||
'Content-Type': 'application/json',
|
||||
'User-Agent': 'brainy-docs-push/1.0'
|
||||
},
|
||||
body: JSON.stringify({ docs: batch }),
|
||||
signal: AbortSignal.timeout(120_000)
|
||||
})
|
||||
if (!res.ok) {
|
||||
throw new Error(`HTTP ${res.status}: ${(await res.text()).slice(0, 300)}`)
|
||||
}
|
||||
console.log(` batch ${i / BATCH + 1}: ${batch.map((d) => d.slug).join(', ')} → ok`)
|
||||
} catch (err) {
|
||||
failed = true
|
||||
console.error(` batch ${i / BATCH + 1} FAILED: ${err instanceof Error ? err.message : err}`)
|
||||
}
|
||||
}
|
||||
|
||||
if (failed) {
|
||||
console.error(
|
||||
'❌ DOCS PUSH INCOMPLETE — soulcraft.com/docs may trail npm. ' +
|
||||
'Re-run `node scripts/push-docs.js` or ping venue on VENUE-DOCS-RELEASE-PUSH.'
|
||||
)
|
||||
process.exit(1)
|
||||
}
|
||||
console.log('✅ Docs pushed.')
|
||||
|
|
@ -196,6 +196,18 @@ else
|
|||
fi
|
||||
echo -e "${GREEN}✅ GitHub release created${NC}\n"
|
||||
|
||||
# Step 12: Push public docs to the soulcraft.com docs ingest door
|
||||
# (VENUE-DOCS-RELEASE-PUSH). Skips with a loud warning when
|
||||
# DOCS_INGEST_SECRET is unset; fails loudly (without undoing the publish —
|
||||
# that already happened) when a push errors, so the docs site never
|
||||
# silently trails npm.
|
||||
echo -e "${BLUE}1️⃣2️⃣ Pushing public docs to soulcraft.com/docs...${NC}"
|
||||
if node scripts/push-docs.js; then
|
||||
echo -e "${GREEN}✅ Docs push step done${NC}\n"
|
||||
else
|
||||
echo -e "${RED}❌ Docs push FAILED — soulcraft.com/docs trails npm until re-run or interim sync${NC}\n"
|
||||
fi
|
||||
|
||||
echo -e "${GREEN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
|
||||
echo -e "${GREEN}🎉 Release ${NEW_VERSION} complete!${NC}"
|
||||
echo -e "${GREEN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue