Files
OmniRoute/scripts/check/check-env-doc-sync.mjs
diegosouzapw f3b944a55a refactor(scripts): organize into build/dev/check/docs/i18n/ad-hoc subfolders
Reorganizes the 29 active scripts under scripts/ into purpose-driven
subfolders:

- scripts/build/    (11) — Build, install, publish, runtime env
- scripts/dev/      (13) — Dev servers, test runners, healthchecks
- scripts/check/    (10) — Lint/validation/coverage checks
- scripts/docs/      (2) — Docs index and provider reference generation
- scripts/i18n/     (+3) — Adds Python translation utilities (check/validate/autotranslate)
- scripts/ad-hoc/    (4) — One-shot maintenance utilities

Updates all references in package.json, electron/package.json,
.husky/pre-commit, .github/workflows/ci.yml, Dockerfile, src/,
tests/, scripts/ internal cross-imports, playwright.config.ts,
and English docs (CODEBASE_DOCUMENTATION, ENVIRONMENT, FEATURES,
RELEASE_CHECKLIST, COVERAGE_PLAN, ELECTRON_GUIDE, I18N, GEMINI).

Also patches scripts/build/pack-artifact-policy.ts so the npm pack
allowlist mirrors the new layout.

Validates with:
- npm run lint            (exit 0 — pre-existing minified-bundle errors only)
- npm run typecheck:core  (exit 0)
- npm run check:docs-all  (exit 0)
- unit tests for moved scripts (57 tests pass)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-13 10:14:25 -03:00

132 lines
4.1 KiB
JavaScript

#!/usr/bin/env node
// Validates that env vars referenced in code appear in .env.example AND in docs/ENVIRONMENT.md.
// Exits 0 on success, 1 on missing entries. Designed for use in pre-commit / CI.
//
// Run: node scripts/check/check-env-doc-sync.mjs
// Strict mode: node scripts/check/check-env-doc-sync.mjs --strict
// In strict mode, missing entries cause failure. In default mode, only summary is printed.
import fs from "node:fs";
import path from "node:path";
import { fileURLToPath } from "node:url";
import { execSync } from "node:child_process";
const __dirname = path.dirname(fileURLToPath(import.meta.url));
const ROOT = path.resolve(__dirname, "..", "..");
const ENV_EXAMPLE = path.join(ROOT, ".env.example");
const ENV_DOC = path.join(ROOT, "docs", "ENVIRONMENT.md");
const STRICT = process.argv.includes("--strict");
// Vars that are intentionally not documented or detected in code via dynamic patterns.
const IGNORE = new Set([
"NODE_ENV",
"PATH",
"HOME",
"USER",
"PWD",
"SHELL",
"TERM",
"TZ",
"LANG",
"LC_ALL",
"CI",
"GITHUB_ACTIONS",
"RUNNER_OS",
// Add false positives here as discovered.
]);
function readEnvExampleVars() {
if (!fs.existsSync(ENV_EXAMPLE)) {
console.error(`${ENV_EXAMPLE} not found`);
process.exit(2);
}
const txt = fs.readFileSync(ENV_EXAMPLE, "utf8");
const vars = new Set();
for (const line of txt.split("\n")) {
// Match both "VAR=value" and "# VAR=value" (commented-out examples are still documented)
const m = line.match(/^#?\s*([A-Z][A-Z0-9_]+)\s*=/);
if (m) vars.add(m[1]);
}
return vars;
}
function readEnvDocVars() {
if (!fs.existsSync(ENV_DOC)) {
console.error(`${ENV_DOC} not found`);
process.exit(2);
}
const txt = fs.readFileSync(ENV_DOC, "utf8");
const vars = new Set();
// Match `VAR_NAME` in inline code or table cells.
for (const m of txt.matchAll(/`([A-Z][A-Z0-9_]{2,})`/g)) {
vars.add(m[1]);
}
return vars;
}
function readCodeVars() {
const vars = new Set();
let stdout;
try {
stdout = execSync(
"grep -rhoE 'process\\.env\\.[A-Z][A-Z0-9_]+' src/ open-sse/ bin/ scripts/ 2>/dev/null || true",
{ cwd: ROOT, encoding: "utf8", maxBuffer: 20 * 1024 * 1024 }
);
} catch (e) {
console.error(`✗ grep failed: ${e.message}`);
process.exit(2);
}
for (const line of stdout.split("\n")) {
const m = line.match(/^process\.env\.([A-Z][A-Z0-9_]+)$/);
if (m && !IGNORE.has(m[1])) vars.add(m[1]);
}
return vars;
}
function diff(set, against) {
return [...set].filter((v) => !against.has(v)).sort();
}
function main() {
const codeVars = readCodeVars();
const exampleVars = readEnvExampleVars();
const docVars = readEnvDocVars();
const inCodeMissingExample = diff(codeVars, exampleVars);
const inCodeMissingDoc = diff(codeVars, docVars);
const inExampleMissingDoc = diff(exampleVars, docVars);
const inExampleMissingCode = diff(exampleVars, codeVars);
console.log("Env var sync report");
console.log("===================");
console.log(`Code references: ${codeVars.size} unique vars`);
console.log(`In .env.example: ${exampleVars.size} unique vars`);
console.log(`In docs/ENVIRONMENT.md: ${docVars.size} unique vars (heuristic)`);
console.log();
function printList(label, list) {
if (list.length === 0) {
console.log(`${label}: none`);
} else {
console.log(`${label}: ${list.length}`);
for (const v of list.slice(0, 30)) console.log(` - ${v}`);
if (list.length > 30) console.log(` ... and ${list.length - 30} more`);
}
}
printList("In code but missing from .env.example", inCodeMissingExample);
printList("In code but missing from ENVIRONMENT.md", inCodeMissingDoc);
printList("In .env.example but missing from ENVIRONMENT.md", inExampleMissingDoc);
printList("In .env.example but not referenced in code (dead?)", inExampleMissingCode);
const errors = inCodeMissingExample.length + inExampleMissingDoc.length;
if (STRICT && errors > 0) {
console.error(`\n${errors} drift(s) detected (strict mode)`);
process.exit(1);
}
console.log(`\n${errors === 0 ? "✓" : "⚠"} Done.`);
}
main();