feat: Hugo engine for iGaming affiliate template (layouts, assets, i18n, verified data, CI build script)

This commit is contained in:
pipeline
2026-07-16 06:07:22 +03:00
parent b9385a1f72
commit 1816ff2093
58 changed files with 5631 additions and 0 deletions
+94
View File
@@ -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}")`);