@lunora/cli
Version:
The Lunora CLI: init, dev, deploy, codegen, run, reset, and migrate commands
80 lines (77 loc) • 3.22 kB
JavaScript
import { existsSync, readFileSync, writeFileSync } from 'node:fs';
import { evaluateSchemaDrift, parseSchemaSnapshot, SchemaSnapshotParseError, serializeSchemaSnapshot } from '@lunora/codegen';
const readBaseline = (snapshotPath) => {
if (!existsSync(snapshotPath)) {
return { status: "absent" };
}
let content;
try {
content = readFileSync(snapshotPath, "utf8");
} catch {
return { status: "corrupt" };
}
try {
const snapshot = parseSchemaSnapshot(content);
return snapshot === void 0 ? { status: "corrupt" } : { snapshot, status: "ok" };
} catch (error) {
if (error instanceof SchemaSnapshotParseError) {
return { status: "corrupt" };
}
throw error;
}
};
const writeBaseline = (snapshotPath, snapshot) => {
writeFileSync(snapshotPath, serializeSchemaSnapshot(snapshot), "utf8");
};
const corruptBaselineResult = (context) => {
const reason = `schema-drift gate: the committed baseline ${context.snapshotPath} is unreadable or malformed, so schema drift cannot be checked. Fix it (e.g. resolve a merge conflict in lunora/.lunora-schema.json), or pass --update-schema-baseline to regenerate it from the current schema.`;
if (context.updateBaseline && !context.readOnly) {
context.logger.warn(
`schema baseline was unreadable; regenerating from the current schema on success (--update-schema-baseline): ${context.snapshotPath}`
);
return { blocked: false, changes: [], reason, rebless: context.rebless };
}
if (!context.readOnly) {
context.logger.error(reason);
}
return { blocked: true, changes: [], reason };
};
const blockedDecisionResult = (decision, context) => {
if (!context.readOnly) {
context.logger.error(decision.reason);
}
if (context.updateBaseline && !context.readOnly) {
context.logger.warn(`schema baseline will be re-blessed despite breaking drift on success (--update-schema-baseline): ${context.snapshotPath}`);
return { blocked: false, changes: decision.changes, reason: decision.reason, rebless: context.rebless };
}
return { blocked: true, changes: decision.changes, reason: decision.reason };
};
const runSchemaDriftGate = (options) => {
const { allowDrift, codegen, logger, readOnly = false, updateBaseline = false } = options;
const snapshotPath = codegen.schemaSnapshotPath;
const baseline = readBaseline(snapshotPath);
const context = {
logger,
readOnly,
rebless: () => {
writeBaseline(snapshotPath, codegen.schemaSnapshot);
},
snapshotPath,
updateBaseline
};
if (baseline.status === "corrupt") {
return corruptBaselineResult(context);
}
const decision = evaluateSchemaDrift({ allowDrift, baseline: baseline.status === "ok" ? baseline.snapshot : void 0, current: codegen.schemaSnapshot });
if (decision.blocked) {
return blockedDecisionResult(decision, context);
}
if (decision.changes.length > 0) {
if (!readOnly) {
logger.info(decision.reason);
}
return { blocked: false, changes: decision.changes, reason: decision.reason, rebless: readOnly ? void 0 : context.rebless };
}
return { blocked: false, changes: decision.changes, reason: decision.reason };
};
export { runSchemaDriftGate as r };