feat: Hugo engine for iGaming affiliate template (layouts, assets, i18n, verified data, CI build script)
This commit is contained in:
@@ -0,0 +1,94 @@
|
||||
#!/usr/bin/env node
|
||||
/**
|
||||
* ci-build-brand.mjs — assemble ONE brand's Hugo build directory in CI and
|
||||
* (optionally) build it. Portable (Linux runner), no hardcoded paths.
|
||||
*
|
||||
* It merges three sources into a clean build dir:
|
||||
* 1. the TEMPLATE repo (engine): layouts/, assets/, static/, i18n/en.yaml,
|
||||
* package.json(+lock), tailwind.config.js, postcss.config.js, and the
|
||||
* VERIFIED, human-maintained data/locales.yaml + data/payment_methods.yaml
|
||||
* (these are never LLM-generated and must come from the template).
|
||||
* 2. the brand overlay from the content repo: repo/<brand>/{content,data,i18n,hugo.toml}
|
||||
* 3. the template's verified data/ files are copied AFTER the brand overlay so
|
||||
* they always win over anything the pipeline emitted.
|
||||
*
|
||||
* Usage:
|
||||
* node ci-build-brand.mjs \
|
||||
* --template <path-to-template-checkout> \
|
||||
* --brand-dir <path-to repo/<brand>> \
|
||||
* --out <build-dir> \
|
||||
* [--base-url https://<brand>.lilsus.fun/]
|
||||
*
|
||||
* Env fallbacks: TEMPLATE_DIR, BRAND_DIR, BUILD_DIR, BASE_URL.
|
||||
* Exits non-zero on any missing required input so CI fails loudly.
|
||||
*/
|
||||
import { existsSync, rmSync, mkdirSync, cpSync, copyFileSync, readdirSync } from 'node:fs';
|
||||
import { join, basename } from 'node:path';
|
||||
|
||||
function arg(name, envName) {
|
||||
const i = process.argv.indexOf('--' + name);
|
||||
if (i !== -1 && process.argv[i + 1]) return process.argv[i + 1];
|
||||
if (envName && process.env[envName]) return process.env[envName];
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const TEMPLATE = arg('template', 'TEMPLATE_DIR');
|
||||
const BRAND_DIR = arg('brand-dir', 'BRAND_DIR');
|
||||
const OUT = arg('out', 'BUILD_DIR');
|
||||
const BASE_URL = arg('base-url', 'BASE_URL'); // optional; only echoed as a hint
|
||||
|
||||
function die(msg) { console.error('ci-build-brand: ' + msg); process.exit(1); }
|
||||
if (!TEMPLATE || !existsSync(TEMPLATE)) die('missing/invalid --template (TEMPLATE_DIR): ' + TEMPLATE);
|
||||
if (!BRAND_DIR || !existsSync(BRAND_DIR)) die('missing/invalid --brand-dir (BRAND_DIR): ' + BRAND_DIR);
|
||||
if (!OUT) die('missing --out (BUILD_DIR)');
|
||||
if (!existsSync(join(BRAND_DIR, 'hugo.toml'))) die('brand dir has no hugo.toml: ' + BRAND_DIR);
|
||||
|
||||
const brand = basename(BRAND_DIR);
|
||||
console.log(`ci-build-brand: brand=${brand}`);
|
||||
console.log(` template = ${TEMPLATE}`);
|
||||
console.log(` brandDir = ${BRAND_DIR}`);
|
||||
console.log(` out = ${OUT}`);
|
||||
|
||||
// Fresh build dir.
|
||||
rmSync(OUT, { recursive: true, force: true });
|
||||
mkdirSync(OUT, { recursive: true });
|
||||
|
||||
// 1. Template engine dirs + files.
|
||||
const TEMPLATE_DIRS = ['layouts', 'assets', 'static', 'i18n', 'scripts'];
|
||||
for (const d of TEMPLATE_DIRS) {
|
||||
const s = join(TEMPLATE, d);
|
||||
if (existsSync(s)) cpSync(s, join(OUT, d), { recursive: true });
|
||||
}
|
||||
const TEMPLATE_FILES = ['package.json', 'package-lock.json', 'tailwind.config.js', 'postcss.config.js'];
|
||||
for (const f of TEMPLATE_FILES) {
|
||||
const s = join(TEMPLATE, f);
|
||||
if (existsSync(s)) copyFileSync(s, join(OUT, f));
|
||||
}
|
||||
|
||||
// 2. Brand overlay: content, data, i18n, hugo.toml.
|
||||
for (const d of ['content', 'data', 'i18n']) {
|
||||
const s = join(BRAND_DIR, d);
|
||||
if (existsSync(s)) cpSync(s, join(OUT, d), { recursive: true });
|
||||
}
|
||||
copyFileSync(join(BRAND_DIR, 'hugo.toml'), join(OUT, 'hugo.toml'));
|
||||
|
||||
// Drop the human-note marker if the pipeline emitted it (not valid Hugo data).
|
||||
rmSync(join(OUT, 'data', 'NOTE-locales-payments.md'), { force: true });
|
||||
|
||||
// 3. VERIFIED data that must come from the template and always win.
|
||||
mkdirSync(join(OUT, 'data'), { recursive: true });
|
||||
for (const f of ['locales.yaml', 'payment_methods.yaml']) {
|
||||
const s = join(TEMPLATE, 'data', f);
|
||||
if (existsSync(s)) copyFileSync(s, join(OUT, 'data', f));
|
||||
else console.warn(` WARN: template is missing data/${f} — RG/payment partials may fail`);
|
||||
}
|
||||
|
||||
// Report.
|
||||
function countMd(dir) {
|
||||
if (!existsSync(dir)) return 0;
|
||||
return readdirSync(dir).filter((f) => f.endsWith('.md')).length;
|
||||
}
|
||||
console.log(` data files: ${existsSync(join(OUT, 'data')) ? readdirSync(join(OUT, 'data')).join(',') : 'none'}`);
|
||||
console.log(` content top-level .md files: ${countMd(join(OUT, 'content'))}`);
|
||||
console.log(`ci-build-brand: build dir ready at ${OUT}`);
|
||||
if (BASE_URL) console.log(` (build with: hugo --source "${OUT}" --gc --minify --baseURL "${BASE_URL}")`);
|
||||
Reference in New Issue
Block a user