refactor(api-docs): write resolved specs to _build/ instead of mutating source
Post-process-specs.ts now writes resolved specs (with info, servers, and tag overlays applied) to api-docs/_build/ instead of back to source files. Downstream consumers (Redoc HTML generation, static spec copy) read from _build/. Source specs in api-docs/ are never mutated by the pipeline. This makes the pipeline idempotent — running it twice produces identical output — and keeps source spec diffs free of YAML re-serialization noise.pull/6942/head
parent
ebc91d2263
commit
f1674d0efa
|
|
@ -14,6 +14,7 @@ package-lock.json
|
|||
/content/influxdb*/**/api/**/*.html
|
||||
!api-docs/**/.config.yml
|
||||
/api-docs/redoc-static.html*
|
||||
/api-docs/_build/
|
||||
/helper-scripts/output/*
|
||||
/telegraf-build
|
||||
!telegraf-build/templates
|
||||
|
|
|
|||
|
|
@ -3,9 +3,11 @@
|
|||
# Generate API reference documentation for all InfluxDB products.
|
||||
#
|
||||
# Pipeline:
|
||||
# 1. post-process-specs.ts — apply info/servers overlays + tag configs
|
||||
# 2. generateRedocHtml — generate Redoc HTML pages with Hugo frontmatter
|
||||
# 3. generate-openapi-articles.js --static-only — copy specs to static/openapi/
|
||||
# 1. post-process-specs.ts — apply info/servers overlays + tag configs,
|
||||
# write resolved specs to _build/ (source specs are never mutated)
|
||||
# 2. generateRedocHtml — generate Redoc HTML from _build/ specs
|
||||
# 3. generate-openapi-articles.js --static-only — copy _build/ specs to
|
||||
# static/openapi/ for download
|
||||
#
|
||||
# Specs must already be fetched and bundled (via getswagger.sh) before running.
|
||||
#
|
||||
|
|
@ -49,8 +51,9 @@ done
|
|||
# ---------------------------------------------------------------------------
|
||||
# Step 1: Post-process specs (info/servers overlays + tag configs)
|
||||
# ---------------------------------------------------------------------------
|
||||
# Runs from the repo root because post-process-specs reads api-docs/.config.yml
|
||||
# paths relative to the api-docs/ directory.
|
||||
# Writes resolved specs to api-docs/_build/. Source specs are never mutated.
|
||||
# Runs from the repo root because post-process-specs reads .config.yml paths
|
||||
# relative to the api-docs/ directory.
|
||||
|
||||
echo ""
|
||||
echo "========================================"
|
||||
|
|
@ -65,6 +68,7 @@ cd api-docs
|
|||
# ---------------------------------------------------------------------------
|
||||
# Iterates each product's .config.yml, generates Redoc HTML wrapped in Hugo
|
||||
# frontmatter, and writes to content/{product}/api/_index.html.
|
||||
# Reads resolved specs from _build/ (written by Step 1).
|
||||
|
||||
function generateRedocHtml {
|
||||
local specPath="$1"
|
||||
|
|
@ -79,14 +83,17 @@ function generateRedocHtml {
|
|||
local apiName
|
||||
apiName=$(echo "$api" | sed 's/@.*//g;')
|
||||
|
||||
# Resolve info.yml: try spec directory first, fall back to product directory.
|
||||
# Resolve info.yml from source (not _build/) for Hugo frontmatter metadata.
|
||||
local specDir
|
||||
specDir=$(dirname "$specPath")
|
||||
# Map _build/ path back to source for content file resolution.
|
||||
local sourceSpecDir="${specDir#_build/}"
|
||||
local sourceProductDir="${productDir}"
|
||||
local infoYml=""
|
||||
if [ -f "$specDir/content/info.yml" ]; then
|
||||
infoYml="$specDir/content/info.yml"
|
||||
elif [ -f "$productDir/content/info.yml" ]; then
|
||||
infoYml="$productDir/content/info.yml"
|
||||
if [ -f "$sourceSpecDir/content/info.yml" ]; then
|
||||
infoYml="$sourceSpecDir/content/info.yml"
|
||||
elif [ -f "$sourceProductDir/content/info.yml" ]; then
|
||||
infoYml="$sourceProductDir/content/info.yml"
|
||||
fi
|
||||
|
||||
local title
|
||||
|
|
@ -167,7 +174,7 @@ echo "Step 2: Generating Redoc HTML"
|
|||
echo "========================================"
|
||||
|
||||
# Iterate product directories that contain a .config.yml.
|
||||
for configPath in $(find . -name '.config.yml' -not -path './.config.yml' -not -path '*/node_modules/*' -not -path '*/openapi/*'); do
|
||||
for configPath in $(find . -name '.config.yml' -not -path './.config.yml' -not -path '*/node_modules/*' -not -path '*/openapi/*' -not -path './_build/*'); do
|
||||
productDir=$(dirname "$configPath")
|
||||
# Strip leading ./
|
||||
productDir="${productDir#./}"
|
||||
|
|
@ -186,17 +193,20 @@ for configPath in $(find . -name '.config.yml' -not -path './.config.yml' -not -
|
|||
echo "======Building $productDir $api======"
|
||||
|
||||
specRootPath=$(yq e ".apis | .$api | .root" "$configPath")
|
||||
specPath="$productDir/$specRootPath"
|
||||
# Read resolved spec from _build/ (written by Step 1)
|
||||
specPath="_build/$productDir/$specRootPath"
|
||||
|
||||
if [ -d "$specPath" ] || [ ! -f "$specPath" ]; then
|
||||
echo "OpenAPI spec $specPath doesn't exist. Skipping."
|
||||
echo "Resolved spec $specPath doesn't exist. Run Step 1 first. Skipping."
|
||||
continue
|
||||
fi
|
||||
|
||||
# If -c flag set, only regenerate specs that differ from master.
|
||||
# Check the source spec (not _build/) for git diff.
|
||||
update=0
|
||||
if [[ $generate_changed == 0 ]]; then
|
||||
diff_result=$(git diff --name-status master -- "${specPath}" 2>/dev/null || true)
|
||||
sourceSpecPath="$productDir/$specRootPath"
|
||||
diff_result=$(git diff --name-status master -- "${sourceSpecPath}" 2>/dev/null || true)
|
||||
if [[ -z "$diff_result" ]]; then
|
||||
update=1
|
||||
fi
|
||||
|
|
|
|||
|
|
@ -82,7 +82,9 @@ type ProductConfigMap = Record<string, ProductConfig>;
|
|||
|
||||
// Calculate the relative paths
|
||||
const DOCS_ROOT = '.';
|
||||
const API_DOCS_ROOT = 'api-docs';
|
||||
// Read resolved specs from _build/ (written by post-process-specs.ts).
|
||||
// Source specs in api-docs/ are never read directly by this script.
|
||||
const API_DOCS_ROOT = 'api-docs/_build';
|
||||
|
||||
// CLI flags
|
||||
const validateLinks = process.argv.includes('--validate-links');
|
||||
|
|
@ -1036,9 +1038,9 @@ const LINK_PATTERN = /\/influxdb\/version\//g;
|
|||
* 'api-docs/enterprise_influxdb/v1/influxdb-enterprise-v1-openapi.yaml' → '/enterprise_influxdb/v1'
|
||||
*/
|
||||
function deriveProductPath(specPath: string): string {
|
||||
// Match: api-docs/(enterprise_influxdb|influxdb3|influxdb)/(product-or-version)/...
|
||||
// Match: api-docs/[_build/](enterprise_influxdb|influxdb3|influxdb)/(product-or-version)/...
|
||||
const match = specPath.match(
|
||||
/api-docs\/(enterprise_influxdb|influxdb3?)\/([\w-]+)\//
|
||||
/api-docs\/(?:_build\/)?(enterprise_influxdb|influxdb3?)\/([\w-]+)\//
|
||||
);
|
||||
if (!match) {
|
||||
throw new Error(`Cannot derive product path from: ${specPath}`);
|
||||
|
|
|
|||
|
|
@ -97,6 +97,9 @@ interface OpenApiSpec {
|
|||
|
||||
const LOG_PREFIX = '[post-process]';
|
||||
|
||||
/** Build output directory for resolved specs. Source specs are never mutated. */
|
||||
const BUILD_DIR = '_build';
|
||||
|
||||
/** Product directories that contain a .config.yml with `apis:` entries. */
|
||||
const PRODUCT_DIRS = [
|
||||
'influxdb3/core',
|
||||
|
|
@ -338,7 +341,11 @@ function applyTagConfig(
|
|||
|
||||
/**
|
||||
* Process a single product directory: read `.config.yml`, find spec files,
|
||||
* apply content overlays and tag configs.
|
||||
* apply content overlays and tag configs, write resolved specs to _build/.
|
||||
*
|
||||
* Source specs in api-docs/ are never mutated. Resolved output goes to
|
||||
* api-docs/_build/{productDir}/{specFile} for downstream consumers
|
||||
* (Redoc HTML, generate-openapi-articles.ts).
|
||||
*/
|
||||
function processProduct(apiDocsRoot: string, productDir: string): void {
|
||||
const productAbsDir = path.join(apiDocsRoot, productDir);
|
||||
|
|
@ -369,22 +376,22 @@ function processProduct(apiDocsRoot: string, productDir: string): void {
|
|||
}
|
||||
|
||||
// Apply all transforms
|
||||
let modified = false;
|
||||
modified =
|
||||
applyInfoOverlay(spec, specDir, productAbsDir, label) || modified;
|
||||
modified =
|
||||
applyServersOverlay(spec, specDir, productAbsDir, label) || modified;
|
||||
applyInfoOverlay(spec, specDir, productAbsDir, label);
|
||||
applyServersOverlay(spec, specDir, productAbsDir, label);
|
||||
|
||||
const tagConfigPath = path.join(specDir, 'tags.yml');
|
||||
if (fs.existsSync(tagConfigPath)) {
|
||||
modified = applyTagConfig(spec, tagConfigPath, label) || modified;
|
||||
applyTagConfig(spec, tagConfigPath, label);
|
||||
}
|
||||
|
||||
// Write only if something changed
|
||||
if (modified) {
|
||||
writeYaml(specAbsPath, spec);
|
||||
log(`${label}: wrote ${path.basename(specAbsPath)}`);
|
||||
// Write resolved spec to _build/, mirroring the source path structure
|
||||
const outPath = path.join(apiDocsRoot, BUILD_DIR, productDir, specRelPath);
|
||||
const outDir = path.dirname(outPath);
|
||||
if (!fs.existsSync(outDir)) {
|
||||
fs.mkdirSync(outDir, { recursive: true });
|
||||
}
|
||||
writeYaml(outPath, spec);
|
||||
log(`${label}: wrote ${path.relative(apiDocsRoot, outPath)}`);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue