Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
31a8885
build(savanna): split control-plane catalog into per-endpoint pages
ngarakapati Sep 20, 2026
3e6067e
docs(savanna): rewrite REST API overview and supporting guides
ngarakapati Sep 20, 2026
dff3598
docs(savanna): point administration links at REST API API key guide
ngarakapati Sep 20, 2026
db99fe7
build: add hot-reload dev preview for Savanna and antora-ui
ngarakapati Sep 20, 2026
8af0a62
build(savanna): merge OpenAPI snapshot into endpoint splitter
ngarakapati Sep 20, 2026
0af2d21
docs(savanna): regenerate REST API pages from expanded catalog
ngarakapati Sep 20, 2026
7a83075
build(savanna): allowlist public endpoints in catalog splitter
ngarakapati Sep 20, 2026
fcb2e37
docs(savanna): regenerate REST API reference for allowlisted endpoints
ngarakapati Sep 20, 2026
4d793ce
build(savanna): clarify public endpoint display names
ngarakapati Sep 20, 2026
9e0c592
docs(savanna): regenerate REST API pages after endpoint renames
ngarakapati Sep 20, 2026
c0dcd5b
build(savanna): add OpenAPI-driven API reference page renderer
ngarakapati Sep 21, 2026
a950324
build(savanna): render endpoint pages from OpenAPI in splitter
ngarakapati Sep 21, 2026
56e47e3
docs(savanna): regenerate REST API reference from OpenAPI pages
ngarakapati Sep 21, 2026
eb9d8ec
docs(savanna): retire draft REST API overview page
ngarakapati Sep 21, 2026
b73634c
docs(savanna): add Run in Postman link on REST API overview
ngarakapati Sep 21, 2026
aa9a6b3
build(savanna): generate Postman collection from public endpoints
ngarakapati Sep 21, 2026
5cf82f5
build(savanna): drop generated Postman collection from splitter
ngarakapati Sep 21, 2026
248e50e
fix(savanna): use angle-bracket placeholders in API samples
ngarakapati Sep 21, 2026
0382600
docs(savanna): regenerate endpoint pages with placeholder samples
ngarakapati Sep 21, 2026
c63efce
docs(savanna): hide data-plane page and link server API docs
ngarakapati Sep 21, 2026
0c101fe
chore: rebuild deploy preview against updated UI bundle
ngarakapati Sep 24, 2026
1b05375
fix(savanna): fail if an allowlist endpoint is missing from the catalog
ngarakapati Sep 25, 2026
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
112 changes: 110 additions & 2 deletions gulpfile.js
Original file line number Diff line number Diff line change
@@ -1,23 +1,44 @@
'use strict'

const connect = require('gulp-connect')
const { execSync } = require('child_process')
const fs = require('fs')
const generator = require('@antora/site-generator-default')
const { reload: livereload } = process.env.LIVERELOAD === 'true' ? require('gulp-connect') : {}
const log = require('fancy-log')
const { series, src, watch } = require('gulp')
const path = require('path')
const yaml = require('js-yaml')

const playbookFilename = process.env.PLAYBOOK || 'antora-playbook.yml'
const playbook = yaml.load(fs.readFileSync(playbookFilename, 'utf8'))
const outputDir = (playbook.output || {}).dir || './build/site'
const serverConfig = { name: 'Preview Site', livereload, port: Number(process.env.PORT) || 5000, root: outputDir }
const serverConfig = {
name: 'Preview Site',
livereload,
port: Number(process.env.PORT) || 5000,
root: outputDir,
}
const antoraArgs = ['--playbook', playbookFilename]
const watchPatterns = playbook.content.sources.filter((source) => !source.url.includes(':')).reduce((accum, source) => {
accum.push(`${source.url}/${source.start_path ? source.start_path + '/' : ''}antora.yml`)
accum.push(`${source.url}/${source.start_path ? source.start_path + '/' : ''}**/*.adoc`)
return accum
}, [])

const savannaSrc = 'modules/savanna'
const previewSrc = 'build/preview-src'
const antoraUiRoot = path.resolve(__dirname, process.env.ANTORA_UI || '../antora-ui')
const uiBundleZip = path.join(antoraUiRoot, 'build/ui-bundle.zip')

const gitEnv = {
...process.env,
GIT_AUTHOR_NAME: 'Preview',
GIT_AUTHOR_EMAIL: 'preview@local',
GIT_COMMITTER_NAME: 'Preview',
GIT_COMMITTER_EMAIL: 'preview@local',
}

function generate (done) {
generator(antoraArgs, process.env)
.then(() => done())
Expand All @@ -27,6 +48,47 @@ function generate (done) {
})
}

function ensurePreviewSrcRepo () {
if (!fs.existsSync(previewSrc)) {
fs.mkdirSync(previewSrc, { recursive: true })
}
if (!fs.existsSync(path.join(previewSrc, '.git'))) {
execSync('git init -q', { cwd: previewSrc })
}
}

function syncPreviewSrc (done) {
ensurePreviewSrcRepo()
execSync(`rsync -a --delete --exclude '.git' ${savannaSrc}/ ${previewSrc}/`, { stdio: 'inherit' })
execSync('git add -A', { cwd: previewSrc, env: gitEnv })
try {
execSync('git diff --cached --quiet', { cwd: previewSrc, stdio: 'ignore' })
} catch (_) {
execSync('git commit -q -m "local preview snapshot"', { cwd: previewSrc, env: gitEnv })
}
done()
}

function rebuildUiBundle (done) {
if (!fs.existsSync(antoraUiRoot)) {
done(new Error(`antora-ui not found at ${antoraUiRoot} (set ANTORA_UI)`))
return
}
log(`Building UI bundle in ${antoraUiRoot}`)
execSync('npx gulp bundle', { cwd: antoraUiRoot, stdio: 'inherit' })
execSync('rm -rf build/ui-bundle && mkdir -p build/ui-bundle', { stdio: 'inherit' })
execSync(`unzip -oq ${JSON.stringify(uiBundleZip)} -d build/ui-bundle`, { stdio: 'inherit' })
// An empty site.js extracts without error and the preview still renders, so the
// only symptom is that nothing scripted works — collapsible nav groups, the
// language picker, search. Fail here instead of serving a mute site.
const siteJs = 'build/ui-bundle/js/site.js'
if (!fs.existsSync(siteJs) || !fs.statSync(siteJs).size) {
done(new Error(`${siteJs} is missing or empty; rerun after ${uiBundleZip} finishes writing`))
return
}
done()
}

function serve (done) {
connect.server(serverConfig, function () {
this.server.on('close', done)
Expand All @@ -35,4 +97,50 @@ function serve (done) {
})
}

module.exports = { serve, generate, default: series(generate, serve) }
function debounce (fn, waitMs) {
let timer
return (done) => {
clearTimeout(timer)
timer = setTimeout(() => fn(done), waitMs)
}
}

function devHot (done) {
const contentRebuild = series(syncPreviewSrc, generate)
const uiRebuild = series(rebuildUiBundle, generate)

series(syncPreviewSrc, rebuildUiBundle, generate)((err) => {
if (err) {
done(err)
return
}

connect.server(serverConfig, function () {
this.server.on('close', done)

const contentPatterns = [
`${savannaSrc}/**/*`,
'lib/**/*.js',
]
const uiPatterns = [
path.join(antoraUiRoot, 'src/**/*'),
path.join(antoraUiRoot, 'gulp.d/**/*'),
]

watch(contentPatterns, debounce((cb) => {
log('cloud-docs change detected — syncing content')
contentRebuild(cb)
}, 400))

watch(uiPatterns, debounce((cb) => {
log('antora-ui change detected — rebuilding bundle')
uiRebuild(cb)
}, 800))

log(`Hot preview: http://localhost:${serverConfig.port}`)
log(`Watching ${savannaSrc} and ${path.join(antoraUiRoot, 'src')}`)
})
})
}

module.exports = { serve, generate, devHot, default: series(generate, serve) }
Loading
Loading