@famgia/omnify-cli 0.0.122 → 0.0.124
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +431 -12
- package/dist/cli.js.map +1 -1
- package/dist/index.cjs +36 -0
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +40 -1
- package/dist/index.js.map +1 -1
- package/package.json +13 -13
package/dist/cli.js
CHANGED
|
@@ -7,10 +7,10 @@ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require
|
|
|
7
7
|
});
|
|
8
8
|
|
|
9
9
|
// src/cli.ts
|
|
10
|
-
import { existsSync as
|
|
11
|
-
import { resolve as
|
|
10
|
+
import { existsSync as existsSync10 } from "fs";
|
|
11
|
+
import { resolve as resolve12 } from "path";
|
|
12
12
|
import { Command } from "commander";
|
|
13
|
-
import { OmnifyError as
|
|
13
|
+
import { OmnifyError as OmnifyError7 } from "@famgia/omnify-core";
|
|
14
14
|
|
|
15
15
|
// src/commands/init.ts
|
|
16
16
|
import { existsSync as existsSync2, mkdirSync, writeFileSync as writeFileSync2 } from "fs";
|
|
@@ -892,7 +892,10 @@ import {
|
|
|
892
892
|
compareSchemasDeep,
|
|
893
893
|
isLockFileV2,
|
|
894
894
|
validateMigrations,
|
|
895
|
-
getMigrationsToRegenerate
|
|
895
|
+
getMigrationsToRegenerate,
|
|
896
|
+
VERSION_CHAIN_FILE,
|
|
897
|
+
readVersionChain,
|
|
898
|
+
checkBulkLockViolation
|
|
896
899
|
} from "@famgia/omnify-atlas";
|
|
897
900
|
import {
|
|
898
901
|
generateMigrations,
|
|
@@ -1518,6 +1521,42 @@ async function runGenerate(options) {
|
|
|
1518
1521
|
const currentSnapshots = await buildSchemaSnapshots(schemas);
|
|
1519
1522
|
const v2Lock = existingLock && isLockFileV2(existingLock) ? existingLock : null;
|
|
1520
1523
|
const comparison = compareSchemasDeep(currentSnapshots, v2Lock);
|
|
1524
|
+
const chainFilePath = resolve7(rootDir, VERSION_CHAIN_FILE);
|
|
1525
|
+
const versionChain = await readVersionChain(chainFilePath);
|
|
1526
|
+
if (versionChain && comparison.hasChanges) {
|
|
1527
|
+
const schemaActions = [];
|
|
1528
|
+
for (const change of comparison.changes) {
|
|
1529
|
+
if (change.changeType === "removed") {
|
|
1530
|
+
schemaActions.push({ name: change.schemaName, action: "delete" });
|
|
1531
|
+
} else if (change.changeType === "modified") {
|
|
1532
|
+
schemaActions.push({ name: change.schemaName, action: "modify" });
|
|
1533
|
+
}
|
|
1534
|
+
}
|
|
1535
|
+
if (schemaActions.length > 0) {
|
|
1536
|
+
const lockCheck = checkBulkLockViolation(versionChain, schemaActions);
|
|
1537
|
+
if (!lockCheck.allowed) {
|
|
1538
|
+
logger.newline();
|
|
1539
|
+
logger.error("\u{1F512} VERSION LOCK VIOLATION DETECTED");
|
|
1540
|
+
logger.error("");
|
|
1541
|
+
logger.error("The following schemas are locked in production:");
|
|
1542
|
+
for (const name of lockCheck.affectedSchemas) {
|
|
1543
|
+
logger.error(` \u2022 ${name}`);
|
|
1544
|
+
}
|
|
1545
|
+
logger.error("");
|
|
1546
|
+
logger.error(`Locked in version(s): ${lockCheck.lockedInVersions.join(", ")}`);
|
|
1547
|
+
logger.error("");
|
|
1548
|
+
logger.error("These schemas CANNOT be modified or deleted.");
|
|
1549
|
+
logger.error("This is enforced by the blockchain-like version chain.");
|
|
1550
|
+
logger.newline();
|
|
1551
|
+
throw new OmnifyError4(
|
|
1552
|
+
lockCheck.reason ?? "Schema modification blocked by version lock",
|
|
1553
|
+
"E407",
|
|
1554
|
+
void 0,
|
|
1555
|
+
"Restore the original schema files or create new schemas instead of modifying locked ones."
|
|
1556
|
+
);
|
|
1557
|
+
}
|
|
1558
|
+
}
|
|
1559
|
+
}
|
|
1521
1560
|
if (existingLock && config.output.laravel?.migrationsPath) {
|
|
1522
1561
|
const migrationsDir = resolve7(rootDir, config.output.laravel.migrationsPath);
|
|
1523
1562
|
const migrationValidation = await validateMigrations(existingLock, migrationsDir);
|
|
@@ -1783,10 +1822,10 @@ async function confirm2(message) {
|
|
|
1783
1822
|
input: process.stdin,
|
|
1784
1823
|
output: process.stdout
|
|
1785
1824
|
});
|
|
1786
|
-
return new Promise((
|
|
1825
|
+
return new Promise((resolve13) => {
|
|
1787
1826
|
rl.question(`${message} (y/N) `, (answer) => {
|
|
1788
1827
|
rl.close();
|
|
1789
|
-
|
|
1828
|
+
resolve13(answer.toLowerCase() === "y" || answer.toLowerCase() === "yes");
|
|
1790
1829
|
});
|
|
1791
1830
|
});
|
|
1792
1831
|
}
|
|
@@ -2134,7 +2173,7 @@ function cloneRepo(repo, targetDir) {
|
|
|
2134
2173
|
logger.success("Repository cloned successfully");
|
|
2135
2174
|
}
|
|
2136
2175
|
function runCommand(command, targetDir) {
|
|
2137
|
-
return new Promise((
|
|
2176
|
+
return new Promise((resolve13, reject) => {
|
|
2138
2177
|
const child = spawn(command, [], {
|
|
2139
2178
|
cwd: targetDir,
|
|
2140
2179
|
shell: true,
|
|
@@ -2142,7 +2181,7 @@ function runCommand(command, targetDir) {
|
|
|
2142
2181
|
});
|
|
2143
2182
|
child.on("close", (code) => {
|
|
2144
2183
|
if (code === 0) {
|
|
2145
|
-
|
|
2184
|
+
resolve13();
|
|
2146
2185
|
} else {
|
|
2147
2186
|
reject(new Error(`Command failed with exit code ${code}`));
|
|
2148
2187
|
}
|
|
@@ -2247,6 +2286,384 @@ function registerCreateProjectCommand(program2) {
|
|
|
2247
2286
|
});
|
|
2248
2287
|
}
|
|
2249
2288
|
|
|
2289
|
+
// src/commands/deploy.ts
|
|
2290
|
+
import { existsSync as existsSync8 } from "fs";
|
|
2291
|
+
import { resolve as resolve10, dirname as dirname7 } from "path";
|
|
2292
|
+
import { loadSchemas as loadSchemas4, OmnifyError as OmnifyError5 } from "@famgia/omnify-core";
|
|
2293
|
+
import {
|
|
2294
|
+
VERSION_CHAIN_FILE as VERSION_CHAIN_FILE2,
|
|
2295
|
+
readVersionChain as readVersionChain2,
|
|
2296
|
+
deployVersion,
|
|
2297
|
+
getChainSummary,
|
|
2298
|
+
verifyChain
|
|
2299
|
+
} from "@famgia/omnify-atlas";
|
|
2300
|
+
async function confirmDeploy(schemaCount, environment, version) {
|
|
2301
|
+
const { createInterface: createInterface2 } = await import("readline");
|
|
2302
|
+
const rl = createInterface2({
|
|
2303
|
+
input: process.stdin,
|
|
2304
|
+
output: process.stdout
|
|
2305
|
+
});
|
|
2306
|
+
return new Promise((resolve13) => {
|
|
2307
|
+
logger.newline();
|
|
2308
|
+
logger.warn("\u26A0\uFE0F WARNING: This action is IRREVERSIBLE!");
|
|
2309
|
+
logger.warn("");
|
|
2310
|
+
logger.warn(` Environment: ${environment}`);
|
|
2311
|
+
logger.warn(` Version: ${version}`);
|
|
2312
|
+
logger.warn(` Schemas: ${schemaCount} file(s)`);
|
|
2313
|
+
logger.warn("");
|
|
2314
|
+
logger.warn(" Once locked, these schema files CANNOT be:");
|
|
2315
|
+
logger.warn(" \u2022 Deleted");
|
|
2316
|
+
logger.warn(" \u2022 Modified (content hash is recorded)");
|
|
2317
|
+
logger.warn("");
|
|
2318
|
+
logger.warn(" This creates an immutable blockchain-like record.");
|
|
2319
|
+
logger.newline();
|
|
2320
|
+
rl.question(' Type "LOCK" to confirm: ', (answer) => {
|
|
2321
|
+
rl.close();
|
|
2322
|
+
resolve13(answer.trim().toUpperCase() === "LOCK");
|
|
2323
|
+
});
|
|
2324
|
+
});
|
|
2325
|
+
}
|
|
2326
|
+
async function collectSchemaFiles(schemasDir) {
|
|
2327
|
+
const files = [];
|
|
2328
|
+
const schemas = await loadSchemas4(schemasDir);
|
|
2329
|
+
for (const [name, schema] of Object.entries(schemas)) {
|
|
2330
|
+
files.push({
|
|
2331
|
+
name,
|
|
2332
|
+
relativePath: schema.relativePath,
|
|
2333
|
+
filePath: schema.filePath
|
|
2334
|
+
});
|
|
2335
|
+
}
|
|
2336
|
+
return files;
|
|
2337
|
+
}
|
|
2338
|
+
async function runDeploy(options) {
|
|
2339
|
+
logger.setVerbose(options.verbose ?? false);
|
|
2340
|
+
logger.header("Deploy Version Lock");
|
|
2341
|
+
logger.debug("Loading configuration...");
|
|
2342
|
+
const { config, configPath: configPath2 } = await loadConfig();
|
|
2343
|
+
const rootDir = configPath2 ? dirname7(configPath2) : process.cwd();
|
|
2344
|
+
validateConfig(config, rootDir);
|
|
2345
|
+
const schemasDir = resolve10(rootDir, config.schemasDir);
|
|
2346
|
+
const chainFilePath = resolve10(rootDir, VERSION_CHAIN_FILE2);
|
|
2347
|
+
if (!existsSync8(schemasDir)) {
|
|
2348
|
+
throw new OmnifyError5(
|
|
2349
|
+
`Schemas directory not found: ${schemasDir}`,
|
|
2350
|
+
"E003",
|
|
2351
|
+
void 0,
|
|
2352
|
+
"Make sure the schemasDir in omnify.config.ts is correct."
|
|
2353
|
+
);
|
|
2354
|
+
}
|
|
2355
|
+
logger.step("Collecting schema files...");
|
|
2356
|
+
const schemaFiles = await collectSchemaFiles(schemasDir);
|
|
2357
|
+
if (schemaFiles.length === 0) {
|
|
2358
|
+
throw new OmnifyError5(
|
|
2359
|
+
"No schema files found",
|
|
2360
|
+
"E003",
|
|
2361
|
+
void 0,
|
|
2362
|
+
"Create schema files in your schemas directory before deploying."
|
|
2363
|
+
);
|
|
2364
|
+
}
|
|
2365
|
+
logger.info(`Found ${schemaFiles.length} schema file(s)`);
|
|
2366
|
+
const existingChain = await readVersionChain2(chainFilePath);
|
|
2367
|
+
if (existingChain) {
|
|
2368
|
+
const summary = getChainSummary(existingChain);
|
|
2369
|
+
logger.debug(`Existing chain: ${summary.blockCount} block(s), ${summary.schemaCount} schema(s)`);
|
|
2370
|
+
logger.step("Verifying chain integrity...");
|
|
2371
|
+
const verification = await verifyChain(existingChain, schemasDir);
|
|
2372
|
+
if (!verification.valid) {
|
|
2373
|
+
logger.error("Chain integrity verification failed!");
|
|
2374
|
+
if (verification.corruptedBlocks.length > 0) {
|
|
2375
|
+
logger.error("");
|
|
2376
|
+
logger.error("Corrupted blocks:");
|
|
2377
|
+
for (const block of verification.corruptedBlocks) {
|
|
2378
|
+
logger.error(` \u2022 ${block.version}: ${block.reason}`);
|
|
2379
|
+
}
|
|
2380
|
+
}
|
|
2381
|
+
if (verification.tamperedSchemas.length > 0) {
|
|
2382
|
+
logger.error("");
|
|
2383
|
+
logger.error("Tampered schemas (modified since lock):");
|
|
2384
|
+
for (const schema of verification.tamperedSchemas) {
|
|
2385
|
+
logger.error(` \u2022 ${schema.schemaName} (locked in ${schema.lockedInVersion})`);
|
|
2386
|
+
}
|
|
2387
|
+
}
|
|
2388
|
+
if (verification.deletedLockedSchemas.length > 0) {
|
|
2389
|
+
logger.error("");
|
|
2390
|
+
logger.error("Deleted locked schemas:");
|
|
2391
|
+
for (const schema of verification.deletedLockedSchemas) {
|
|
2392
|
+
logger.error(` \u2022 ${schema.schemaName} (locked in ${schema.lockedInVersion})`);
|
|
2393
|
+
}
|
|
2394
|
+
}
|
|
2395
|
+
throw new OmnifyError5(
|
|
2396
|
+
"Cannot deploy: chain integrity compromised",
|
|
2397
|
+
"E406",
|
|
2398
|
+
void 0,
|
|
2399
|
+
"Restore deleted/modified files to match the locked state, or contact support."
|
|
2400
|
+
);
|
|
2401
|
+
}
|
|
2402
|
+
logger.success("Chain integrity verified \u2713");
|
|
2403
|
+
} else {
|
|
2404
|
+
logger.info("Creating new version chain (first deployment)");
|
|
2405
|
+
}
|
|
2406
|
+
const environment = options.environment ?? "production";
|
|
2407
|
+
const version = options.version ?? generateVersionFromTimestamp();
|
|
2408
|
+
if (options.dryRun) {
|
|
2409
|
+
logger.newline();
|
|
2410
|
+
logger.info("DRY RUN - No changes will be made");
|
|
2411
|
+
logger.info("");
|
|
2412
|
+
logger.info(`Would create block:`);
|
|
2413
|
+
logger.info(` Version: ${version}`);
|
|
2414
|
+
logger.info(` Environment: ${environment}`);
|
|
2415
|
+
logger.info(` Schemas: ${schemaFiles.length} file(s)`);
|
|
2416
|
+
logger.info("");
|
|
2417
|
+
for (const file of schemaFiles) {
|
|
2418
|
+
logger.info(` \u2022 ${file.name} (${file.relativePath})`);
|
|
2419
|
+
}
|
|
2420
|
+
return;
|
|
2421
|
+
}
|
|
2422
|
+
if (!options.yes) {
|
|
2423
|
+
const confirmed = await confirmDeploy(schemaFiles.length, environment, version);
|
|
2424
|
+
if (!confirmed) {
|
|
2425
|
+
logger.newline();
|
|
2426
|
+
logger.info("Deployment cancelled.");
|
|
2427
|
+
return;
|
|
2428
|
+
}
|
|
2429
|
+
}
|
|
2430
|
+
logger.step("Creating version lock...");
|
|
2431
|
+
const deployOptions = {
|
|
2432
|
+
version,
|
|
2433
|
+
environment,
|
|
2434
|
+
deployedBy: options.deployedBy ?? process.env.USER ?? "unknown",
|
|
2435
|
+
comment: options.comment,
|
|
2436
|
+
skipConfirmation: true
|
|
2437
|
+
};
|
|
2438
|
+
const result = await deployVersion(chainFilePath, schemasDir, schemaFiles, deployOptions);
|
|
2439
|
+
if (!result.success) {
|
|
2440
|
+
throw new OmnifyError5(
|
|
2441
|
+
result.error ?? "Deployment failed",
|
|
2442
|
+
"E408"
|
|
2443
|
+
);
|
|
2444
|
+
}
|
|
2445
|
+
logger.newline();
|
|
2446
|
+
logger.success("\u{1F512} Version locked successfully!");
|
|
2447
|
+
logger.newline();
|
|
2448
|
+
if (result.block) {
|
|
2449
|
+
logger.info(` Version: ${result.block.version}`);
|
|
2450
|
+
logger.info(` Block Hash: ${result.block.blockHash.substring(0, 16)}...`);
|
|
2451
|
+
logger.info(` Locked At: ${result.block.lockedAt}`);
|
|
2452
|
+
logger.info(` Environment: ${result.block.environment}`);
|
|
2453
|
+
logger.info(` Schemas: ${result.block.schemas.length} file(s)`);
|
|
2454
|
+
}
|
|
2455
|
+
if (result.addedSchemas.length > 0) {
|
|
2456
|
+
logger.newline();
|
|
2457
|
+
logger.info(" New schemas locked:");
|
|
2458
|
+
for (const name of result.addedSchemas) {
|
|
2459
|
+
logger.info(` + ${name}`);
|
|
2460
|
+
}
|
|
2461
|
+
}
|
|
2462
|
+
if (result.warnings.length > 0) {
|
|
2463
|
+
logger.newline();
|
|
2464
|
+
logger.warn(" Warnings:");
|
|
2465
|
+
for (const warning of result.warnings) {
|
|
2466
|
+
logger.warn(` \u26A0 ${warning}`);
|
|
2467
|
+
}
|
|
2468
|
+
}
|
|
2469
|
+
logger.newline();
|
|
2470
|
+
logger.info(` Chain file: ${VERSION_CHAIN_FILE2}`);
|
|
2471
|
+
logger.info(" This file should be committed to version control.");
|
|
2472
|
+
}
|
|
2473
|
+
function generateVersionFromTimestamp() {
|
|
2474
|
+
const now = /* @__PURE__ */ new Date();
|
|
2475
|
+
const year = now.getFullYear();
|
|
2476
|
+
const month = String(now.getMonth() + 1).padStart(2, "0");
|
|
2477
|
+
const day = String(now.getDate()).padStart(2, "0");
|
|
2478
|
+
const hour = String(now.getHours()).padStart(2, "0");
|
|
2479
|
+
const minute = String(now.getMinutes()).padStart(2, "0");
|
|
2480
|
+
const second = String(now.getSeconds()).padStart(2, "0");
|
|
2481
|
+
return `v${year}.${month}.${day}-${hour}${minute}${second}`;
|
|
2482
|
+
}
|
|
2483
|
+
function registerDeployCommand(program2) {
|
|
2484
|
+
program2.command("deploy").description("Lock schema version for production (blockchain-like immutable record)").option("-v, --verbose", "Show detailed output").option("--version <version>", "Version name (e.g., v1.0.0, default: auto-generated)").option("-e, --environment <env>", "Deployment environment (default: production)", "production").option("-c, --comment <comment>", "Deployment comment").option("-y, --yes", "Skip confirmation prompt (for CI/CD)").option("--deployed-by <name>", "Deployer name (default: $USER)").option("--dry-run", "Show what would be locked without making changes").action(async (options) => {
|
|
2485
|
+
try {
|
|
2486
|
+
await runDeploy(options);
|
|
2487
|
+
} catch (error) {
|
|
2488
|
+
if (error instanceof OmnifyError5) {
|
|
2489
|
+
logger.formatError(error);
|
|
2490
|
+
process.exit(logger.getExitCode(error));
|
|
2491
|
+
} else if (error instanceof Error) {
|
|
2492
|
+
logger.error(error.message);
|
|
2493
|
+
process.exit(1);
|
|
2494
|
+
}
|
|
2495
|
+
process.exit(1);
|
|
2496
|
+
}
|
|
2497
|
+
});
|
|
2498
|
+
}
|
|
2499
|
+
|
|
2500
|
+
// src/commands/verify.ts
|
|
2501
|
+
import { existsSync as existsSync9 } from "fs";
|
|
2502
|
+
import { resolve as resolve11, dirname as dirname8 } from "path";
|
|
2503
|
+
import { OmnifyError as OmnifyError6 } from "@famgia/omnify-core";
|
|
2504
|
+
import {
|
|
2505
|
+
VERSION_CHAIN_FILE as VERSION_CHAIN_FILE3,
|
|
2506
|
+
readVersionChain as readVersionChain3,
|
|
2507
|
+
verifyChain as verifyChain2,
|
|
2508
|
+
getChainSummary as getChainSummary2,
|
|
2509
|
+
getLockedSchemas
|
|
2510
|
+
} from "@famgia/omnify-atlas";
|
|
2511
|
+
async function runVerify(options) {
|
|
2512
|
+
logger.setVerbose(options.verbose ?? false);
|
|
2513
|
+
if (!options.json) {
|
|
2514
|
+
logger.header("Verify Version Chain");
|
|
2515
|
+
}
|
|
2516
|
+
logger.debug("Loading configuration...");
|
|
2517
|
+
const { config, configPath: configPath2 } = await loadConfig();
|
|
2518
|
+
const rootDir = configPath2 ? dirname8(configPath2) : process.cwd();
|
|
2519
|
+
validateConfig(config, rootDir);
|
|
2520
|
+
const schemasDir = resolve11(rootDir, config.schemasDir);
|
|
2521
|
+
const chainFilePath = resolve11(rootDir, VERSION_CHAIN_FILE3);
|
|
2522
|
+
if (!existsSync9(chainFilePath)) {
|
|
2523
|
+
if (options.json) {
|
|
2524
|
+
console.log(JSON.stringify({
|
|
2525
|
+
valid: true,
|
|
2526
|
+
message: "No version chain exists yet",
|
|
2527
|
+
blockCount: 0,
|
|
2528
|
+
schemaCount: 0
|
|
2529
|
+
}, null, 2));
|
|
2530
|
+
return;
|
|
2531
|
+
}
|
|
2532
|
+
logger.info("No version chain exists yet.");
|
|
2533
|
+
logger.info('Run "npx omnify deploy" to create the first version lock.');
|
|
2534
|
+
return;
|
|
2535
|
+
}
|
|
2536
|
+
const chain = await readVersionChain3(chainFilePath);
|
|
2537
|
+
if (!chain) {
|
|
2538
|
+
throw new OmnifyError6(
|
|
2539
|
+
"Failed to read version chain file",
|
|
2540
|
+
"E406",
|
|
2541
|
+
void 0,
|
|
2542
|
+
`Check if ${VERSION_CHAIN_FILE3} is valid JSON.`
|
|
2543
|
+
);
|
|
2544
|
+
}
|
|
2545
|
+
const summary = getChainSummary2(chain);
|
|
2546
|
+
if (!options.json) {
|
|
2547
|
+
logger.step("Chain Summary");
|
|
2548
|
+
logger.info(` Blocks: ${summary.blockCount}`);
|
|
2549
|
+
logger.info(` Schemas: ${summary.schemaCount}`);
|
|
2550
|
+
logger.info(` First Lock: ${summary.firstVersion ?? "N/A"}`);
|
|
2551
|
+
logger.info(` Latest Lock: ${summary.latestVersion ?? "N/A"}`);
|
|
2552
|
+
logger.info(` Environments: ${summary.environments.join(", ") || "N/A"}`);
|
|
2553
|
+
logger.newline();
|
|
2554
|
+
}
|
|
2555
|
+
if (!options.json) {
|
|
2556
|
+
logger.step("Verifying chain integrity...");
|
|
2557
|
+
}
|
|
2558
|
+
const verification = await verifyChain2(chain, schemasDir);
|
|
2559
|
+
if (options.json) {
|
|
2560
|
+
console.log(JSON.stringify({
|
|
2561
|
+
valid: verification.valid,
|
|
2562
|
+
blockCount: verification.blockCount,
|
|
2563
|
+
verifiedBlocks: verification.verifiedBlocks,
|
|
2564
|
+
corruptedBlocks: verification.corruptedBlocks,
|
|
2565
|
+
tamperedSchemas: verification.tamperedSchemas,
|
|
2566
|
+
deletedLockedSchemas: verification.deletedLockedSchemas,
|
|
2567
|
+
summary
|
|
2568
|
+
}, null, 2));
|
|
2569
|
+
if (!verification.valid) {
|
|
2570
|
+
process.exit(1);
|
|
2571
|
+
}
|
|
2572
|
+
return;
|
|
2573
|
+
}
|
|
2574
|
+
if (verification.valid) {
|
|
2575
|
+
logger.success("\u2713 Chain integrity verified");
|
|
2576
|
+
logger.success(`\u2713 All ${verification.blockCount} block(s) valid`);
|
|
2577
|
+
logger.success("\u2713 No tampered or deleted locked schemas");
|
|
2578
|
+
logger.newline();
|
|
2579
|
+
if (options.showAll) {
|
|
2580
|
+
const lockedSchemas = getLockedSchemas(chain);
|
|
2581
|
+
logger.step("Locked Schemas");
|
|
2582
|
+
for (const [name, info] of lockedSchemas) {
|
|
2583
|
+
logger.info(` \u2022 ${name}`);
|
|
2584
|
+
logger.debug(` Path: ${info.relativePath}`);
|
|
2585
|
+
logger.debug(` Hash: ${info.hash.substring(0, 16)}...`);
|
|
2586
|
+
logger.debug(` Version: ${info.version}`);
|
|
2587
|
+
}
|
|
2588
|
+
}
|
|
2589
|
+
} else {
|
|
2590
|
+
logger.error("\u2717 Chain integrity verification FAILED");
|
|
2591
|
+
logger.newline();
|
|
2592
|
+
let exitCode = 0;
|
|
2593
|
+
if (verification.corruptedBlocks.length > 0) {
|
|
2594
|
+
logger.error("Corrupted Blocks:");
|
|
2595
|
+
for (const block of verification.corruptedBlocks) {
|
|
2596
|
+
logger.error(` \u2717 ${block.version}`);
|
|
2597
|
+
logger.error(` Reason: ${block.reason}`);
|
|
2598
|
+
logger.error(` Expected: ${block.expectedHash.substring(0, 16)}...`);
|
|
2599
|
+
logger.error(` Actual: ${block.actualHash.substring(0, 16)}...`);
|
|
2600
|
+
}
|
|
2601
|
+
logger.newline();
|
|
2602
|
+
exitCode = 1;
|
|
2603
|
+
}
|
|
2604
|
+
if (verification.tamperedSchemas.length > 0) {
|
|
2605
|
+
logger.error("Tampered Schemas (modified since lock):");
|
|
2606
|
+
for (const schema of verification.tamperedSchemas) {
|
|
2607
|
+
logger.error(` \u2717 ${schema.schemaName}`);
|
|
2608
|
+
logger.error(` File: ${schema.filePath}`);
|
|
2609
|
+
logger.error(` Locked in: ${schema.lockedInVersion}`);
|
|
2610
|
+
logger.error(` Locked: ${schema.lockedHash.substring(0, 16)}...`);
|
|
2611
|
+
logger.error(` Current: ${schema.currentHash.substring(0, 16)}...`);
|
|
2612
|
+
}
|
|
2613
|
+
logger.newline();
|
|
2614
|
+
exitCode = 1;
|
|
2615
|
+
}
|
|
2616
|
+
if (verification.deletedLockedSchemas.length > 0) {
|
|
2617
|
+
logger.error("Deleted Locked Schemas:");
|
|
2618
|
+
for (const schema of verification.deletedLockedSchemas) {
|
|
2619
|
+
logger.error(` \u2717 ${schema.schemaName}`);
|
|
2620
|
+
logger.error(` File: ${schema.filePath}`);
|
|
2621
|
+
logger.error(` Locked in: ${schema.lockedInVersion}`);
|
|
2622
|
+
logger.error(` Hash: ${schema.lockedHash.substring(0, 16)}...`);
|
|
2623
|
+
}
|
|
2624
|
+
logger.newline();
|
|
2625
|
+
exitCode = 1;
|
|
2626
|
+
}
|
|
2627
|
+
logger.newline();
|
|
2628
|
+
logger.warn("How to fix:");
|
|
2629
|
+
logger.warn(" 1. Restore deleted files from git or backup");
|
|
2630
|
+
logger.warn(" 2. Revert modified files to their locked state");
|
|
2631
|
+
logger.warn(" 3. Do NOT modify the .omnify.chain file");
|
|
2632
|
+
logger.newline();
|
|
2633
|
+
process.exit(exitCode);
|
|
2634
|
+
}
|
|
2635
|
+
}
|
|
2636
|
+
function registerVerifyCommand(program2) {
|
|
2637
|
+
program2.command("verify").description("Verify version chain integrity and schema states").option("-v, --verbose", "Show detailed output").option("-a, --show-all", "Show all locked schemas").option("--json", "Output result as JSON").action(async (options) => {
|
|
2638
|
+
try {
|
|
2639
|
+
await runVerify(options);
|
|
2640
|
+
} catch (error) {
|
|
2641
|
+
if (error instanceof OmnifyError6) {
|
|
2642
|
+
if (options.json) {
|
|
2643
|
+
console.log(JSON.stringify({
|
|
2644
|
+
valid: false,
|
|
2645
|
+
error: error.message
|
|
2646
|
+
}, null, 2));
|
|
2647
|
+
} else {
|
|
2648
|
+
logger.formatError(error);
|
|
2649
|
+
}
|
|
2650
|
+
process.exit(logger.getExitCode(error));
|
|
2651
|
+
} else if (error instanceof Error) {
|
|
2652
|
+
if (options.json) {
|
|
2653
|
+
console.log(JSON.stringify({
|
|
2654
|
+
valid: false,
|
|
2655
|
+
error: error.message
|
|
2656
|
+
}, null, 2));
|
|
2657
|
+
} else {
|
|
2658
|
+
logger.error(error.message);
|
|
2659
|
+
}
|
|
2660
|
+
process.exit(1);
|
|
2661
|
+
}
|
|
2662
|
+
process.exit(1);
|
|
2663
|
+
}
|
|
2664
|
+
});
|
|
2665
|
+
}
|
|
2666
|
+
|
|
2250
2667
|
// src/cli.ts
|
|
2251
2668
|
var VERSION = "0.0.5";
|
|
2252
2669
|
var program = new Command();
|
|
@@ -2257,8 +2674,10 @@ registerDiffCommand(program);
|
|
|
2257
2674
|
registerGenerateCommand(program);
|
|
2258
2675
|
registerResetCommand(program);
|
|
2259
2676
|
registerCreateProjectCommand(program);
|
|
2677
|
+
registerDeployCommand(program);
|
|
2678
|
+
registerVerifyCommand(program);
|
|
2260
2679
|
process.on("uncaughtException", (error) => {
|
|
2261
|
-
if (error instanceof
|
|
2680
|
+
if (error instanceof OmnifyError7) {
|
|
2262
2681
|
logger.formatError(error);
|
|
2263
2682
|
process.exit(logger.getExitCode(error));
|
|
2264
2683
|
} else {
|
|
@@ -2267,7 +2686,7 @@ process.on("uncaughtException", (error) => {
|
|
|
2267
2686
|
}
|
|
2268
2687
|
});
|
|
2269
2688
|
process.on("unhandledRejection", (reason) => {
|
|
2270
|
-
if (reason instanceof
|
|
2689
|
+
if (reason instanceof OmnifyError7) {
|
|
2271
2690
|
logger.formatError(reason);
|
|
2272
2691
|
process.exit(logger.getExitCode(reason));
|
|
2273
2692
|
} else if (reason instanceof Error) {
|
|
@@ -2280,8 +2699,8 @@ process.on("unhandledRejection", (reason) => {
|
|
|
2280
2699
|
var args = process.argv.slice(2);
|
|
2281
2700
|
var firstArg = args[0];
|
|
2282
2701
|
var hasCommand = firstArg !== void 0 && !firstArg.startsWith("-");
|
|
2283
|
-
var configPath =
|
|
2284
|
-
var hasConfig =
|
|
2702
|
+
var configPath = resolve12(process.cwd(), "omnify.config.ts");
|
|
2703
|
+
var hasConfig = existsSync10(configPath);
|
|
2285
2704
|
if (!hasCommand && !hasConfig) {
|
|
2286
2705
|
runInit({}).catch((error) => {
|
|
2287
2706
|
if (error instanceof Error) {
|