@runa-ai/runa-cli 0.6.0 → 0.7.0
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/{build-BXUJKYHC.js → build-V66FAQXB.js} +152 -163
- package/dist/{cache-H63JKFYH.js → cache-N7WNPEYF.js} +2 -3
- package/dist/check-LOMVIRHX.js +12 -0
- package/dist/{chunk-HPYJPB5Y.js → chunk-2APB25TT.js} +44 -10
- package/dist/chunk-3WDV32GA.js +33 -0
- package/dist/chunk-5FT3F36G.js +59 -0
- package/dist/{chunk-7QV7U6NI.js → chunk-6FAU4IGR.js} +2 -1
- package/dist/{chunk-CE3DEYFT.js → chunk-7B5C6U2K.js} +2 -208
- package/dist/{chunk-GOGRLQNP.js → chunk-AIP6MR42.js} +1 -1
- package/dist/chunk-FHG3ILE4.js +2011 -0
- package/dist/{chunk-22CS6EMA.js → chunk-H2AHNI75.js} +1 -1
- package/dist/{chunk-UU55OH7P.js → chunk-KE6QJBZG.js} +2 -3
- package/dist/{check-6AB5NGWK.js → chunk-QM53IQHM.js} +14 -12
- package/dist/{chunk-RRGQCUKT.js → chunk-WJXC4MVY.js} +30 -3
- package/dist/chunk-XDCHRVE3.js +215 -0
- package/dist/{chunk-P7U52PBY.js → chunk-Z4Z5DNW4.js} +49 -2
- package/dist/{ci-V3PIG2GI.js → ci-ZWRVWNFX.js} +1108 -132
- package/dist/cli/contract-output.d.ts +1 -0
- package/dist/{cli-GFRZCJQR.js → cli-2JNBJUBB.js} +216 -173
- package/dist/commands/build/actors/validate.d.ts +2 -0
- package/dist/commands/check/commands/check.d.ts +8 -3
- package/dist/commands/ci/machine/actors/db/collect-schema-stats.d.ts +9 -6
- package/dist/commands/ci/machine/actors/db/schema-canonical-diff.d.ts +55 -0
- package/dist/commands/ci/machine/actors/db/schema-stats.d.ts +11 -0
- package/dist/commands/ci/machine/actors/db/sync-schema.d.ts +9 -1
- package/dist/commands/ci/machine/formatters/sections/schema-matrix.d.ts +3 -3
- package/dist/commands/ci/machine/types.d.ts +2 -0
- package/dist/commands/ci/utils/execa-helpers.d.ts +1 -0
- package/dist/commands/db/commands/db-sync/error-classifier.d.ts +9 -0
- package/dist/commands/dev/actors/index.d.ts +5 -0
- package/dist/commands/dev/actors/tables-manifest.d.ts +16 -0
- package/dist/commands/dev/contract.d.ts +1 -1
- package/dist/commands/dev/guards.d.ts +24 -0
- package/dist/commands/dev/machine.d.ts +22 -3
- package/dist/commands/dev/types.d.ts +2 -0
- package/dist/commands/doctor.d.ts +9 -0
- package/dist/commands/inject-test-attrs/defaults.d.ts +9 -0
- package/dist/commands/utils/machine-state-logging.d.ts +20 -0
- package/dist/commands/utils/repo-root.d.ts +2 -0
- package/dist/{db-HR7CREX2.js → db-XULCILOU.js} +440 -2216
- package/dist/{dev-A7RW6XQV.js → dev-5YXNPTCJ.js} +168 -49
- package/dist/doctor-MZLOA53G.js +44 -0
- package/dist/{env-B47Z4747.js → env-SS66PZ4B.js} +6 -7
- package/dist/{env-files-K2C7O7L5.js → env-files-2UIUYLLR.js} +2 -2
- package/dist/{error-handler-4EYSDOSE.js → error-handler-HEXBRNVV.js} +2 -2
- package/dist/{hotfix-CULKKMGS.js → hotfix-YA3DGLOM.js} +3 -3
- package/dist/index.js +4 -4
- package/dist/{init-ELK5QCWR.js → init-ZIL6LRFO.js} +5 -6
- package/dist/{inject-test-attrs-Y5UD5P7Q.js → inject-test-attrs-P44BVTQS.js} +5 -18
- package/dist/{link-C43JRZWY.js → link-VSNDVZZD.js} +2 -3
- package/dist/manifest-TMFLESHW.js +19 -0
- package/dist/{risk-detector-BXUY2WKS.js → risk-detector-4U6ZJ2G5.js} +1 -1
- package/dist/{risk-detector-core-O7I7SPR7.js → risk-detector-core-TK4OAI3N.js} +2 -2
- package/dist/{risk-detector-plpgsql-SGMVKYJP.js → risk-detector-plpgsql-HWKS4OLR.js} +37 -7
- package/dist/{status-IJ4ZWHMX.js → status-UTKS63AB.js} +2 -3
- package/dist/{telemetry-FN7V727Y.js → telemetry-P56UBLZ2.js} +2 -3
- package/dist/{template-check-PNG5NQ5H.js → template-check-3P4HZXVY.js} +40 -29
- package/dist/{test-QYXE5UVW.js → test-V4KQL574.js} +34 -10
- package/dist/{test-gen-QPWOIEHU.js → test-gen-FS4CEY3P.js} +2 -3
- package/dist/{upgrade-3SLWVNAC.js → upgrade-NUK3ZBCL.js} +18 -6
- package/dist/{validate-SM4PXPS7.js → validate-CAAW4Y44.js} +2 -3
- package/dist/{vuln-check-TYQNEFS7.js → vuln-check-2W7N5TA2.js} +3 -4
- package/dist/{vuln-checker-2QXGN5YT.js → vuln-checker-IQJ56RUV.js} +413 -140
- package/dist/{watch-UCDVOQAH.js → watch-PNTKZYFB.js} +1 -1
- package/dist/{workflow-ZB5Q2PFY.js → workflow-H75N4BXX.js} +3 -4
- package/package.json +2 -2
- package/dist/chunk-JT5SUTWE.js +0 -9
- package/dist/chunk-M47WJJVS.js +0 -71
- package/dist/manifest-2NOQ2IMK.js +0 -32
- package/dist/{chunk-MNPMZERI.js → chunk-644FVGIQ.js} +1 -1
- package/dist/{chunk-XJBQINSA.js → chunk-SGJG3BKD.js} +1 -1
|
@@ -1,18 +1,19 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { createRequire } from 'module';
|
|
3
|
-
import { normalizeDatabaseUrlForDdl, enhanceConnectionError, parseBoolish,
|
|
4
|
-
import {
|
|
5
|
-
import { ensureRunaTmpDir, runLogged } from './chunk-7QV7U6NI.js';
|
|
3
|
+
import { normalizeDatabaseUrlForDdl, enhanceConnectionError, parseBoolish, detectAppSchemas, formatSchemasForSql } from './chunk-XDCHRVE3.js';
|
|
4
|
+
import { isPathContained } from './chunk-DRSUEMAK.js';
|
|
6
5
|
import './chunk-QDF7QXBL.js';
|
|
7
6
|
import { getSnapshotStateName, isSnapshotComplete } from './chunk-IBVVGH6X.js';
|
|
8
|
-
import { getSafeEnv, getFilteredEnv, redactSecrets } from './chunk-II7VYQEM.js';
|
|
9
7
|
import { writeEnvLocal, startAppBackground, waitForAppReady, executePrSetupBase, createErrorOutput } from './chunk-HD74F6W2.js';
|
|
8
|
+
import { psqlSyncQuery, parsePostgresUrl, buildPsqlArgs, buildPsqlEnv } from './chunk-7B5C6U2K.js';
|
|
9
|
+
import { ensureRunaTmpDir, runLogged } from './chunk-6FAU4IGR.js';
|
|
10
|
+
import { createMachineStateChangeLogger } from './chunk-5FT3F36G.js';
|
|
11
|
+
import { getSafeEnv, getFilteredEnv, redactSecrets } from './chunk-II7VYQEM.js';
|
|
10
12
|
import { init_constants, detectSupabasePorts } from './chunk-VM3IWOT5.js';
|
|
11
|
-
import {
|
|
12
|
-
import
|
|
13
|
-
import './chunk-RRGQCUKT.js';
|
|
14
|
-
import './chunk-JT5SUTWE.js';
|
|
13
|
+
import { emitJsonSuccess } from './chunk-KE6QJBZG.js';
|
|
14
|
+
import './chunk-WJXC4MVY.js';
|
|
15
15
|
import { setOutputFormat } from './chunk-HKUWEGUX.js';
|
|
16
|
+
import { detectEnvironment } from './chunk-JMJP4A47.js';
|
|
16
17
|
import { init_esm_shims, __require } from './chunk-VRXHCR5K.js';
|
|
17
18
|
import { Command } from 'commander';
|
|
18
19
|
import { spawnSync, spawn, execFileSync } from 'child_process';
|
|
@@ -24,9 +25,9 @@ import { existsSync, readFileSync, readdirSync, promises, lstatSync, statSync }
|
|
|
24
25
|
import { resolve4 } from 'dns/promises';
|
|
25
26
|
import net, { isIP } from 'net';
|
|
26
27
|
import { fromPromise, setup, assign, createActor } from 'xstate';
|
|
27
|
-
import
|
|
28
|
+
import { randomUUID, createHash } from 'crypto';
|
|
28
29
|
import { execa } from 'execa';
|
|
29
|
-
import '
|
|
30
|
+
import postgres from 'postgres';
|
|
30
31
|
import { glob } from 'glob';
|
|
31
32
|
|
|
32
33
|
createRequire(import.meta.url);
|
|
@@ -2498,9 +2499,429 @@ var applySeedsActor = fromPromise(
|
|
|
2498
2499
|
// src/commands/ci/machine/actors/db/collect-schema-stats.ts
|
|
2499
2500
|
init_esm_shims();
|
|
2500
2501
|
|
|
2501
|
-
// src/commands/ci/machine/actors/db/schema-
|
|
2502
|
+
// src/commands/ci/machine/actors/db/schema-canonical-diff.ts
|
|
2502
2503
|
init_esm_shims();
|
|
2504
|
+
function createUnavailableCanonicalSnapshot(error) {
|
|
2505
|
+
return {
|
|
2506
|
+
available: false,
|
|
2507
|
+
objects: [],
|
|
2508
|
+
error
|
|
2509
|
+
};
|
|
2510
|
+
}
|
|
2503
2511
|
var EXCLUDED_SCHEMAS = [
|
|
2512
|
+
"pg_catalog",
|
|
2513
|
+
"pg_toast",
|
|
2514
|
+
"information_schema",
|
|
2515
|
+
"auth",
|
|
2516
|
+
"storage",
|
|
2517
|
+
"realtime",
|
|
2518
|
+
"_realtime",
|
|
2519
|
+
"supabase_functions",
|
|
2520
|
+
"supabase_migrations",
|
|
2521
|
+
"vault",
|
|
2522
|
+
"pgsodium",
|
|
2523
|
+
"pgsodium_masks",
|
|
2524
|
+
"graphql",
|
|
2525
|
+
"graphql_public",
|
|
2526
|
+
"extensions",
|
|
2527
|
+
"net",
|
|
2528
|
+
"cron",
|
|
2529
|
+
"tests"
|
|
2530
|
+
];
|
|
2531
|
+
function normalizeWhitespace(value) {
|
|
2532
|
+
return (value ?? "").replace(/\s+/g, " ").trim();
|
|
2533
|
+
}
|
|
2534
|
+
function stableHash(payload) {
|
|
2535
|
+
return createHash("sha256").update(JSON.stringify(payload)).digest("hex").slice(0, 16);
|
|
2536
|
+
}
|
|
2537
|
+
function createCanonicalObject(kind, schema, name, label, payload, parentName) {
|
|
2538
|
+
return {
|
|
2539
|
+
kind,
|
|
2540
|
+
schema,
|
|
2541
|
+
name,
|
|
2542
|
+
key: `${kind}:${schema}:${name}`,
|
|
2543
|
+
label,
|
|
2544
|
+
parentName,
|
|
2545
|
+
signature: stableHash(payload)
|
|
2546
|
+
};
|
|
2547
|
+
}
|
|
2548
|
+
function buildSchemaListSql(schemaNames) {
|
|
2549
|
+
return schemaNames.map((schemaName) => `'${schemaName}'`).join(", ");
|
|
2550
|
+
}
|
|
2551
|
+
async function getUserSchemas(sql) {
|
|
2552
|
+
const excludeList = buildSchemaListSql(EXCLUDED_SCHEMAS);
|
|
2553
|
+
const rows = await sql`
|
|
2554
|
+
SELECT nspname
|
|
2555
|
+
FROM pg_namespace
|
|
2556
|
+
WHERE nspname NOT IN (${sql.unsafe(excludeList)})
|
|
2557
|
+
AND nspname NOT LIKE 'pg_%'
|
|
2558
|
+
ORDER BY nspname
|
|
2559
|
+
`;
|
|
2560
|
+
return rows.map((row) => row.nspname);
|
|
2561
|
+
}
|
|
2562
|
+
async function queryTableColumns(sql, schemaList) {
|
|
2563
|
+
return await sql`
|
|
2564
|
+
SELECT
|
|
2565
|
+
n.nspname AS schema_name,
|
|
2566
|
+
c.relname AS table_name,
|
|
2567
|
+
a.attnum::int AS attnum,
|
|
2568
|
+
a.attname AS column_name,
|
|
2569
|
+
format_type(a.atttypid, a.atttypmod) AS data_type,
|
|
2570
|
+
a.attnotnull AS not_null,
|
|
2571
|
+
pg_get_expr(ad.adbin, ad.adrelid, true) AS default_expr,
|
|
2572
|
+
NULLIF(a.attidentity, '') AS identity_kind,
|
|
2573
|
+
NULLIF(a.attgenerated, '') AS generated_kind
|
|
2574
|
+
FROM pg_class c
|
|
2575
|
+
JOIN pg_namespace n ON n.oid = c.relnamespace
|
|
2576
|
+
JOIN pg_attribute a ON a.attrelid = c.oid
|
|
2577
|
+
LEFT JOIN pg_attrdef ad ON ad.adrelid = c.oid AND ad.adnum = a.attnum
|
|
2578
|
+
WHERE n.nspname IN (${sql.unsafe(schemaList)})
|
|
2579
|
+
AND c.relkind IN ('r', 'p')
|
|
2580
|
+
AND a.attnum > 0
|
|
2581
|
+
AND NOT a.attisdropped
|
|
2582
|
+
ORDER BY n.nspname, c.relname, a.attnum
|
|
2583
|
+
`;
|
|
2584
|
+
}
|
|
2585
|
+
async function queryTableFlags(sql, schemaList) {
|
|
2586
|
+
return await sql`
|
|
2587
|
+
SELECT
|
|
2588
|
+
n.nspname AS schema_name,
|
|
2589
|
+
c.relname AS table_name,
|
|
2590
|
+
c.relrowsecurity AS rls_enabled,
|
|
2591
|
+
c.relforcerowsecurity AS rls_forced
|
|
2592
|
+
FROM pg_class c
|
|
2593
|
+
JOIN pg_namespace n ON n.oid = c.relnamespace
|
|
2594
|
+
WHERE n.nspname IN (${sql.unsafe(schemaList)})
|
|
2595
|
+
AND c.relkind IN ('r', 'p')
|
|
2596
|
+
`;
|
|
2597
|
+
}
|
|
2598
|
+
async function queryTableConstraints(sql, schemaList) {
|
|
2599
|
+
return await sql`
|
|
2600
|
+
SELECT
|
|
2601
|
+
n.nspname AS schema_name,
|
|
2602
|
+
c.relname AS table_name,
|
|
2603
|
+
con.conname AS constraint_name,
|
|
2604
|
+
con.contype AS constraint_type,
|
|
2605
|
+
pg_get_constraintdef(con.oid, true) AS definition
|
|
2606
|
+
FROM pg_constraint con
|
|
2607
|
+
JOIN pg_class c ON c.oid = con.conrelid
|
|
2608
|
+
JOIN pg_namespace n ON n.oid = c.relnamespace
|
|
2609
|
+
WHERE n.nspname IN (${sql.unsafe(schemaList)})
|
|
2610
|
+
AND c.relkind IN ('r', 'p')
|
|
2611
|
+
ORDER BY n.nspname, c.relname, con.conname
|
|
2612
|
+
`;
|
|
2613
|
+
}
|
|
2614
|
+
function buildTableObjects(columns, flags, constraints) {
|
|
2615
|
+
const tables = /* @__PURE__ */ new Map();
|
|
2616
|
+
for (const row of columns) {
|
|
2617
|
+
const key = `${row.schema_name}.${row.table_name}`;
|
|
2618
|
+
const entry = tables.get(key) ?? {
|
|
2619
|
+
schema: row.schema_name,
|
|
2620
|
+
table: row.table_name,
|
|
2621
|
+
columns: [],
|
|
2622
|
+
constraints: [],
|
|
2623
|
+
rlsEnabled: false,
|
|
2624
|
+
rlsForced: false
|
|
2625
|
+
};
|
|
2626
|
+
entry.columns.push({
|
|
2627
|
+
attnum: row.attnum,
|
|
2628
|
+
name: row.column_name,
|
|
2629
|
+
type: normalizeWhitespace(row.data_type),
|
|
2630
|
+
notNull: row.not_null,
|
|
2631
|
+
default: normalizeWhitespace(row.default_expr),
|
|
2632
|
+
identity: row.identity_kind ?? "",
|
|
2633
|
+
generated: row.generated_kind ?? ""
|
|
2634
|
+
});
|
|
2635
|
+
tables.set(key, entry);
|
|
2636
|
+
}
|
|
2637
|
+
for (const row of flags) {
|
|
2638
|
+
const key = `${row.schema_name}.${row.table_name}`;
|
|
2639
|
+
const entry = tables.get(key) ?? {
|
|
2640
|
+
schema: row.schema_name,
|
|
2641
|
+
table: row.table_name,
|
|
2642
|
+
columns: [],
|
|
2643
|
+
constraints: [],
|
|
2644
|
+
rlsEnabled: false,
|
|
2645
|
+
rlsForced: false
|
|
2646
|
+
};
|
|
2647
|
+
entry.rlsEnabled = row.rls_enabled;
|
|
2648
|
+
entry.rlsForced = row.rls_forced;
|
|
2649
|
+
tables.set(key, entry);
|
|
2650
|
+
}
|
|
2651
|
+
for (const row of constraints) {
|
|
2652
|
+
const key = `${row.schema_name}.${row.table_name}`;
|
|
2653
|
+
const entry = tables.get(key) ?? {
|
|
2654
|
+
schema: row.schema_name,
|
|
2655
|
+
table: row.table_name,
|
|
2656
|
+
columns: [],
|
|
2657
|
+
constraints: [],
|
|
2658
|
+
rlsEnabled: false,
|
|
2659
|
+
rlsForced: false
|
|
2660
|
+
};
|
|
2661
|
+
entry.constraints.push({
|
|
2662
|
+
name: row.constraint_name,
|
|
2663
|
+
type: row.constraint_type,
|
|
2664
|
+
definition: normalizeWhitespace(row.definition)
|
|
2665
|
+
});
|
|
2666
|
+
tables.set(key, entry);
|
|
2667
|
+
}
|
|
2668
|
+
return Array.from(tables.values()).map(
|
|
2669
|
+
(table) => createCanonicalObject("table", table.schema, table.table, `${table.schema}.${table.table}`, {
|
|
2670
|
+
columns: table.columns,
|
|
2671
|
+
constraints: table.constraints,
|
|
2672
|
+
rlsEnabled: table.rlsEnabled,
|
|
2673
|
+
rlsForced: table.rlsForced
|
|
2674
|
+
})
|
|
2675
|
+
);
|
|
2676
|
+
}
|
|
2677
|
+
async function queryFunctions(sql, schemaList) {
|
|
2678
|
+
return await sql`
|
|
2679
|
+
SELECT
|
|
2680
|
+
n.nspname AS schema_name,
|
|
2681
|
+
p.proname AS object_name,
|
|
2682
|
+
format('%I.%I(%s)', n.nspname, p.proname, pg_get_function_identity_arguments(p.oid)) AS object_key,
|
|
2683
|
+
format('%I.%I(%s)', n.nspname, p.proname, pg_get_function_identity_arguments(p.oid)) AS object_label,
|
|
2684
|
+
pg_get_functiondef(p.oid) AS definition
|
|
2685
|
+
FROM pg_proc p
|
|
2686
|
+
JOIN pg_namespace n ON p.pronamespace = n.oid
|
|
2687
|
+
WHERE n.nspname IN (${sql.unsafe(schemaList)})
|
|
2688
|
+
AND p.prokind = 'f'
|
|
2689
|
+
AND NOT EXISTS (
|
|
2690
|
+
SELECT 1 FROM pg_depend d
|
|
2691
|
+
WHERE d.objid = p.oid
|
|
2692
|
+
AND d.deptype = 'e'
|
|
2693
|
+
)
|
|
2694
|
+
AND p.proname NOT LIKE 'supabase_%'
|
|
2695
|
+
AND p.proname NOT LIKE 'postgrest_%'
|
|
2696
|
+
AND p.proname NOT LIKE 'pgrst_%'
|
|
2697
|
+
AND p.proname NOT LIKE '_pgrst_%'
|
|
2698
|
+
ORDER BY n.nspname, p.proname
|
|
2699
|
+
`;
|
|
2700
|
+
}
|
|
2701
|
+
async function queryPolicies(sql, schemaList) {
|
|
2702
|
+
return await sql`
|
|
2703
|
+
SELECT
|
|
2704
|
+
schemaname AS schema_name,
|
|
2705
|
+
policyname AS object_name,
|
|
2706
|
+
format('%I.%I.%I', schemaname, tablename, policyname) AS object_key,
|
|
2707
|
+
format('%I.%I policy %I', schemaname, tablename, policyname) AS object_label,
|
|
2708
|
+
tablename AS parent_name,
|
|
2709
|
+
jsonb_build_object(
|
|
2710
|
+
'table', tablename,
|
|
2711
|
+
'permissive', permissive,
|
|
2712
|
+
'roles', COALESCE(roles, ARRAY[]::text[]),
|
|
2713
|
+
'command', cmd,
|
|
2714
|
+
'using', COALESCE(qual, ''),
|
|
2715
|
+
'withCheck', COALESCE(with_check, '')
|
|
2716
|
+
)::text AS definition
|
|
2717
|
+
FROM pg_policies
|
|
2718
|
+
WHERE schemaname IN (${sql.unsafe(schemaList)})
|
|
2719
|
+
ORDER BY schemaname, tablename, policyname
|
|
2720
|
+
`;
|
|
2721
|
+
}
|
|
2722
|
+
async function queryIndexes(sql, schemaList) {
|
|
2723
|
+
return await sql`
|
|
2724
|
+
SELECT
|
|
2725
|
+
n.nspname AS schema_name,
|
|
2726
|
+
i.relname AS object_name,
|
|
2727
|
+
format('%I.%I', n.nspname, i.relname) AS object_key,
|
|
2728
|
+
format('%I.%I on %I', n.nspname, i.relname, t.relname) AS object_label,
|
|
2729
|
+
t.relname AS parent_name,
|
|
2730
|
+
jsonb_build_object(
|
|
2731
|
+
'table', t.relname,
|
|
2732
|
+
'definition',
|
|
2733
|
+
regexp_replace(pg_get_indexdef(i.oid), '^CREATE( UNIQUE)? INDEX [^ ]+ ON ', 'CREATE\\1 INDEX ON '),
|
|
2734
|
+
'predicate',
|
|
2735
|
+
COALESCE(pg_get_expr(idx.indpred, idx.indrelid, true), ''),
|
|
2736
|
+
'unique',
|
|
2737
|
+
idx.indisunique
|
|
2738
|
+
)::text AS definition
|
|
2739
|
+
FROM pg_index idx
|
|
2740
|
+
JOIN pg_class i ON i.oid = idx.indexrelid
|
|
2741
|
+
JOIN pg_class t ON t.oid = idx.indrelid
|
|
2742
|
+
JOIN pg_namespace n ON n.oid = i.relnamespace
|
|
2743
|
+
WHERE n.nspname IN (${sql.unsafe(schemaList)})
|
|
2744
|
+
AND NOT EXISTS (
|
|
2745
|
+
SELECT 1 FROM pg_constraint c
|
|
2746
|
+
WHERE c.conindid = idx.indexrelid
|
|
2747
|
+
)
|
|
2748
|
+
ORDER BY n.nspname, t.relname, i.relname
|
|
2749
|
+
`;
|
|
2750
|
+
}
|
|
2751
|
+
async function queryTriggers(sql, schemaList) {
|
|
2752
|
+
return await sql`
|
|
2753
|
+
SELECT
|
|
2754
|
+
n.nspname AS schema_name,
|
|
2755
|
+
t.tgname AS object_name,
|
|
2756
|
+
format('%I.%I.%I', n.nspname, c.relname, t.tgname) AS object_key,
|
|
2757
|
+
format('%I.%I trigger %I', n.nspname, c.relname, t.tgname) AS object_label,
|
|
2758
|
+
c.relname AS parent_name,
|
|
2759
|
+
pg_get_triggerdef(t.oid, true) AS definition
|
|
2760
|
+
FROM pg_trigger t
|
|
2761
|
+
JOIN pg_class c ON t.tgrelid = c.oid
|
|
2762
|
+
JOIN pg_namespace n ON c.relnamespace = n.oid
|
|
2763
|
+
WHERE n.nspname IN (${sql.unsafe(schemaList)})
|
|
2764
|
+
AND NOT t.tgisinternal
|
|
2765
|
+
ORDER BY n.nspname, c.relname, t.tgname
|
|
2766
|
+
`;
|
|
2767
|
+
}
|
|
2768
|
+
function rowsToObjects(kind, rows) {
|
|
2769
|
+
return rows.map(
|
|
2770
|
+
(row) => createCanonicalObject(
|
|
2771
|
+
kind,
|
|
2772
|
+
row.schema_name,
|
|
2773
|
+
row.object_key,
|
|
2774
|
+
row.object_label,
|
|
2775
|
+
normalizeWhitespace(row.definition),
|
|
2776
|
+
row.parent_name ?? void 0
|
|
2777
|
+
)
|
|
2778
|
+
);
|
|
2779
|
+
}
|
|
2780
|
+
async function getCanonicalSchemaSnapshot(databaseUrl) {
|
|
2781
|
+
let sql = null;
|
|
2782
|
+
try {
|
|
2783
|
+
const isRemoteSupabase = databaseUrl.includes(".supabase.co");
|
|
2784
|
+
sql = postgres(databaseUrl, {
|
|
2785
|
+
max: 1,
|
|
2786
|
+
idle_timeout: 10,
|
|
2787
|
+
connect_timeout: 10,
|
|
2788
|
+
...isRemoteSupabase && { ssl: "require" }
|
|
2789
|
+
});
|
|
2790
|
+
const userSchemas = await getUserSchemas(sql);
|
|
2791
|
+
if (userSchemas.length === 0) {
|
|
2792
|
+
return { available: true, objects: [] };
|
|
2793
|
+
}
|
|
2794
|
+
const schemaList = buildSchemaListSql(userSchemas);
|
|
2795
|
+
const [columns, flags, constraints, functions, policies, indexes, triggers] = await Promise.all(
|
|
2796
|
+
[
|
|
2797
|
+
queryTableColumns(sql, schemaList),
|
|
2798
|
+
queryTableFlags(sql, schemaList),
|
|
2799
|
+
queryTableConstraints(sql, schemaList),
|
|
2800
|
+
queryFunctions(sql, schemaList),
|
|
2801
|
+
queryPolicies(sql, schemaList),
|
|
2802
|
+
queryIndexes(sql, schemaList),
|
|
2803
|
+
queryTriggers(sql, schemaList)
|
|
2804
|
+
]
|
|
2805
|
+
);
|
|
2806
|
+
return {
|
|
2807
|
+
available: true,
|
|
2808
|
+
objects: [
|
|
2809
|
+
...buildTableObjects(columns, flags, constraints),
|
|
2810
|
+
...rowsToObjects("function", functions),
|
|
2811
|
+
...rowsToObjects("policy", policies),
|
|
2812
|
+
...rowsToObjects("index", indexes),
|
|
2813
|
+
...rowsToObjects("trigger", triggers)
|
|
2814
|
+
].sort((a, b) => a.key.localeCompare(b.key))
|
|
2815
|
+
};
|
|
2816
|
+
} catch (error) {
|
|
2817
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
2818
|
+
console.warn(`[schema-canonical-diff] Could not query canonical snapshot: ${message}`);
|
|
2819
|
+
return createUnavailableCanonicalSnapshot(message);
|
|
2820
|
+
} finally {
|
|
2821
|
+
if (sql) {
|
|
2822
|
+
await sql.end();
|
|
2823
|
+
}
|
|
2824
|
+
}
|
|
2825
|
+
}
|
|
2826
|
+
function buildObjectMap(snapshot) {
|
|
2827
|
+
return new Map(snapshot.objects.map((object) => [object.key, object]));
|
|
2828
|
+
}
|
|
2829
|
+
function buildSignatureBuckets(objects) {
|
|
2830
|
+
const buckets = /* @__PURE__ */ new Map();
|
|
2831
|
+
for (const object of objects) {
|
|
2832
|
+
const bucketKey = `${object.kind}:${object.schema}:${object.signature}`;
|
|
2833
|
+
const current = buckets.get(bucketKey) ?? [];
|
|
2834
|
+
current.push(object);
|
|
2835
|
+
buckets.set(bucketKey, current);
|
|
2836
|
+
}
|
|
2837
|
+
return buckets;
|
|
2838
|
+
}
|
|
2839
|
+
function compareCanonicalSnapshots(reference, target) {
|
|
2840
|
+
const missing = [];
|
|
2841
|
+
const extra = [];
|
|
2842
|
+
const changed = [];
|
|
2843
|
+
const renamed = [];
|
|
2844
|
+
const targetByKey = buildObjectMap(target);
|
|
2845
|
+
const targetSignatureBuckets = buildSignatureBuckets(target.objects);
|
|
2846
|
+
const matchedTargetKeys = /* @__PURE__ */ new Set();
|
|
2847
|
+
for (const referenceObject of reference.objects) {
|
|
2848
|
+
const matchedTarget = targetByKey.get(referenceObject.key);
|
|
2849
|
+
if (matchedTarget) {
|
|
2850
|
+
matchedTargetKeys.add(matchedTarget.key);
|
|
2851
|
+
if (matchedTarget.signature !== referenceObject.signature) {
|
|
2852
|
+
changed.push({ reference: referenceObject, target: matchedTarget });
|
|
2853
|
+
}
|
|
2854
|
+
continue;
|
|
2855
|
+
}
|
|
2856
|
+
if (referenceObject.kind === "index") {
|
|
2857
|
+
const renameBucket = targetSignatureBuckets.get(
|
|
2858
|
+
`${referenceObject.kind}:${referenceObject.schema}:${referenceObject.signature}`
|
|
2859
|
+
) ?? [];
|
|
2860
|
+
const renamedTarget = renameBucket.find((candidate) => !matchedTargetKeys.has(candidate.key));
|
|
2861
|
+
if (renamedTarget) {
|
|
2862
|
+
matchedTargetKeys.add(renamedTarget.key);
|
|
2863
|
+
renamed.push({ reference: referenceObject, target: renamedTarget });
|
|
2864
|
+
continue;
|
|
2865
|
+
}
|
|
2866
|
+
}
|
|
2867
|
+
missing.push(referenceObject);
|
|
2868
|
+
}
|
|
2869
|
+
for (const targetObject of target.objects) {
|
|
2870
|
+
if (!matchedTargetKeys.has(targetObject.key) && !reference.objects.some((candidate) => candidate.key === targetObject.key)) {
|
|
2871
|
+
extra.push(targetObject);
|
|
2872
|
+
}
|
|
2873
|
+
}
|
|
2874
|
+
return { missing, extra, changed, renamed };
|
|
2875
|
+
}
|
|
2876
|
+
function addSummaryKind(summary, kind) {
|
|
2877
|
+
if (!summary.changedKinds.includes(kind)) {
|
|
2878
|
+
summary.changedKinds.push(kind);
|
|
2879
|
+
summary.changedKinds.sort();
|
|
2880
|
+
}
|
|
2881
|
+
summary.hasChanges = true;
|
|
2882
|
+
}
|
|
2883
|
+
function ensureSchemaSummary(summaries, schemaName) {
|
|
2884
|
+
const existing = summaries[schemaName];
|
|
2885
|
+
if (existing) return existing;
|
|
2886
|
+
const created = {
|
|
2887
|
+
hasChanges: false,
|
|
2888
|
+
changedKinds: [],
|
|
2889
|
+
counts: { missing: 0, extra: 0, changed: 0, renamed: 0 }
|
|
2890
|
+
};
|
|
2891
|
+
summaries[schemaName] = created;
|
|
2892
|
+
return created;
|
|
2893
|
+
}
|
|
2894
|
+
function summarizeCanonicalDiffBySchema(diff) {
|
|
2895
|
+
const summaries = {};
|
|
2896
|
+
for (const object of diff.missing) {
|
|
2897
|
+
const summary = ensureSchemaSummary(summaries, object.schema);
|
|
2898
|
+
summary.counts.missing += 1;
|
|
2899
|
+
addSummaryKind(summary, object.kind);
|
|
2900
|
+
}
|
|
2901
|
+
for (const object of diff.extra) {
|
|
2902
|
+
const summary = ensureSchemaSummary(summaries, object.schema);
|
|
2903
|
+
summary.counts.extra += 1;
|
|
2904
|
+
addSummaryKind(summary, object.kind);
|
|
2905
|
+
}
|
|
2906
|
+
for (const pair of diff.changed) {
|
|
2907
|
+
const summary = ensureSchemaSummary(summaries, pair.reference.schema);
|
|
2908
|
+
summary.counts.changed += 1;
|
|
2909
|
+
addSummaryKind(summary, pair.reference.kind);
|
|
2910
|
+
}
|
|
2911
|
+
for (const pair of diff.renamed) {
|
|
2912
|
+
const summary = ensureSchemaSummary(summaries, pair.reference.schema);
|
|
2913
|
+
summary.counts.renamed += 1;
|
|
2914
|
+
addSummaryKind(summary, pair.reference.kind);
|
|
2915
|
+
}
|
|
2916
|
+
return summaries;
|
|
2917
|
+
}
|
|
2918
|
+
function hasCanonicalChanges(diff) {
|
|
2919
|
+
return diff.missing.length > 0 || diff.extra.length > 0 || diff.changed.length > 0 || diff.renamed.length > 0;
|
|
2920
|
+
}
|
|
2921
|
+
|
|
2922
|
+
// src/commands/ci/machine/actors/db/schema-stats.ts
|
|
2923
|
+
init_esm_shims();
|
|
2924
|
+
var EXCLUDED_SCHEMAS2 = [
|
|
2504
2925
|
// PostgreSQL system schemas
|
|
2505
2926
|
"pg_catalog",
|
|
2506
2927
|
"pg_toast",
|
|
@@ -2604,15 +3025,18 @@ async function getDetailedIndexList(sql, schemaList) {
|
|
|
2604
3025
|
t.relname as table,
|
|
2605
3026
|
idx.indisunique as is_unique,
|
|
2606
3027
|
idx.indpred IS NOT NULL as is_partial,
|
|
3028
|
+
am.amname as access_method,
|
|
2607
3029
|
CASE
|
|
2608
3030
|
WHEN idx.indpred IS NOT NULL
|
|
2609
3031
|
THEN pg_get_expr(idx.indpred, idx.indrelid, true)
|
|
2610
3032
|
ELSE NULL
|
|
2611
|
-
END as where_clause
|
|
3033
|
+
END as where_clause,
|
|
3034
|
+
pg_get_indexdef(i.oid) as definition
|
|
2612
3035
|
FROM pg_index idx
|
|
2613
3036
|
JOIN pg_class i ON i.oid = idx.indexrelid
|
|
2614
3037
|
JOIN pg_class t ON t.oid = idx.indrelid
|
|
2615
3038
|
JOIN pg_namespace n ON n.oid = i.relnamespace
|
|
3039
|
+
JOIN pg_am am ON am.oid = i.relam
|
|
2616
3040
|
WHERE n.nspname IN (${sql.unsafe(schemaList)})
|
|
2617
3041
|
AND NOT EXISTS (
|
|
2618
3042
|
SELECT 1 FROM pg_constraint c
|
|
@@ -2626,11 +3050,13 @@ async function getDetailedIndexList(sql, schemaList) {
|
|
|
2626
3050
|
table: row.table,
|
|
2627
3051
|
isUnique: row.is_unique,
|
|
2628
3052
|
isPartial: row.is_partial,
|
|
2629
|
-
whereClause: row.where_clause || void 0
|
|
3053
|
+
whereClause: row.where_clause || void 0,
|
|
3054
|
+
accessMethod: row.access_method || void 0,
|
|
3055
|
+
definition: normalizeIndexDefinition(row.definition || void 0)
|
|
2630
3056
|
}));
|
|
2631
3057
|
}
|
|
2632
|
-
async function
|
|
2633
|
-
const excludeList =
|
|
3058
|
+
async function getUserSchemas2(sql) {
|
|
3059
|
+
const excludeList = EXCLUDED_SCHEMAS2.map((s) => `'${s}'`).join(", ");
|
|
2634
3060
|
const result = await sql`
|
|
2635
3061
|
SELECT nspname
|
|
2636
3062
|
FROM pg_namespace
|
|
@@ -2653,7 +3079,7 @@ async function getDbSchemaStats(databaseUrl) {
|
|
|
2653
3079
|
// Supabase production requires SSL; local Docker does not
|
|
2654
3080
|
...isRemoteSupabase && { ssl: "require" }
|
|
2655
3081
|
});
|
|
2656
|
-
const userSchemas = await
|
|
3082
|
+
const userSchemas = await getUserSchemas2(sql);
|
|
2657
3083
|
if (userSchemas.length === 0) {
|
|
2658
3084
|
return { schemas, total };
|
|
2659
3085
|
}
|
|
@@ -2783,12 +3209,30 @@ function collectExpectedDriftReasons(schemaName, fieldDiffs, expectedDrift) {
|
|
|
2783
3209
|
function compareIndexLists(reference, target) {
|
|
2784
3210
|
const refIndexes = reference.indexList || [];
|
|
2785
3211
|
const targetIndexes = target.indexList || [];
|
|
2786
|
-
const
|
|
2787
|
-
|
|
2788
|
-
|
|
2789
|
-
|
|
3212
|
+
const buildIndexKey = (index) => [
|
|
3213
|
+
index.schema,
|
|
3214
|
+
index.table,
|
|
3215
|
+
index.isUnique ? "unique" : "nonunique",
|
|
3216
|
+
index.isPartial ? "partial" : "full",
|
|
3217
|
+
normalizeDiffValue(index.whereClause),
|
|
3218
|
+
normalizeDiffValue(index.accessMethod),
|
|
3219
|
+
normalizeIndexDefinition(index.definition)
|
|
3220
|
+
].join(":");
|
|
3221
|
+
const refKeys = new Set(refIndexes.map(buildIndexKey));
|
|
3222
|
+
const targetKeys = new Set(targetIndexes.map(buildIndexKey));
|
|
3223
|
+
const missing = refIndexes.filter((index) => !targetKeys.has(buildIndexKey(index)));
|
|
3224
|
+
const extra = targetIndexes.filter((index) => !refKeys.has(buildIndexKey(index)));
|
|
2790
3225
|
return { missing, extra };
|
|
2791
3226
|
}
|
|
3227
|
+
function normalizeDiffValue(value) {
|
|
3228
|
+
return (value ?? "").replace(/\s+/g, " ").trim();
|
|
3229
|
+
}
|
|
3230
|
+
function normalizeIndexDefinition(value) {
|
|
3231
|
+
return normalizeDiffValue(value).replace(
|
|
3232
|
+
/^create\s+(unique\s+)?index\s+\S+\s+on\s+/i,
|
|
3233
|
+
(_match, unique = "") => `create ${unique || ""}index on `
|
|
3234
|
+
);
|
|
3235
|
+
}
|
|
2792
3236
|
function formatIndexInfo(index) {
|
|
2793
3237
|
const qualifiedName = `${index.schema}.${index.name}`;
|
|
2794
3238
|
const tags = [];
|
|
@@ -2805,7 +3249,16 @@ function hasIndexDiff(diff) {
|
|
|
2805
3249
|
function emptyStats() {
|
|
2806
3250
|
return {
|
|
2807
3251
|
schemas: {},
|
|
2808
|
-
total: { tables: 0, functions: 0, policies: 0, indexes: 0, triggers: 0 }
|
|
3252
|
+
total: { tables: 0, functions: 0, policies: 0, indexes: 0, triggers: 0 },
|
|
3253
|
+
available: true
|
|
3254
|
+
};
|
|
3255
|
+
}
|
|
3256
|
+
function unavailableStats(error) {
|
|
3257
|
+
return {
|
|
3258
|
+
...emptyStats(),
|
|
3259
|
+
available: false,
|
|
3260
|
+
error,
|
|
3261
|
+
canonicalSnapshot: createUnavailableCanonicalSnapshot(error)
|
|
2809
3262
|
};
|
|
2810
3263
|
}
|
|
2811
3264
|
function logSchemaStats(label, stats, includeSchemas = false) {
|
|
@@ -2817,42 +3270,117 @@ function logSchemaStats(label, stats, includeSchemas = false) {
|
|
|
2817
3270
|
const schemas = Object.keys(stats.schemas);
|
|
2818
3271
|
console.log(`${base} (schemas: ${schemas.join(", ")})`);
|
|
2819
3272
|
}
|
|
2820
|
-
function
|
|
3273
|
+
async function collectEnvironmentStats(dbUrl, label, includeSchemas = false) {
|
|
2821
3274
|
if (!dbUrl) {
|
|
2822
|
-
return
|
|
3275
|
+
return null;
|
|
2823
3276
|
}
|
|
2824
|
-
|
|
2825
|
-
|
|
2826
|
-
|
|
2827
|
-
|
|
3277
|
+
const stats = await getDbSchemaStats(dbUrl);
|
|
3278
|
+
stats.canonicalSnapshot = await getCanonicalSchemaSnapshot(dbUrl);
|
|
3279
|
+
stats.available = true;
|
|
3280
|
+
logSchemaStats(label, stats, includeSchemas);
|
|
3281
|
+
return stats;
|
|
2828
3282
|
}
|
|
2829
3283
|
function resolveSettledStats(result, failureLabel) {
|
|
2830
3284
|
if (result.status === "fulfilled" && result.value) {
|
|
2831
3285
|
return result.value;
|
|
2832
3286
|
}
|
|
3287
|
+
const reason = result.status === "rejected" ? String(result.reason) : `${failureLabel} database URL is not available for schema comparison`;
|
|
2833
3288
|
if (result.status === "rejected") {
|
|
2834
3289
|
console.warn(`[schema-stats] Failed to query ${failureLabel} database: ${result.reason}`);
|
|
2835
3290
|
}
|
|
2836
|
-
return
|
|
3291
|
+
return unavailableStats(reason);
|
|
2837
3292
|
}
|
|
2838
3293
|
function resolveSettledProductionStats(result) {
|
|
2839
3294
|
if (result.status === "fulfilled") {
|
|
2840
3295
|
return result.value;
|
|
2841
3296
|
}
|
|
2842
3297
|
console.warn(`[schema-stats] Failed to query production database: ${result.reason}`);
|
|
2843
|
-
return
|
|
3298
|
+
return unavailableStats(String(result.reason));
|
|
3299
|
+
}
|
|
3300
|
+
function buildAdminDsn(sourceDbUrl) {
|
|
3301
|
+
const conn = parsePostgresUrl(sourceDbUrl);
|
|
3302
|
+
const password = conn.password ? `:${encodeURIComponent(conn.password)}` : "";
|
|
3303
|
+
return `postgresql://${conn.user}${password}@${conn.host}:${conn.port}/postgres`;
|
|
3304
|
+
}
|
|
3305
|
+
function buildShadowDsn(sourceDbUrl, dbName) {
|
|
3306
|
+
const conn = parsePostgresUrl(sourceDbUrl);
|
|
3307
|
+
const password = conn.password ? `:${encodeURIComponent(conn.password)}` : "";
|
|
3308
|
+
return `postgresql://${conn.user}${password}@${conn.host}:${conn.port}/${dbName}`;
|
|
3309
|
+
}
|
|
3310
|
+
function createReferenceDb(sourceDbUrl) {
|
|
3311
|
+
const dbName = `ci_schema_ref_${randomUUID().replace(/-/g, "").slice(0, 12)}`;
|
|
3312
|
+
const adminDsn = buildAdminDsn(sourceDbUrl);
|
|
3313
|
+
const createResult = psqlSyncQuery({
|
|
3314
|
+
databaseUrl: adminDsn,
|
|
3315
|
+
sql: `CREATE DATABASE ${dbName}`,
|
|
3316
|
+
timeout: 3e4
|
|
3317
|
+
});
|
|
3318
|
+
if (createResult.status !== 0) {
|
|
3319
|
+
throw new Error(
|
|
3320
|
+
`Failed to create reference database: ${createResult.stderr || createResult.stdout}`
|
|
3321
|
+
);
|
|
3322
|
+
}
|
|
3323
|
+
return { dsn: buildShadowDsn(sourceDbUrl, dbName), dbName };
|
|
3324
|
+
}
|
|
3325
|
+
function dropReferenceDb(sourceDbUrl, dbName) {
|
|
3326
|
+
const adminDsn = buildAdminDsn(sourceDbUrl);
|
|
3327
|
+
psqlSyncQuery({
|
|
3328
|
+
databaseUrl: adminDsn,
|
|
3329
|
+
sql: `
|
|
3330
|
+
SELECT pg_terminate_backend(pid)
|
|
3331
|
+
FROM pg_stat_activity
|
|
3332
|
+
WHERE datname = '${dbName}' AND pid <> pg_backend_pid()
|
|
3333
|
+
`,
|
|
3334
|
+
timeout: 1e4
|
|
3335
|
+
});
|
|
3336
|
+
psqlSyncQuery({
|
|
3337
|
+
databaseUrl: adminDsn,
|
|
3338
|
+
sql: `DROP DATABASE IF EXISTS ${dbName}`,
|
|
3339
|
+
timeout: 3e4
|
|
3340
|
+
});
|
|
3341
|
+
}
|
|
3342
|
+
async function buildReferenceStats(repoRoot, tmpDir, sourceDbUrl) {
|
|
3343
|
+
if (!sourceDbUrl) return null;
|
|
3344
|
+
const referenceDb = createReferenceDb(sourceDbUrl);
|
|
3345
|
+
const logFile = path4.join(tmpDir, "ci-reference-db-apply.log");
|
|
3346
|
+
try {
|
|
3347
|
+
const result = await execa(
|
|
3348
|
+
"pnpm",
|
|
3349
|
+
["exec", "runa", "db", "apply", "preview", "--auto-approve", "--no-seed", "--verbose"],
|
|
3350
|
+
{
|
|
3351
|
+
cwd: repoRoot,
|
|
3352
|
+
env: {
|
|
3353
|
+
...process.env,
|
|
3354
|
+
DATABASE_URL_ADMIN: referenceDb.dsn,
|
|
3355
|
+
DATABASE_URL: referenceDb.dsn
|
|
3356
|
+
},
|
|
3357
|
+
reject: false
|
|
3358
|
+
}
|
|
3359
|
+
);
|
|
3360
|
+
const fullOutput = `${result.stdout || ""}
|
|
3361
|
+
${result.stderr || ""}`;
|
|
3362
|
+
if (result.exitCode !== 0) {
|
|
3363
|
+
throw new Error(`Failed to build reference DB: ${fullOutput.substring(0, 1e3)}`);
|
|
3364
|
+
}
|
|
3365
|
+
await (await import('fs/promises')).writeFile(logFile, fullOutput);
|
|
3366
|
+
return collectEnvironmentStats(referenceDb.dsn, "Reference", true);
|
|
3367
|
+
} finally {
|
|
3368
|
+
dropReferenceDb(sourceDbUrl, referenceDb.dbName);
|
|
3369
|
+
}
|
|
2844
3370
|
}
|
|
2845
3371
|
var collectSchemaStatsActor = fromPromise(
|
|
2846
3372
|
async ({ input }) => {
|
|
2847
|
-
const {
|
|
2848
|
-
console.log(
|
|
3373
|
+
const { repoRoot, referenceDbUrl, ciDbUrl, queryProduction, tmpDir } = input;
|
|
3374
|
+
console.log(
|
|
3375
|
+
"[schema-stats] Collecting schema statistics (Reference/CI/Production, parallel)..."
|
|
3376
|
+
);
|
|
2849
3377
|
const productionUrl = process.env.GH_DATABASE_URL_ADMIN || process.env.GH_DATABASE_URL;
|
|
2850
|
-
const [
|
|
2851
|
-
|
|
2852
|
-
|
|
2853
|
-
|
|
3378
|
+
const [referenceResult, ciResult, productionResult] = await Promise.allSettled([
|
|
3379
|
+
buildReferenceStats(repoRoot, tmpDir, referenceDbUrl),
|
|
3380
|
+
collectEnvironmentStats(ciDbUrl, "CI"),
|
|
3381
|
+
collectEnvironmentStats(queryProduction ? productionUrl ?? null : null, "Production")
|
|
2854
3382
|
]);
|
|
2855
|
-
const local = resolveSettledStats(
|
|
3383
|
+
const local = resolveSettledStats(referenceResult, "reference");
|
|
2856
3384
|
const ci = resolveSettledStats(ciResult, "CI");
|
|
2857
3385
|
const production = resolveSettledProductionStats(productionResult);
|
|
2858
3386
|
return {
|
|
@@ -3611,6 +4139,25 @@ function parseSchemaChangeStats(sqlInput, logPath, repoRoot) {
|
|
|
3611
4139
|
stats.hazards = parseHazards(sql, idempotentRoles);
|
|
3612
4140
|
return stats;
|
|
3613
4141
|
}
|
|
4142
|
+
function isCheckSummaryOutput(output) {
|
|
4143
|
+
return output.includes("check complete") || output.includes("Check Summary");
|
|
4144
|
+
}
|
|
4145
|
+
function isSchemaOutOfSyncOutput(output) {
|
|
4146
|
+
const lowerOutput = output.toLowerCase();
|
|
4147
|
+
return lowerOutput.includes("database schema is out of sync") || lowerOutput.includes("db_schema_out_of_sync");
|
|
4148
|
+
}
|
|
4149
|
+
function countResidualStatements(stats) {
|
|
4150
|
+
return stats.creates.tables + stats.creates.indexes + stats.creates.policies + stats.creates.functions + stats.creates.other + stats.alters + stats.drops;
|
|
4151
|
+
}
|
|
4152
|
+
function analyzePostCheckResult(params) {
|
|
4153
|
+
const hasSchemaOutOfSync = isSchemaOutOfSyncOutput(params.output);
|
|
4154
|
+
const hasDrift = hasSchemaOutOfSync || countResidualStatements(params.residualStats) > 0;
|
|
4155
|
+
const checkSucceeded = params.exitCode === 0 || isCheckSummaryOutput(params.output) || hasSchemaOutOfSync;
|
|
4156
|
+
return {
|
|
4157
|
+
hasDrift: hasDrift || params.residualStats.hazards.length > 0,
|
|
4158
|
+
commandFailed: !checkSucceeded
|
|
4159
|
+
};
|
|
4160
|
+
}
|
|
3614
4161
|
var syncSchemaActor = fromPromise(
|
|
3615
4162
|
async ({ input }) => {
|
|
3616
4163
|
const { repoRoot, tmpDir, databaseUrl, mode, skipCodegen } = input;
|
|
@@ -3649,7 +4196,18 @@ var syncSchemaActor = fromPromise(
|
|
|
3649
4196
|
// Always verbose for full traceability
|
|
3650
4197
|
...skipCodegen ? ["--skip-codegen"] : []
|
|
3651
4198
|
];
|
|
4199
|
+
const checkArgs = useDbApply ? ["exec", "runa", "db", "apply", envArg, "--check", "--no-seed", "--verbose"] : [
|
|
4200
|
+
"exec",
|
|
4201
|
+
"runa",
|
|
4202
|
+
"db",
|
|
4203
|
+
"sync",
|
|
4204
|
+
envArg,
|
|
4205
|
+
"--check",
|
|
4206
|
+
"--verbose",
|
|
4207
|
+
...skipCodegen ? ["--skip-codegen"] : []
|
|
4208
|
+
];
|
|
3652
4209
|
const logFile = path4.join(tmpDir, `ci-db-${useDbApply ? "apply" : "sync"}-${envArg}.log`);
|
|
4210
|
+
const afterCheckLogFile = path4.join(tmpDir, `ci-db-check-${envArg}.log`);
|
|
3653
4211
|
await runLogged({
|
|
3654
4212
|
cwd: repoRoot,
|
|
3655
4213
|
env: baseEnv,
|
|
@@ -3658,23 +4216,54 @@ var syncSchemaActor = fromPromise(
|
|
|
3658
4216
|
args: syncArgs,
|
|
3659
4217
|
logFile
|
|
3660
4218
|
});
|
|
4219
|
+
const postCheckResult = await runLogged({
|
|
4220
|
+
cwd: repoRoot,
|
|
4221
|
+
env: baseEnv,
|
|
4222
|
+
label: `db ${useDbApply ? "apply" : "sync"} check (${envArg})`,
|
|
4223
|
+
command: "pnpm",
|
|
4224
|
+
args: checkArgs,
|
|
4225
|
+
logFile: afterCheckLogFile,
|
|
4226
|
+
reject: false
|
|
4227
|
+
});
|
|
3661
4228
|
let logContent = "";
|
|
4229
|
+
let afterCheckLogContent = "";
|
|
3662
4230
|
try {
|
|
3663
4231
|
logContent = readFileSync(logFile, "utf-8");
|
|
3664
4232
|
} catch {
|
|
3665
4233
|
}
|
|
4234
|
+
try {
|
|
4235
|
+
afterCheckLogContent = readFileSync(afterCheckLogFile, "utf-8");
|
|
4236
|
+
} catch {
|
|
4237
|
+
}
|
|
3666
4238
|
const changeStats = parseSchemaChangeStats(logContent, null, repoRoot);
|
|
4239
|
+
const residualStats = parseSchemaChangeStats(afterCheckLogContent, null, repoRoot);
|
|
4240
|
+
const fullPostCheckOutput = `${postCheckResult.stdout || ""}
|
|
4241
|
+
${postCheckResult.stderr || ""}`;
|
|
4242
|
+
const postCheckAnalysis = analyzePostCheckResult({
|
|
4243
|
+
exitCode: postCheckResult.exitCode ?? 1,
|
|
4244
|
+
output: fullPostCheckOutput,
|
|
4245
|
+
residualStats
|
|
4246
|
+
});
|
|
4247
|
+
if (postCheckAnalysis.commandFailed) {
|
|
4248
|
+
throw new Error(
|
|
4249
|
+
`Schema post-check failed (${postCheckResult.exitCode}): ${fullPostCheckOutput.substring(
|
|
4250
|
+
0,
|
|
4251
|
+
1e3
|
|
4252
|
+
)}`
|
|
4253
|
+
);
|
|
4254
|
+
}
|
|
3667
4255
|
const schemaDrift = {
|
|
3668
4256
|
beforeSql: logContent.substring(0, 5e3),
|
|
3669
4257
|
// Truncate for comment
|
|
3670
|
-
afterSql:
|
|
3671
|
-
|
|
4258
|
+
afterSql: postCheckAnalysis.hasDrift ? afterCheckLogContent.substring(0, 5e3) : "",
|
|
4259
|
+
checkExecuted: true,
|
|
4260
|
+
hasDrift: postCheckAnalysis.hasDrift,
|
|
3672
4261
|
changeStats,
|
|
3673
4262
|
gitDiff,
|
|
3674
4263
|
logs: {
|
|
3675
4264
|
beforeCheckLogPath: logFile,
|
|
3676
4265
|
applyLogPath: logFile,
|
|
3677
|
-
afterCheckLogPath:
|
|
4266
|
+
afterCheckLogPath: afterCheckLogFile
|
|
3678
4267
|
}
|
|
3679
4268
|
};
|
|
3680
4269
|
return { applied: true, schemaDrift };
|
|
@@ -3682,6 +4271,7 @@ var syncSchemaActor = fromPromise(
|
|
|
3682
4271
|
const schemaDrift = {
|
|
3683
4272
|
beforeSql: null,
|
|
3684
4273
|
afterSql: null,
|
|
4274
|
+
checkExecuted: false,
|
|
3685
4275
|
hasDrift: false,
|
|
3686
4276
|
changeStats: null,
|
|
3687
4277
|
gitDiff,
|
|
@@ -4345,6 +4935,213 @@ function safeReadFile(filePath) {
|
|
|
4345
4935
|
return null;
|
|
4346
4936
|
}
|
|
4347
4937
|
}
|
|
4938
|
+
function buildSanitizeState() {
|
|
4939
|
+
return {
|
|
4940
|
+
inSingle: false,
|
|
4941
|
+
inDouble: false,
|
|
4942
|
+
inTemplate: false,
|
|
4943
|
+
inLineComment: false,
|
|
4944
|
+
inBlockComment: false
|
|
4945
|
+
};
|
|
4946
|
+
}
|
|
4947
|
+
function applySanitizeStep(state, stateUpdate) {
|
|
4948
|
+
return { ...state, ...stateUpdate };
|
|
4949
|
+
}
|
|
4950
|
+
function processLineCommentState(char) {
|
|
4951
|
+
return {
|
|
4952
|
+
output: char === "\n" ? "\n" : " ",
|
|
4953
|
+
nextIndex: 1,
|
|
4954
|
+
state: {
|
|
4955
|
+
inLineComment: char !== "\n"
|
|
4956
|
+
}
|
|
4957
|
+
};
|
|
4958
|
+
}
|
|
4959
|
+
function processBlockCommentState(char, next) {
|
|
4960
|
+
if (char === "*" && next === "/") {
|
|
4961
|
+
return {
|
|
4962
|
+
output: " ",
|
|
4963
|
+
nextIndex: 2,
|
|
4964
|
+
state: { inBlockComment: false }
|
|
4965
|
+
};
|
|
4966
|
+
}
|
|
4967
|
+
return {
|
|
4968
|
+
output: char === "\n" ? "\n" : " ",
|
|
4969
|
+
nextIndex: 1,
|
|
4970
|
+
state: {}
|
|
4971
|
+
};
|
|
4972
|
+
}
|
|
4973
|
+
function processSingleQuoteState(content, i, stripStrings) {
|
|
4974
|
+
const char = content[i] ?? "";
|
|
4975
|
+
if (char === "\\") {
|
|
4976
|
+
return {
|
|
4977
|
+
output: stripStrings ? " " : `${char}${content[i + 1] ?? ""}`,
|
|
4978
|
+
nextIndex: 2,
|
|
4979
|
+
state: {}
|
|
4980
|
+
};
|
|
4981
|
+
}
|
|
4982
|
+
if (char === "'") {
|
|
4983
|
+
return {
|
|
4984
|
+
output: char,
|
|
4985
|
+
nextIndex: 1,
|
|
4986
|
+
state: { inSingle: false }
|
|
4987
|
+
};
|
|
4988
|
+
}
|
|
4989
|
+
return {
|
|
4990
|
+
output: stripStrings ? " " : char,
|
|
4991
|
+
nextIndex: 1,
|
|
4992
|
+
state: {}
|
|
4993
|
+
};
|
|
4994
|
+
}
|
|
4995
|
+
function processDoubleQuoteState(content, i, stripStrings) {
|
|
4996
|
+
const char = content[i] ?? "";
|
|
4997
|
+
if (char === "\\") {
|
|
4998
|
+
return {
|
|
4999
|
+
output: stripStrings ? " " : `${char}${content[i + 1] ?? ""}`,
|
|
5000
|
+
nextIndex: 2,
|
|
5001
|
+
state: {}
|
|
5002
|
+
};
|
|
5003
|
+
}
|
|
5004
|
+
if (char === '"') {
|
|
5005
|
+
return {
|
|
5006
|
+
output: char,
|
|
5007
|
+
nextIndex: 1,
|
|
5008
|
+
state: { inDouble: false }
|
|
5009
|
+
};
|
|
5010
|
+
}
|
|
5011
|
+
return {
|
|
5012
|
+
output: stripStrings ? " " : char,
|
|
5013
|
+
nextIndex: 1,
|
|
5014
|
+
state: {}
|
|
5015
|
+
};
|
|
5016
|
+
}
|
|
5017
|
+
function processTemplateQuoteState(content, i, stripStrings) {
|
|
5018
|
+
const char = content[i] ?? "";
|
|
5019
|
+
if (char === "\\") {
|
|
5020
|
+
return {
|
|
5021
|
+
output: stripStrings ? " " : `${char}${content[i + 1] ?? ""}`,
|
|
5022
|
+
nextIndex: 2,
|
|
5023
|
+
state: {}
|
|
5024
|
+
};
|
|
5025
|
+
}
|
|
5026
|
+
if (char === "`") {
|
|
5027
|
+
return {
|
|
5028
|
+
output: "`",
|
|
5029
|
+
nextIndex: 1,
|
|
5030
|
+
state: { inTemplate: false }
|
|
5031
|
+
};
|
|
5032
|
+
}
|
|
5033
|
+
return {
|
|
5034
|
+
output: stripStrings ? char === "\n" ? "\n" : " " : char,
|
|
5035
|
+
nextIndex: 1,
|
|
5036
|
+
state: {}
|
|
5037
|
+
};
|
|
5038
|
+
}
|
|
5039
|
+
function processQuoteState(content, i, stripStrings, quote) {
|
|
5040
|
+
if (quote === "'") return processSingleQuoteState(content, i, stripStrings);
|
|
5041
|
+
if (quote === '"') return processDoubleQuoteState(content, i, stripStrings);
|
|
5042
|
+
return processTemplateQuoteState(content, i, stripStrings);
|
|
5043
|
+
}
|
|
5044
|
+
function processDefaultState(char, next) {
|
|
5045
|
+
if (char === "/" && next === "/") {
|
|
5046
|
+
return {
|
|
5047
|
+
output: " ",
|
|
5048
|
+
nextIndex: 2,
|
|
5049
|
+
state: { inLineComment: true }
|
|
5050
|
+
};
|
|
5051
|
+
}
|
|
5052
|
+
if (char === "/" && next === "*") {
|
|
5053
|
+
return {
|
|
5054
|
+
output: " ",
|
|
5055
|
+
nextIndex: 2,
|
|
5056
|
+
state: { inBlockComment: true }
|
|
5057
|
+
};
|
|
5058
|
+
}
|
|
5059
|
+
if (char === "'") {
|
|
5060
|
+
return {
|
|
5061
|
+
output: char,
|
|
5062
|
+
nextIndex: 1,
|
|
5063
|
+
state: { inSingle: true }
|
|
5064
|
+
};
|
|
5065
|
+
}
|
|
5066
|
+
if (char === '"') {
|
|
5067
|
+
return {
|
|
5068
|
+
output: char,
|
|
5069
|
+
nextIndex: 1,
|
|
5070
|
+
state: { inDouble: true }
|
|
5071
|
+
};
|
|
5072
|
+
}
|
|
5073
|
+
if (char === "`") {
|
|
5074
|
+
return {
|
|
5075
|
+
output: char,
|
|
5076
|
+
nextIndex: 1,
|
|
5077
|
+
state: { inTemplate: true }
|
|
5078
|
+
};
|
|
5079
|
+
}
|
|
5080
|
+
return { output: char, nextIndex: 1, state: {} };
|
|
5081
|
+
}
|
|
5082
|
+
function runSanitizeStep(step, state, result, currentIndex) {
|
|
5083
|
+
return {
|
|
5084
|
+
i: currentIndex + step.nextIndex,
|
|
5085
|
+
result: `${result}${step.output}`,
|
|
5086
|
+
state: applySanitizeStep(state, step.state)
|
|
5087
|
+
};
|
|
5088
|
+
}
|
|
5089
|
+
function sanitizeTsContent(content, options = {}) {
|
|
5090
|
+
const { stripStrings = false } = options;
|
|
5091
|
+
let result = "";
|
|
5092
|
+
let i = 0;
|
|
5093
|
+
let state = buildSanitizeState();
|
|
5094
|
+
while (i < content.length) {
|
|
5095
|
+
const char = content[i] ?? "";
|
|
5096
|
+
const next = content[i + 1] ?? "";
|
|
5097
|
+
if (state.inLineComment) {
|
|
5098
|
+
const step2 = processLineCommentState(char);
|
|
5099
|
+
const nextState2 = runSanitizeStep(step2, state, result, i);
|
|
5100
|
+
i = nextState2.i;
|
|
5101
|
+
result = nextState2.result;
|
|
5102
|
+
state = nextState2.state;
|
|
5103
|
+
continue;
|
|
5104
|
+
}
|
|
5105
|
+
if (state.inBlockComment) {
|
|
5106
|
+
const step2 = processBlockCommentState(char, next);
|
|
5107
|
+
const nextState2 = runSanitizeStep(step2, state, result, i);
|
|
5108
|
+
i = nextState2.i;
|
|
5109
|
+
result = nextState2.result;
|
|
5110
|
+
state = nextState2.state;
|
|
5111
|
+
continue;
|
|
5112
|
+
}
|
|
5113
|
+
if (state.inSingle) {
|
|
5114
|
+
const step2 = processQuoteState(content, i, stripStrings, "'");
|
|
5115
|
+
const nextState2 = runSanitizeStep(step2, state, result, i);
|
|
5116
|
+
i = nextState2.i;
|
|
5117
|
+
result = nextState2.result;
|
|
5118
|
+
state = nextState2.state;
|
|
5119
|
+
continue;
|
|
5120
|
+
}
|
|
5121
|
+
if (state.inDouble) {
|
|
5122
|
+
const step2 = processQuoteState(content, i, stripStrings, '"');
|
|
5123
|
+
const nextState2 = runSanitizeStep(step2, state, result, i);
|
|
5124
|
+
i = nextState2.i;
|
|
5125
|
+
result = nextState2.result;
|
|
5126
|
+
state = nextState2.state;
|
|
5127
|
+
continue;
|
|
5128
|
+
}
|
|
5129
|
+
if (state.inTemplate) {
|
|
5130
|
+
const step2 = processQuoteState(content, i, stripStrings, "`");
|
|
5131
|
+
const nextState2 = runSanitizeStep(step2, state, result, i);
|
|
5132
|
+
i = nextState2.i;
|
|
5133
|
+
result = nextState2.result;
|
|
5134
|
+
state = nextState2.state;
|
|
5135
|
+
continue;
|
|
5136
|
+
}
|
|
5137
|
+
const step = processDefaultState(char, next);
|
|
5138
|
+
const nextState = runSanitizeStep(step, state, result, i);
|
|
5139
|
+
i = nextState.i;
|
|
5140
|
+
result = nextState.result;
|
|
5141
|
+
state = nextState.state;
|
|
5142
|
+
}
|
|
5143
|
+
return result;
|
|
5144
|
+
}
|
|
4348
5145
|
function readManifest(manifestPath) {
|
|
4349
5146
|
try {
|
|
4350
5147
|
if (!existsSync(manifestPath)) {
|
|
@@ -4389,11 +5186,12 @@ async function detectLayer1Content(repoRoot, manifest) {
|
|
|
4389
5186
|
const filePath = join(repoRoot, file);
|
|
4390
5187
|
const content = safeReadFile(filePath);
|
|
4391
5188
|
if (!content) continue;
|
|
5189
|
+
const codeOnly = sanitizeTsContent(content, { stripStrings: true });
|
|
4392
5190
|
const xstatePatterns = [
|
|
4393
5191
|
/(?:export\s+)?(?:const|let|var)\s+\w+\s*=\s*createMachine\s*\(/,
|
|
4394
5192
|
/(?:export\s+)?(?:const|let|var)\s+\w+\s*=\s*setup\s*\([^)]*\)\s*\.createMachine\s*\(/
|
|
4395
5193
|
];
|
|
4396
|
-
const hasXStateMachine = xstatePatterns.some((p) => p.test(
|
|
5194
|
+
const hasXStateMachine = xstatePatterns.some((p) => p.test(codeOnly));
|
|
4397
5195
|
if (hasXStateMachine) {
|
|
4398
5196
|
machineCount++;
|
|
4399
5197
|
}
|
|
@@ -4523,6 +5321,41 @@ async function detectLayer4Content(repoRoot, manifest) {
|
|
|
4523
5321
|
}
|
|
4524
5322
|
return createSkipStatus(base, "No user-facing pages found");
|
|
4525
5323
|
}
|
|
5324
|
+
var detectStaticLayer = async (_repoRoot, _manifest) => ({
|
|
5325
|
+
layer: 0,
|
|
5326
|
+
hasContent: true,
|
|
5327
|
+
contentType: "machines",
|
|
5328
|
+
// Not really, but L0 always runs
|
|
5329
|
+
count: 1,
|
|
5330
|
+
source: "filesystem"
|
|
5331
|
+
});
|
|
5332
|
+
var layerDetectors = {
|
|
5333
|
+
0: detectStaticLayer,
|
|
5334
|
+
1: detectLayer1Content,
|
|
5335
|
+
2: detectLayer2Content,
|
|
5336
|
+
3: detectLayer3Content,
|
|
5337
|
+
4: detectLayer4Content
|
|
5338
|
+
};
|
|
5339
|
+
function getUnknownLayerStatus(layer) {
|
|
5340
|
+
return {
|
|
5341
|
+
layer,
|
|
5342
|
+
hasContent: false,
|
|
5343
|
+
contentType: "machines",
|
|
5344
|
+
count: 0,
|
|
5345
|
+
source: "filesystem",
|
|
5346
|
+
skipReason: `Unknown layer ${layer}`
|
|
5347
|
+
};
|
|
5348
|
+
}
|
|
5349
|
+
async function detectLayerStatus(layer, repoRoot, manifest) {
|
|
5350
|
+
const detector = layerDetectors[layer];
|
|
5351
|
+
if (!detector) {
|
|
5352
|
+
return getUnknownLayerStatus(layer);
|
|
5353
|
+
}
|
|
5354
|
+
if (layer === 0) {
|
|
5355
|
+
return detectStaticLayer();
|
|
5356
|
+
}
|
|
5357
|
+
return detector(repoRoot, manifest);
|
|
5358
|
+
}
|
|
4526
5359
|
async function detectLayerContent(repoRoot, selectedLayers, manifestPath) {
|
|
4527
5360
|
const effectiveManifestPath = join(repoRoot, ".runa/manifests/manifest.json");
|
|
4528
5361
|
const manifest = readManifest(effectiveManifestPath);
|
|
@@ -4530,40 +5363,7 @@ async function detectLayerContent(repoRoot, selectedLayers, manifestPath) {
|
|
|
4530
5363
|
const skippedLayers = [];
|
|
4531
5364
|
const runnableLayers = [];
|
|
4532
5365
|
for (const layer of selectedLayers) {
|
|
4533
|
-
|
|
4534
|
-
switch (layer) {
|
|
4535
|
-
case 0:
|
|
4536
|
-
status = {
|
|
4537
|
-
layer: 0,
|
|
4538
|
-
hasContent: true,
|
|
4539
|
-
contentType: "machines",
|
|
4540
|
-
// Not really, but L0 always runs
|
|
4541
|
-
count: 1,
|
|
4542
|
-
source: "filesystem"
|
|
4543
|
-
};
|
|
4544
|
-
break;
|
|
4545
|
-
case 1:
|
|
4546
|
-
status = await detectLayer1Content(repoRoot, manifest);
|
|
4547
|
-
break;
|
|
4548
|
-
case 2:
|
|
4549
|
-
status = await detectLayer2Content(repoRoot);
|
|
4550
|
-
break;
|
|
4551
|
-
case 3:
|
|
4552
|
-
status = await detectLayer3Content(repoRoot, manifest);
|
|
4553
|
-
break;
|
|
4554
|
-
case 4:
|
|
4555
|
-
status = await detectLayer4Content(repoRoot, manifest);
|
|
4556
|
-
break;
|
|
4557
|
-
default:
|
|
4558
|
-
status = {
|
|
4559
|
-
layer,
|
|
4560
|
-
hasContent: false,
|
|
4561
|
-
contentType: "machines",
|
|
4562
|
-
count: 0,
|
|
4563
|
-
source: "filesystem",
|
|
4564
|
-
skipReason: `Unknown layer ${layer}`
|
|
4565
|
-
};
|
|
4566
|
-
}
|
|
5366
|
+
const status = await detectLayerStatus(layer, repoRoot, manifest);
|
|
4567
5367
|
layers[layer] = status;
|
|
4568
5368
|
if (status.hasContent) {
|
|
4569
5369
|
runnableLayers.push(layer);
|
|
@@ -5248,38 +6048,70 @@ init_esm_shims();
|
|
|
5248
6048
|
function emptyStats2() {
|
|
5249
6049
|
return { tables: 0, functions: 0, policies: 0, indexes: 0, triggers: 0 };
|
|
5250
6050
|
}
|
|
5251
|
-
function
|
|
6051
|
+
function isStatsAvailable(stats) {
|
|
6052
|
+
return stats != null && stats.available !== false;
|
|
6053
|
+
}
|
|
6054
|
+
function isSemanticSnapshotAvailable(stats) {
|
|
6055
|
+
return stats != null && stats.available !== false && stats.canonicalSnapshot?.available === true;
|
|
6056
|
+
}
|
|
6057
|
+
function formatUnavailableCell(stats) {
|
|
6058
|
+
if (stats == null) return "n/a";
|
|
6059
|
+
return stats.error ? `\u26AA unavailable` : "n/a";
|
|
6060
|
+
}
|
|
6061
|
+
function formatReferenceCell(stats, isAvailable) {
|
|
6062
|
+
if (!isAvailable) return "\u26AA unavailable";
|
|
5252
6063
|
return formatSchemaStatsCompact(stats, true);
|
|
5253
6064
|
}
|
|
5254
|
-
function formatCiCellWithDiff(stats, reference) {
|
|
5255
|
-
if (
|
|
6065
|
+
function formatCiCellWithDiff(stats, reference, semanticSummary, isAvailable, referenceAvailable) {
|
|
6066
|
+
if (!isAvailable) return "\u26AA unavailable";
|
|
6067
|
+
if (!referenceAvailable) return formatSchemaStatsCompact(stats, true);
|
|
6068
|
+
if (hasDisplayedStatsDiff(stats, reference) || semanticSummary?.hasChanges) {
|
|
5256
6069
|
return `\u26A0\uFE0F ${formatSchemaStatsWithDiff(stats, reference)}`;
|
|
5257
6070
|
}
|
|
5258
6071
|
return formatSchemaStatsCompact(stats, true);
|
|
5259
6072
|
}
|
|
5260
|
-
function formatProdCellWithDiff(stats, reference, schemaName, expectedDrift) {
|
|
5261
|
-
if (!
|
|
6073
|
+
function formatProdCellWithDiff(stats, reference, schemaName, expectedDrift, semanticSummary, isAvailable, referenceAvailable) {
|
|
6074
|
+
if (!isAvailable) return "\u26AA unavailable";
|
|
6075
|
+
if (!referenceAvailable) return formatSchemaStatsCompact(stats, true);
|
|
6076
|
+
if (!hasDisplayedStatsDiff(stats, reference) && !semanticSummary?.hasChanges) {
|
|
5262
6077
|
return formatSchemaStatsCompact(stats, true);
|
|
5263
6078
|
}
|
|
5264
6079
|
const fieldDiffs = getFieldDiffs(stats, reference);
|
|
5265
|
-
const allExpected = expectedDrift.length > 0 && isAllDriftExpected(schemaName, fieldDiffs, expectedDrift);
|
|
6080
|
+
const allExpected = !semanticSummary?.hasChanges && expectedDrift.length > 0 && isAllDriftExpected(schemaName, fieldDiffs, expectedDrift);
|
|
5266
6081
|
const marker = allExpected ? "\u{1F4CB}" : "\u{1F4DD}";
|
|
5267
6082
|
return `${marker} ${formatSchemaStatsWithDiff(stats, reference)}`;
|
|
5268
6083
|
}
|
|
5269
|
-
function formatCiTotalCellWithDiff(stats, reference) {
|
|
6084
|
+
function formatCiTotalCellWithDiff(stats, reference, hasSemanticChanges, isAvailable, referenceAvailable) {
|
|
6085
|
+
if (!isAvailable) return "**\u26AA unavailable**";
|
|
6086
|
+
if (!referenceAvailable) return `**${formatSchemaStatsCompact(stats, true)}**`;
|
|
5270
6087
|
const formatted = formatSchemaStatsCompact(stats, true);
|
|
5271
|
-
if (hasDisplayedStatsDiff(stats, reference)) {
|
|
6088
|
+
if (hasDisplayedStatsDiff(stats, reference) || hasSemanticChanges) {
|
|
5272
6089
|
return `\u26A0\uFE0F **${formatted}**`;
|
|
5273
6090
|
}
|
|
5274
6091
|
return `**${formatted}**`;
|
|
5275
6092
|
}
|
|
5276
|
-
function formatProdTotalCellWithDiff(stats, reference) {
|
|
6093
|
+
function formatProdTotalCellWithDiff(stats, reference, hasSemanticChanges, isAvailable, referenceAvailable) {
|
|
6094
|
+
if (!isAvailable) return "**\u26AA unavailable**";
|
|
6095
|
+
if (!referenceAvailable) return `**${formatSchemaStatsCompact(stats, true)}**`;
|
|
5277
6096
|
const formatted = formatSchemaStatsCompact(stats, true);
|
|
5278
|
-
if (hasDisplayedStatsDiff(stats, reference)) {
|
|
6097
|
+
if (hasDisplayedStatsDiff(stats, reference) || hasSemanticChanges) {
|
|
5279
6098
|
return `\u{1F4DD} **${formatted}**`;
|
|
5280
6099
|
}
|
|
5281
6100
|
return `**${formatted}**`;
|
|
5282
6101
|
}
|
|
6102
|
+
function buildCanonicalDiffs(schemaStats) {
|
|
6103
|
+
const referenceSnapshot = schemaStats.local.canonicalSnapshot;
|
|
6104
|
+
const ciSnapshot = schemaStats.ci.canonicalSnapshot;
|
|
6105
|
+
const productionSnapshot = schemaStats.production?.canonicalSnapshot;
|
|
6106
|
+
const ciDiff = isSemanticSnapshotAvailable(schemaStats.local) && isSemanticSnapshotAvailable(schemaStats.ci) ? compareCanonicalSnapshots(referenceSnapshot, ciSnapshot) : null;
|
|
6107
|
+
const productionDiff = isSemanticSnapshotAvailable(schemaStats.local) && isSemanticSnapshotAvailable(schemaStats.production) ? compareCanonicalSnapshots(referenceSnapshot, productionSnapshot) : null;
|
|
6108
|
+
return {
|
|
6109
|
+
ci: ciDiff,
|
|
6110
|
+
production: productionDiff,
|
|
6111
|
+
ciBySchema: ciDiff ? summarizeCanonicalDiffBySchema(ciDiff) : {},
|
|
6112
|
+
productionBySchema: productionDiff ? summarizeCanonicalDiffBySchema(productionDiff) : {}
|
|
6113
|
+
};
|
|
6114
|
+
}
|
|
5283
6115
|
function getSortedActiveSchemas(schemaStats) {
|
|
5284
6116
|
const allSchemas = /* @__PURE__ */ new Set([
|
|
5285
6117
|
...getActiveSchemas(schemaStats.local),
|
|
@@ -5292,37 +6124,67 @@ function getSortedActiveSchemas(schemaStats) {
|
|
|
5292
6124
|
return a.localeCompare(b);
|
|
5293
6125
|
});
|
|
5294
6126
|
}
|
|
5295
|
-
function generateSchemaTableRows(schemaStats, sortedSchemas, hasProduction, expectedDrift) {
|
|
6127
|
+
function generateSchemaTableRows(schemaStats, sortedSchemas, hasProduction, expectedDrift, ciDiffBySchema, productionDiffBySchema) {
|
|
5296
6128
|
const lines = [];
|
|
5297
6129
|
for (const schemaName of sortedSchemas) {
|
|
5298
6130
|
const localStats = schemaStats.local.schemas[schemaName] ?? emptyStats2();
|
|
5299
6131
|
const ciStats = schemaStats.ci.schemas[schemaName] ?? emptyStats2();
|
|
5300
6132
|
const prodStats = schemaStats.production?.schemas[schemaName] ?? null;
|
|
5301
|
-
const localCell =
|
|
5302
|
-
const ciCell = formatCiCellWithDiff(
|
|
5303
|
-
|
|
6133
|
+
const localCell = formatReferenceCell(localStats, isStatsAvailable(schemaStats.local));
|
|
6134
|
+
const ciCell = formatCiCellWithDiff(
|
|
6135
|
+
ciStats,
|
|
6136
|
+
localStats,
|
|
6137
|
+
ciDiffBySchema[schemaName],
|
|
6138
|
+
isStatsAvailable(schemaStats.ci),
|
|
6139
|
+
isStatsAvailable(schemaStats.local)
|
|
6140
|
+
);
|
|
6141
|
+
const prodCell = prodStats !== null ? formatProdCellWithDiff(
|
|
6142
|
+
prodStats,
|
|
6143
|
+
localStats,
|
|
6144
|
+
schemaName,
|
|
6145
|
+
expectedDrift,
|
|
6146
|
+
productionDiffBySchema[schemaName],
|
|
6147
|
+
isStatsAvailable(schemaStats.production),
|
|
6148
|
+
isStatsAvailable(schemaStats.local)
|
|
6149
|
+
) : formatUnavailableCell(schemaStats.production);
|
|
5304
6150
|
lines.push(
|
|
5305
6151
|
hasProduction ? `| ${schemaName} | ${localCell} | ${ciCell} | ${prodCell ?? "n/a"} |` : `| ${schemaName} | ${localCell} | ${ciCell} |`
|
|
5306
6152
|
);
|
|
5307
6153
|
}
|
|
5308
6154
|
return lines;
|
|
5309
6155
|
}
|
|
5310
|
-
function generateSchemaMatrixSummary(localTotal, ciTotal, prodTotal) {
|
|
5311
|
-
|
|
5312
|
-
|
|
5313
|
-
|
|
5314
|
-
|
|
6156
|
+
function generateSchemaMatrixSummary(localTotal, ciTotal, prodTotal, hasCiSemanticDiff, hasProdSemanticDiff, localAvailable, ciAvailable, prodAvailable) {
|
|
6157
|
+
if (!localAvailable) {
|
|
6158
|
+
const parts2 = ["\u{1F4CA} **Reference:** unavailable"];
|
|
6159
|
+
if (ciAvailable) parts2.push(`| **CI:** ${formatTotalStatsCompact(ciTotal)}`);
|
|
6160
|
+
else parts2.push("| **CI:** unavailable");
|
|
6161
|
+
if (prodTotal !== null) {
|
|
6162
|
+
parts2.push(
|
|
6163
|
+
prodAvailable ? `| **Prod:** ${formatTotalStatsCompact(prodTotal)}` : "| **Prod:** unavailable"
|
|
6164
|
+
);
|
|
6165
|
+
}
|
|
6166
|
+
return parts2.join(" ");
|
|
6167
|
+
}
|
|
6168
|
+
const ciSynced = ciAvailable && !hasDisplayedStatsDiff(localTotal, ciTotal) && !hasCiSemanticDiff;
|
|
6169
|
+
const prodSynced = prodTotal === null || prodAvailable && !hasDisplayedStatsDiff(localTotal, prodTotal) && !hasProdSemanticDiff;
|
|
6170
|
+
if (ciSynced && prodSynced && ciAvailable && (prodTotal === null || prodAvailable)) {
|
|
6171
|
+
return `\u{1F4CA} **Reference:** ${formatTotalStatsCompact(localTotal)}`;
|
|
5315
6172
|
}
|
|
5316
|
-
const parts = [`\u{1F4CA} **
|
|
5317
|
-
if (!
|
|
6173
|
+
const parts = [`\u{1F4CA} **Reference:** ${formatTotalStatsCompact(localTotal)}`];
|
|
6174
|
+
if (!ciAvailable) parts.push("| **CI:** unavailable");
|
|
6175
|
+
else if (!ciSynced) parts.push(`| **CI:** ${formatTotalStatsCompact(ciTotal)}`);
|
|
5318
6176
|
if (prodTotal !== null && !prodSynced) {
|
|
5319
|
-
parts.push(
|
|
6177
|
+
parts.push(
|
|
6178
|
+
prodAvailable ? `| **Prod:** \u{1F4DD} ${formatTotalStatsCompact(prodTotal)}` : "| **Prod:** unavailable"
|
|
6179
|
+
);
|
|
5320
6180
|
}
|
|
5321
6181
|
return parts.join(" ");
|
|
5322
6182
|
}
|
|
5323
6183
|
function generateIndexDiffSection(schemaStats) {
|
|
5324
|
-
if (!schemaStats.production) return [];
|
|
5325
|
-
const
|
|
6184
|
+
if (!isStatsAvailable(schemaStats.local) || !isStatsAvailable(schemaStats.production)) return [];
|
|
6185
|
+
const productionStats = schemaStats.production;
|
|
6186
|
+
if (!productionStats) return [];
|
|
6187
|
+
const diff = compareIndexLists(schemaStats.local, productionStats);
|
|
5326
6188
|
if (!hasIndexDiff(diff)) return [];
|
|
5327
6189
|
const lines = [];
|
|
5328
6190
|
if (diff.missing.length > 0) {
|
|
@@ -5342,8 +6204,85 @@ function generateIndexDiffSection(schemaStats) {
|
|
|
5342
6204
|
}
|
|
5343
6205
|
return lines;
|
|
5344
6206
|
}
|
|
6207
|
+
function appendAvailabilityNote(lines, label, error, kind) {
|
|
6208
|
+
const scope = kind === "stats" ? "schema stats" : "semantic diff";
|
|
6209
|
+
lines.push(`> \u26AA **${label} ${scope} unavailable:** ${error ?? "unknown error"}`);
|
|
6210
|
+
}
|
|
6211
|
+
function generateAvailabilityNotes(schemaStats) {
|
|
6212
|
+
const lines = [];
|
|
6213
|
+
const appendNotesForEnv = (label, stats) => {
|
|
6214
|
+
if (stats == null) return;
|
|
6215
|
+
if (!isStatsAvailable(stats)) {
|
|
6216
|
+
appendAvailabilityNote(lines, label, stats.error, "stats");
|
|
6217
|
+
return;
|
|
6218
|
+
}
|
|
6219
|
+
if (stats.canonicalSnapshot?.available === false) {
|
|
6220
|
+
appendAvailabilityNote(lines, label, stats.canonicalSnapshot.error, "semantic");
|
|
6221
|
+
}
|
|
6222
|
+
};
|
|
6223
|
+
appendNotesForEnv("Reference", schemaStats.local);
|
|
6224
|
+
appendNotesForEnv("CI", schemaStats.ci);
|
|
6225
|
+
appendNotesForEnv("Prod", schemaStats.production);
|
|
6226
|
+
if (lines.length === 0) return [];
|
|
6227
|
+
return ["", ...lines, ">"];
|
|
6228
|
+
}
|
|
6229
|
+
function describeObject(object) {
|
|
6230
|
+
switch (object.kind) {
|
|
6231
|
+
case "index":
|
|
6232
|
+
return `index \`${object.label}\``;
|
|
6233
|
+
case "policy":
|
|
6234
|
+
return `policy \`${object.label}\``;
|
|
6235
|
+
case "trigger":
|
|
6236
|
+
return `trigger \`${object.label}\``;
|
|
6237
|
+
case "function":
|
|
6238
|
+
return `function \`${object.label}\``;
|
|
6239
|
+
case "table":
|
|
6240
|
+
return `table \`${object.label}\``;
|
|
6241
|
+
}
|
|
6242
|
+
}
|
|
6243
|
+
function appendObjectLines(lines, prefix, objects, label) {
|
|
6244
|
+
if (objects.length === 0) return;
|
|
6245
|
+
lines.push(`> ${prefix} **${label}:**`);
|
|
6246
|
+
for (const object of objects.slice(0, 6)) {
|
|
6247
|
+
lines.push(`> - ${describeObject(object)}`);
|
|
6248
|
+
}
|
|
6249
|
+
if (objects.length > 6) {
|
|
6250
|
+
lines.push(`> - ... \u4ED6${objects.length - 6}\u4EF6`);
|
|
6251
|
+
}
|
|
6252
|
+
lines.push(">");
|
|
6253
|
+
}
|
|
6254
|
+
function appendChangedObjectLines(lines, prefix, pairs, label) {
|
|
6255
|
+
if (pairs.length === 0) return;
|
|
6256
|
+
lines.push(`> ${prefix} **${label}:**`);
|
|
6257
|
+
for (const pair of pairs.slice(0, 6)) {
|
|
6258
|
+
lines.push(`> - ${describeObject(pair.reference)}`);
|
|
6259
|
+
}
|
|
6260
|
+
if (pairs.length > 6) {
|
|
6261
|
+
lines.push(`> - ... \u4ED6${pairs.length - 6}\u4EF6`);
|
|
6262
|
+
}
|
|
6263
|
+
lines.push(">");
|
|
6264
|
+
}
|
|
6265
|
+
function generateSemanticDiffSection(title, prefix, diff) {
|
|
6266
|
+
if (!diff || !hasCanonicalChanges(diff)) return [];
|
|
6267
|
+
const lines = ["", `> ${prefix} **${title} semantic diff:**`, ">"];
|
|
6268
|
+
appendObjectLines(lines, prefix, diff.missing, "missing objects");
|
|
6269
|
+
appendObjectLines(lines, prefix, diff.extra, "extra objects");
|
|
6270
|
+
appendChangedObjectLines(lines, prefix, diff.changed, "changed objects");
|
|
6271
|
+
if (diff.renamed.length > 0) {
|
|
6272
|
+
lines.push(`> ${prefix} **renamed indexes:**`);
|
|
6273
|
+
for (const pair of diff.renamed.slice(0, 6)) {
|
|
6274
|
+
lines.push(`> - \`${pair.reference.label}\` -> \`${pair.target.label}\``);
|
|
6275
|
+
}
|
|
6276
|
+
if (diff.renamed.length > 6) {
|
|
6277
|
+
lines.push(`> - ... \u4ED6${diff.renamed.length - 6}\u4EF6`);
|
|
6278
|
+
}
|
|
6279
|
+
lines.push(">");
|
|
6280
|
+
}
|
|
6281
|
+
return lines;
|
|
6282
|
+
}
|
|
5345
6283
|
function generateExpectedDriftFootnotes(schemaStats, sortedSchemas, expectedDrift) {
|
|
5346
6284
|
if (expectedDrift.length === 0 || !schemaStats.production) return [];
|
|
6285
|
+
if (!isStatsAvailable(schemaStats.local) || !isStatsAvailable(schemaStats.production)) return [];
|
|
5347
6286
|
const allReasons = /* @__PURE__ */ new Set();
|
|
5348
6287
|
for (const schemaName of sortedSchemas) {
|
|
5349
6288
|
const localStats = schemaStats.local.schemas[schemaName] ?? emptyStats2();
|
|
@@ -5370,33 +6309,69 @@ function formatSchemaMatrix(schemaStats, gitDiff, expectedDrift = []) {
|
|
|
5370
6309
|
const hasGitChanges = gitDiff && gitDiff.filesChanged.length > 0;
|
|
5371
6310
|
const header = hasGitChanges ? `### \u30B9\u30AD\u30FC\u30DE\u72B6\u6CC1 (+${gitDiff.linesAdded}/-${gitDiff.linesDeleted} PR\u5185)` : "### \u30B9\u30AD\u30FC\u30DE\u72B6\u6CC1";
|
|
5372
6311
|
const sortedSchemas = getSortedActiveSchemas(schemaStats);
|
|
6312
|
+
const availabilityNotes = generateAvailabilityNotes(schemaStats);
|
|
5373
6313
|
if (sortedSchemas.length === 0) {
|
|
5374
|
-
return [header, "", "_\u30B9\u30AD\u30FC\u30DE\u30AA\u30D6\u30B8\u30A7\u30AF\u30C8\u304C\u3042\u308A\u307E\u305B\u3093_", ""];
|
|
6314
|
+
return [header, "", ...availabilityNotes, "_\u30B9\u30AD\u30FC\u30DE\u30AA\u30D6\u30B8\u30A7\u30AF\u30C8\u304C\u3042\u308A\u307E\u305B\u3093_", ""];
|
|
5375
6315
|
}
|
|
5376
6316
|
const hasProduction = schemaStats.production !== null;
|
|
5377
|
-
const
|
|
5378
|
-
const
|
|
6317
|
+
const canonicalDiffs = buildCanonicalDiffs(schemaStats);
|
|
6318
|
+
const tableHeader = hasProduction ? ["| Schema | \u{1F4C4} Reference | \u{1F52C} CI | \u{1F3ED} Prod |", "|--------|:------------:|:-----:|:-------:|"] : ["| Schema | \u{1F4C4} Reference | \u{1F52C} CI |", "|--------|:------------:|:-----:|"];
|
|
6319
|
+
const rows = generateSchemaTableRows(
|
|
6320
|
+
schemaStats,
|
|
6321
|
+
sortedSchemas,
|
|
6322
|
+
hasProduction,
|
|
6323
|
+
expectedDrift,
|
|
6324
|
+
canonicalDiffs.ciBySchema,
|
|
6325
|
+
canonicalDiffs.productionBySchema
|
|
6326
|
+
);
|
|
5379
6327
|
const { local, ci, production } = schemaStats;
|
|
5380
|
-
const localTotalCell = `**${formatSchemaStatsCompact(local.total, true)}
|
|
5381
|
-
const ciTotalCell = formatCiTotalCellWithDiff(
|
|
5382
|
-
|
|
6328
|
+
const localTotalCell = isStatsAvailable(local) ? `**${formatSchemaStatsCompact(local.total, true)}**` : "**\u26AA unavailable**";
|
|
6329
|
+
const ciTotalCell = formatCiTotalCellWithDiff(
|
|
6330
|
+
ci.total,
|
|
6331
|
+
local.total,
|
|
6332
|
+
canonicalDiffs.ci ? hasCanonicalChanges(canonicalDiffs.ci) : false,
|
|
6333
|
+
isStatsAvailable(ci),
|
|
6334
|
+
isStatsAvailable(local)
|
|
6335
|
+
);
|
|
6336
|
+
const prodTotalCell = production ? formatProdTotalCellWithDiff(
|
|
6337
|
+
production.total,
|
|
6338
|
+
local.total,
|
|
6339
|
+
canonicalDiffs.production ? hasCanonicalChanges(canonicalDiffs.production) : false,
|
|
6340
|
+
isStatsAvailable(production),
|
|
6341
|
+
isStatsAvailable(local)
|
|
6342
|
+
) : null;
|
|
5383
6343
|
const totalRow = hasProduction ? `| **Total** | ${localTotalCell} | ${ciTotalCell} | ${prodTotalCell ?? "n/a"} |` : `| **Total** | ${localTotalCell} | ${ciTotalCell} |`;
|
|
5384
|
-
const summary = generateSchemaMatrixSummary(
|
|
6344
|
+
const summary = generateSchemaMatrixSummary(
|
|
6345
|
+
local.total,
|
|
6346
|
+
ci.total,
|
|
6347
|
+
production?.total ?? null,
|
|
6348
|
+
canonicalDiffs.ci ? hasCanonicalChanges(canonicalDiffs.ci) : false,
|
|
6349
|
+
canonicalDiffs.production ? hasCanonicalChanges(canonicalDiffs.production) : false,
|
|
6350
|
+
isStatsAvailable(local),
|
|
6351
|
+
isStatsAvailable(ci),
|
|
6352
|
+
isStatsAvailable(production)
|
|
6353
|
+
);
|
|
5385
6354
|
const indexDiffSection = generateIndexDiffSection(schemaStats);
|
|
5386
6355
|
const expectedDriftNotes = generateExpectedDriftFootnotes(
|
|
5387
6356
|
schemaStats,
|
|
5388
6357
|
sortedSchemas,
|
|
5389
6358
|
expectedDrift
|
|
5390
6359
|
);
|
|
6360
|
+
const semanticDiffSection = [
|
|
6361
|
+
...generateSemanticDiffSection("CI", "\u26A0\uFE0F", canonicalDiffs.ci),
|
|
6362
|
+
...generateSemanticDiffSection("Prod", "\u{1F4DD}", canonicalDiffs.production)
|
|
6363
|
+
];
|
|
5391
6364
|
return [
|
|
5392
6365
|
header,
|
|
5393
6366
|
"",
|
|
6367
|
+
...availabilityNotes,
|
|
5394
6368
|
...tableHeader,
|
|
5395
6369
|
...rows,
|
|
5396
6370
|
totalRow,
|
|
5397
6371
|
"",
|
|
5398
6372
|
summary,
|
|
5399
6373
|
...indexDiffSection,
|
|
6374
|
+
...semanticDiffSection,
|
|
5400
6375
|
...expectedDriftNotes,
|
|
5401
6376
|
"",
|
|
5402
6377
|
"<sub>T=\u30C6\u30FC\u30D6\u30EB F=\u95A2\u6570 P=\u30DD\u30EA\u30B7\u30FC I=\u30A4\u30F3\u30C7\u30C3\u30AF\u30B9 Tr=\u30C8\u30EA\u30AC\u30FC</sub>",
|
|
@@ -5406,8 +6381,8 @@ function formatSchemaMatrix(schemaStats, gitDiff, expectedDrift = []) {
|
|
|
5406
6381
|
|
|
5407
6382
|
// src/commands/ci/machine/formatters/sections/final-comment.ts
|
|
5408
6383
|
function formatDriftStatus(drift) {
|
|
5409
|
-
if (drift == null) {
|
|
5410
|
-
return "
|
|
6384
|
+
if (drift == null || !drift.checkExecuted) {
|
|
6385
|
+
return "_\u672A\u691C\u8A3C_";
|
|
5411
6386
|
}
|
|
5412
6387
|
return drift.hasDrift ? "**\u274C \u30C9\u30EA\u30D5\u30C8\u691C\u51FA (schemas \u2194 CI DB)**" : "**\u2705 \u30C9\u30EA\u30D5\u30C8\u306A\u3057 (schemas \u2194 CI DB)**";
|
|
5413
6388
|
}
|
|
@@ -5534,6 +6509,8 @@ function classifyPreviewError(error) {
|
|
|
5534
6509
|
}
|
|
5535
6510
|
function generateIndexDiffWarning(schemaStats) {
|
|
5536
6511
|
if (!schemaStats?.local || !schemaStats?.production) return [];
|
|
6512
|
+
if (schemaStats.local.available === false || schemaStats.production.available === false)
|
|
6513
|
+
return [];
|
|
5537
6514
|
const indexDiff = compareIndexLists(schemaStats.local, schemaStats.production);
|
|
5538
6515
|
if (!hasIndexDiff(indexDiff)) return [];
|
|
5539
6516
|
const diffParts = [];
|
|
@@ -6410,7 +7387,7 @@ function createBuildAndPlaywrightInput(context) {
|
|
|
6410
7387
|
enablePublicE2EFlag: true
|
|
6411
7388
|
}),
|
|
6412
7389
|
isCI: context.input.isCI ?? false,
|
|
6413
|
-
skipPlaywright: context.selectedLayers.includes(4)
|
|
7390
|
+
skipPlaywright: !context.selectedLayers.includes(4)
|
|
6414
7391
|
};
|
|
6415
7392
|
}
|
|
6416
7393
|
function createAppStartInput(context) {
|
|
@@ -6845,11 +7822,12 @@ var ciMachine = setup({
|
|
|
6845
7822
|
invoke: {
|
|
6846
7823
|
src: "collectSchemaStats",
|
|
6847
7824
|
input: ({ context }) => ({
|
|
6848
|
-
|
|
6849
|
-
|
|
7825
|
+
repoRoot: context.repoRoot ?? process.cwd(),
|
|
7826
|
+
referenceDbUrl: context.supabase?.databaseUrlRaw ?? null,
|
|
6850
7827
|
ciDbUrl: context.supabase?.databaseUrlRaw ?? null,
|
|
6851
7828
|
// Query production only in ci-pr modes
|
|
6852
|
-
queryProduction: context.mode !== "ci-local"
|
|
7829
|
+
queryProduction: context.mode !== "ci-local",
|
|
7830
|
+
tmpDir: context.tmpDir ?? process.cwd()
|
|
6853
7831
|
}),
|
|
6854
7832
|
onDone: {
|
|
6855
7833
|
target: "decidePath",
|
|
@@ -7704,16 +8682,13 @@ var stateLogHandlers = {
|
|
|
7704
8682
|
logger.info(`Running: ${ctx.selectedLayers.map((l) => `test layer${l}`).join(", ")}`);
|
|
7705
8683
|
}
|
|
7706
8684
|
};
|
|
7707
|
-
|
|
7708
|
-
|
|
7709
|
-
|
|
7710
|
-
|
|
7711
|
-
|
|
7712
|
-
|
|
7713
|
-
|
|
7714
|
-
handler(snapshot.context, logger);
|
|
7715
|
-
}
|
|
7716
|
-
}
|
|
8685
|
+
var handleStateChange = createMachineStateChangeLogger({
|
|
8686
|
+
getState: getStateName,
|
|
8687
|
+
getContext: (snapshot) => snapshot.context,
|
|
8688
|
+
handlers: stateLogHandlers,
|
|
8689
|
+
useParentState: true,
|
|
8690
|
+
handlerArgumentOrder: "context-first"
|
|
8691
|
+
});
|
|
7717
8692
|
function printSummary(logger, output) {
|
|
7718
8693
|
endCurrentGroup();
|
|
7719
8694
|
logSection2("Summary");
|
|
@@ -8052,6 +9027,7 @@ function logPlan2(steps) {
|
|
|
8052
9027
|
console.log("");
|
|
8053
9028
|
}
|
|
8054
9029
|
}
|
|
9030
|
+
var CI_PR_UNKNOWN_STATE_LOG_SKIP = /* @__PURE__ */ new Set(["decidePath", "done", "failed"]);
|
|
8055
9031
|
var stateLogHandlers2 = {
|
|
8056
9032
|
// ─── Setup Phase ───────────────────────────────────────────
|
|
8057
9033
|
setup: (_ctx, _logger) => {
|
|
@@ -8186,18 +9162,18 @@ var stateLogHandlers2 = {
|
|
|
8186
9162
|
logSection3("Finalize: Post GitHub Comment");
|
|
8187
9163
|
}
|
|
8188
9164
|
};
|
|
8189
|
-
|
|
8190
|
-
|
|
8191
|
-
|
|
8192
|
-
|
|
8193
|
-
|
|
8194
|
-
|
|
8195
|
-
|
|
8196
|
-
|
|
8197
|
-
|
|
8198
|
-
|
|
9165
|
+
var handleStateChange2 = createMachineStateChangeLogger({
|
|
9166
|
+
getState: getStateName,
|
|
9167
|
+
getContext: (snapshot) => snapshot.context,
|
|
9168
|
+
handlers: stateLogHandlers2,
|
|
9169
|
+
useParentState: true,
|
|
9170
|
+
handlerArgumentOrder: "context-first",
|
|
9171
|
+
onUnknownState: (state, logger) => {
|
|
9172
|
+
if (cliOutputFormat !== "json" && !CI_PR_UNKNOWN_STATE_LOG_SKIP.has(state)) {
|
|
9173
|
+
logger.info(`State: ${state}`);
|
|
9174
|
+
}
|
|
8199
9175
|
}
|
|
8200
|
-
}
|
|
9176
|
+
});
|
|
8201
9177
|
function printSummary2(logger, output) {
|
|
8202
9178
|
logSection3("Summary");
|
|
8203
9179
|
logger.info(`Mode: ${output.mode}`);
|