@runa-ai/runa-cli 0.7.0 → 0.7.1
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-V66FAQXB.js → build-HUDIP6KU.js} +1 -1
- package/dist/{chunk-AIP6MR42.js → chunk-AFY3TX4I.js} +1 -1
- package/dist/{chunk-KWX3JHCY.js → chunk-AKZAN4BC.js} +6 -1
- package/dist/{chunk-SGJG3BKD.js → chunk-CCW3PLQY.js} +1 -1
- package/dist/{chunk-IBVVGH6X.js → chunk-EMB6IZFT.js} +17 -4
- package/dist/{ci-ZWRVWNFX.js → ci-XY6IKEDC.js} +844 -120
- package/dist/{cli-2JNBJUBB.js → cli-UZA4RBNQ.js} +13 -13
- package/dist/commands/ci/machine/actors/db/collect-schema-stats.d.ts +4 -1
- package/dist/commands/ci/machine/actors/db/production-preview.d.ts +10 -0
- package/dist/commands/ci/machine/actors/db/schema-canonical-diff.d.ts +22 -0
- package/dist/commands/ci/machine/commands/machine-runner.d.ts +2 -0
- package/dist/commands/ci/machine/formatters/sections/production-schema-status.d.ts +30 -0
- package/dist/commands/ci/machine/helpers.d.ts +8 -0
- package/dist/commands/ci/machine/machine.d.ts +57 -4
- package/dist/commands/template-check/commands/template-check.d.ts +1 -0
- package/dist/commands/template-check/contract.d.ts +1 -0
- package/dist/constants/versions.d.ts +1 -1
- package/dist/{db-XULCILOU.js → db-Q3GF7JWP.js} +78 -18
- package/dist/{env-SS66PZ4B.js → env-GMB3THRG.js} +1 -1
- package/dist/{hotfix-YA3DGLOM.js → hotfix-NDTPY2T4.js} +1 -1
- package/dist/index.js +3 -3
- package/dist/{init-ZIL6LRFO.js → init-U4VCRHTD.js} +1 -1
- package/dist/{template-check-3P4HZXVY.js → template-check-FFJVDLBF.js} +23 -6
- package/dist/{upgrade-NUK3ZBCL.js → upgrade-7TWORWBV.js} +1 -1
- package/dist/{vuln-check-2W7N5TA2.js → vuln-check-6CMNPSBR.js} +1 -1
- package/dist/{vuln-checker-IQJ56RUV.js → vuln-checker-EJJTNDNE.js} +1 -1
- package/package.json +2 -2
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { createRequire } from 'module';
|
|
3
|
-
import { normalizeDatabaseUrlForDdl,
|
|
3
|
+
import { normalizeDatabaseUrlForDdl, parseBoolish, enhanceConnectionError, detectAppSchemas, formatSchemasForSql } from './chunk-XDCHRVE3.js';
|
|
4
4
|
import { isPathContained } from './chunk-DRSUEMAK.js';
|
|
5
5
|
import './chunk-QDF7QXBL.js';
|
|
6
|
-
import { getSnapshotStateName, isSnapshotComplete } from './chunk-
|
|
6
|
+
import { getSnapshotStateName, isSnapshotComplete } from './chunk-EMB6IZFT.js';
|
|
7
7
|
import { writeEnvLocal, startAppBackground, waitForAppReady, executePrSetupBase, createErrorOutput } from './chunk-HD74F6W2.js';
|
|
8
|
-
import {
|
|
8
|
+
import { parsePostgresUrl, buildPsqlArgs, buildPsqlEnv, psqlSyncQuery } from './chunk-7B5C6U2K.js';
|
|
9
9
|
import { ensureRunaTmpDir, runLogged } from './chunk-6FAU4IGR.js';
|
|
10
10
|
import { createMachineStateChangeLogger } from './chunk-5FT3F36G.js';
|
|
11
11
|
import { getSafeEnv, getFilteredEnv, redactSecrets } from './chunk-II7VYQEM.js';
|
|
@@ -1884,24 +1884,41 @@ var ciProdApplyCommand = new Command("prod-apply").description("Apply production
|
|
|
1884
1884
|
|
|
1885
1885
|
// src/commands/ci/commands/ci-static.ts
|
|
1886
1886
|
init_esm_shims();
|
|
1887
|
-
function
|
|
1888
|
-
|
|
1889
|
-
{
|
|
1887
|
+
function hasNativeActionlint() {
|
|
1888
|
+
try {
|
|
1889
|
+
const result = spawnSync("actionlint", ["-version"], {
|
|
1890
|
+
encoding: "utf-8",
|
|
1891
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
1892
|
+
timeout: 5e3
|
|
1893
|
+
});
|
|
1894
|
+
return result.status === 0;
|
|
1895
|
+
} catch {
|
|
1896
|
+
return false;
|
|
1897
|
+
}
|
|
1898
|
+
}
|
|
1899
|
+
function createActionlintCheck(mode) {
|
|
1900
|
+
if (mode === "local" && hasNativeActionlint()) {
|
|
1901
|
+
return {
|
|
1890
1902
|
name: "actionlint",
|
|
1891
1903
|
label: "Workflow Lint",
|
|
1892
1904
|
description: "GitHub Actions syntax validation",
|
|
1893
|
-
command: "
|
|
1894
|
-
args: [
|
|
1895
|
-
"run",
|
|
1896
|
-
"--rm",
|
|
1897
|
-
"-v",
|
|
1898
|
-
`${process.cwd()}:/repo`,
|
|
1899
|
-
"-w",
|
|
1900
|
-
"/repo",
|
|
1901
|
-
"rhysd/actionlint:1.7.9"
|
|
1902
|
-
],
|
|
1905
|
+
command: "actionlint",
|
|
1906
|
+
args: ["-color"],
|
|
1903
1907
|
category: "workflow"
|
|
1904
|
-
}
|
|
1908
|
+
};
|
|
1909
|
+
}
|
|
1910
|
+
return {
|
|
1911
|
+
name: "actionlint",
|
|
1912
|
+
label: "Workflow Lint",
|
|
1913
|
+
description: "GitHub Actions syntax validation",
|
|
1914
|
+
command: "docker",
|
|
1915
|
+
args: ["run", "--rm", "-v", `${process.cwd()}:/repo`, "-w", "/repo", "rhysd/actionlint:1.7.9"],
|
|
1916
|
+
category: "workflow"
|
|
1917
|
+
};
|
|
1918
|
+
}
|
|
1919
|
+
function getChecks(mode) {
|
|
1920
|
+
return [
|
|
1921
|
+
createActionlintCheck(mode),
|
|
1905
1922
|
{
|
|
1906
1923
|
name: "type-check",
|
|
1907
1924
|
label: "Type Check",
|
|
@@ -2014,7 +2031,7 @@ function buildStepSummaryMarkdown2(summary) {
|
|
|
2014
2031
|
return lines.join("\n");
|
|
2015
2032
|
}
|
|
2016
2033
|
function getChecksToRun(options) {
|
|
2017
|
-
let checks = getChecks().filter((c) => {
|
|
2034
|
+
let checks = getChecks(options.mode).filter((c) => {
|
|
2018
2035
|
if (c.category === "workflow") return true;
|
|
2019
2036
|
if (c.category === "core") return true;
|
|
2020
2037
|
if (c.category === "build") return options.includeBuild;
|
|
@@ -2534,6 +2551,19 @@ function normalizeWhitespace(value) {
|
|
|
2534
2551
|
function stableHash(payload) {
|
|
2535
2552
|
return createHash("sha256").update(JSON.stringify(payload)).digest("hex").slice(0, 16);
|
|
2536
2553
|
}
|
|
2554
|
+
function createCanonicalTableColumnPayload(row) {
|
|
2555
|
+
return {
|
|
2556
|
+
name: row.column_name,
|
|
2557
|
+
type: normalizeWhitespace(row.data_type),
|
|
2558
|
+
notNull: row.not_null,
|
|
2559
|
+
default: normalizeWhitespace(row.default_expr),
|
|
2560
|
+
identity: row.identity_kind ?? "",
|
|
2561
|
+
generated: row.generated_kind ?? ""
|
|
2562
|
+
};
|
|
2563
|
+
}
|
|
2564
|
+
function sortCanonicalTableColumns(columns) {
|
|
2565
|
+
return [...columns].sort((left, right) => left.name.localeCompare(right.name));
|
|
2566
|
+
}
|
|
2537
2567
|
function createCanonicalObject(kind, schema, name, label, payload, parentName) {
|
|
2538
2568
|
return {
|
|
2539
2569
|
kind,
|
|
@@ -2623,15 +2653,7 @@ function buildTableObjects(columns, flags, constraints) {
|
|
|
2623
2653
|
rlsEnabled: false,
|
|
2624
2654
|
rlsForced: false
|
|
2625
2655
|
};
|
|
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
|
-
});
|
|
2656
|
+
entry.columns.push(createCanonicalTableColumnPayload(row));
|
|
2635
2657
|
tables.set(key, entry);
|
|
2636
2658
|
}
|
|
2637
2659
|
for (const row of flags) {
|
|
@@ -2667,7 +2689,7 @@ function buildTableObjects(columns, flags, constraints) {
|
|
|
2667
2689
|
}
|
|
2668
2690
|
return Array.from(tables.values()).map(
|
|
2669
2691
|
(table) => createCanonicalObject("table", table.schema, table.table, `${table.schema}.${table.table}`, {
|
|
2670
|
-
columns: table.columns,
|
|
2692
|
+
columns: sortCanonicalTableColumns(table.columns),
|
|
2671
2693
|
constraints: table.constraints,
|
|
2672
2694
|
rlsEnabled: table.rlsEnabled,
|
|
2673
2695
|
rlsForced: table.rlsForced
|
|
@@ -3261,6 +3283,17 @@ function unavailableStats(error) {
|
|
|
3261
3283
|
canonicalSnapshot: createUnavailableCanonicalSnapshot(error)
|
|
3262
3284
|
};
|
|
3263
3285
|
}
|
|
3286
|
+
function cloneEnvironmentStats(stats) {
|
|
3287
|
+
return {
|
|
3288
|
+
...stats,
|
|
3289
|
+
schemas: Object.fromEntries(
|
|
3290
|
+
Object.entries(stats.schemas).map(([schema, values]) => [schema, { ...values }])
|
|
3291
|
+
),
|
|
3292
|
+
total: { ...stats.total },
|
|
3293
|
+
indexList: stats.indexList?.map((index) => ({ ...index })),
|
|
3294
|
+
canonicalSnapshot: stats.canonicalSnapshot
|
|
3295
|
+
};
|
|
3296
|
+
}
|
|
3264
3297
|
function logSchemaStats(label, stats, includeSchemas = false) {
|
|
3265
3298
|
const base = `[schema-stats] ${label}: ${stats.total.tables}T ${stats.total.functions}F ${stats.total.policies}P`;
|
|
3266
3299
|
if (!includeSchemas) {
|
|
@@ -3368,20 +3401,60 @@ ${result.stderr || ""}`;
|
|
|
3368
3401
|
dropReferenceDb(sourceDbUrl, referenceDb.dbName);
|
|
3369
3402
|
}
|
|
3370
3403
|
}
|
|
3404
|
+
async function resolveReferenceAndCiStats(params) {
|
|
3405
|
+
const { repoRoot, tmpDir, referenceDbUrl, ciDbUrl, referenceStrategy } = params;
|
|
3406
|
+
if (referenceStrategy === "reuse-ci" && ciDbUrl) {
|
|
3407
|
+
console.log(
|
|
3408
|
+
"[schema-stats] Reusing synced CI database as reference schema (skip temporary rebuild)"
|
|
3409
|
+
);
|
|
3410
|
+
const [ciResult2] = await Promise.allSettled([collectEnvironmentStats(ciDbUrl, "CI")]);
|
|
3411
|
+
const ci = resolveSettledStats(ciResult2, "CI");
|
|
3412
|
+
const reference = ci.available === false ? unavailableStats(ci.error ?? "CI database is unavailable for reference schema reuse") : cloneEnvironmentStats(ci);
|
|
3413
|
+
return [reference, ci];
|
|
3414
|
+
}
|
|
3415
|
+
const [referenceResult, ciResult] = await Promise.allSettled([
|
|
3416
|
+
buildReferenceStats(repoRoot, tmpDir, referenceDbUrl),
|
|
3417
|
+
collectEnvironmentStats(ciDbUrl, "CI")
|
|
3418
|
+
]);
|
|
3419
|
+
return [resolveSettledStats(referenceResult, "reference"), resolveSettledStats(ciResult, "CI")];
|
|
3420
|
+
}
|
|
3371
3421
|
var collectSchemaStatsActor = fromPromise(
|
|
3372
3422
|
async ({ input }) => {
|
|
3373
|
-
const {
|
|
3423
|
+
const {
|
|
3424
|
+
repoRoot,
|
|
3425
|
+
referenceDbUrl,
|
|
3426
|
+
ciDbUrl,
|
|
3427
|
+
referenceStrategy = "rebuild",
|
|
3428
|
+
queryProduction,
|
|
3429
|
+
tmpDir
|
|
3430
|
+
} = input;
|
|
3374
3431
|
console.log(
|
|
3375
3432
|
"[schema-stats] Collecting schema statistics (Reference/CI/Production, parallel)..."
|
|
3376
3433
|
);
|
|
3377
3434
|
const productionUrl = process.env.GH_DATABASE_URL_ADMIN || process.env.GH_DATABASE_URL;
|
|
3378
|
-
const [referenceResult,
|
|
3379
|
-
|
|
3380
|
-
|
|
3435
|
+
const [referenceResult, productionResult] = await Promise.allSettled([
|
|
3436
|
+
resolveReferenceAndCiStats({
|
|
3437
|
+
repoRoot,
|
|
3438
|
+
tmpDir,
|
|
3439
|
+
referenceDbUrl,
|
|
3440
|
+
ciDbUrl,
|
|
3441
|
+
referenceStrategy
|
|
3442
|
+
}),
|
|
3381
3443
|
collectEnvironmentStats(queryProduction ? productionUrl ?? null : null, "Production")
|
|
3382
3444
|
]);
|
|
3383
|
-
|
|
3384
|
-
|
|
3445
|
+
let local;
|
|
3446
|
+
let ci;
|
|
3447
|
+
if (referenceResult.status === "fulfilled") {
|
|
3448
|
+
local = referenceResult.value[0];
|
|
3449
|
+
ci = referenceResult.value[1];
|
|
3450
|
+
} else {
|
|
3451
|
+
console.warn(
|
|
3452
|
+
`[schema-stats] Failed to collect reference/CI schema statistics: ${referenceResult.reason}`
|
|
3453
|
+
);
|
|
3454
|
+
const reason = String(referenceResult.reason);
|
|
3455
|
+
local = unavailableStats(reason);
|
|
3456
|
+
ci = unavailableStats(reason);
|
|
3457
|
+
}
|
|
3385
3458
|
const production = resolveSettledProductionStats(productionResult);
|
|
3386
3459
|
return {
|
|
3387
3460
|
schemaStats: {
|
|
@@ -3713,24 +3786,27 @@ function extractHazardsWithContext(fullOutput, repoRoot) {
|
|
|
3713
3786
|
appendEmojiHazards(fullOutput, idempotentRoles, hazards);
|
|
3714
3787
|
return hazards;
|
|
3715
3788
|
}
|
|
3716
|
-
function
|
|
3789
|
+
function detectSchemaChangeState(fullOutput) {
|
|
3717
3790
|
const lowerOutput = fullOutput.toLowerCase();
|
|
3791
|
+
const changesIndicators = [
|
|
3792
|
+
"check mode: schema changes detected (not applied)",
|
|
3793
|
+
"schema changes: detected"
|
|
3794
|
+
];
|
|
3795
|
+
if (changesIndicators.some((indicator) => lowerOutput.includes(indicator))) {
|
|
3796
|
+
return "changes";
|
|
3797
|
+
}
|
|
3718
3798
|
const noChangesIndicators = [
|
|
3799
|
+
"check mode: all changes are for idempotent-managed objects (nothing to apply)",
|
|
3800
|
+
"schema changes: none",
|
|
3719
3801
|
"no schema changes detected",
|
|
3720
|
-
// From actors.ts line 969
|
|
3721
|
-
"no schema changes",
|
|
3722
|
-
// Shorter variant
|
|
3723
3802
|
"already in sync",
|
|
3724
|
-
"nothing to apply",
|
|
3725
3803
|
"schemas are up to date",
|
|
3726
|
-
"
|
|
3727
|
-
// From db-apply.ts line 150
|
|
3728
|
-
"no changes",
|
|
3729
|
-
// From actors.ts line 578, 968
|
|
3730
|
-
"in sync"
|
|
3731
|
-
// From db-sync.ts line 80
|
|
3804
|
+
"nothing to apply"
|
|
3732
3805
|
];
|
|
3733
|
-
|
|
3806
|
+
if (noChangesIndicators.some((indicator) => lowerOutput.includes(indicator))) {
|
|
3807
|
+
return "no-changes";
|
|
3808
|
+
}
|
|
3809
|
+
return "unknown";
|
|
3734
3810
|
}
|
|
3735
3811
|
function isCommandSuccess(exitCode, fullOutput) {
|
|
3736
3812
|
return exitCode === 0 || fullOutput.includes("check complete") || fullOutput.includes("Check Summary");
|
|
@@ -3786,6 +3862,39 @@ function buildErrorResult(error, databaseUrl) {
|
|
|
3786
3862
|
}
|
|
3787
3863
|
};
|
|
3788
3864
|
}
|
|
3865
|
+
function buildPreviewFromOutput(fullOutput, repoRoot, productionUrl) {
|
|
3866
|
+
const lines = fullOutput.split("\n");
|
|
3867
|
+
const planSql = extractSqlFromLines(lines) || extractSqlFromSchemaChanges(fullOutput);
|
|
3868
|
+
const hazardDetails = extractHazardsWithContext(fullOutput, repoRoot);
|
|
3869
|
+
const hazards = hazardDetails.map((h) => `${h.type}: ${h.message}`);
|
|
3870
|
+
const schemaChangeState = detectSchemaChangeState(fullOutput);
|
|
3871
|
+
const hasSqlPlan = planSql !== null && planSql.trim().length > 0;
|
|
3872
|
+
const hasHazards = hazards.length > 0;
|
|
3873
|
+
const hasChanges = schemaChangeState === "changes" ? true : schemaChangeState === "no-changes" ? false : hasSqlPlan || hasHazards;
|
|
3874
|
+
const effectivePlanSql = hasChanges ? planSql?.substring(0, 5e3) || null : null;
|
|
3875
|
+
const rawError = extractErrorMessage(fullOutput);
|
|
3876
|
+
return {
|
|
3877
|
+
planSql: effectivePlanSql,
|
|
3878
|
+
hazards,
|
|
3879
|
+
hazardDetails: hazardDetails.length > 0 ? hazardDetails : void 0,
|
|
3880
|
+
hasChanges,
|
|
3881
|
+
error: enhanceConnectionError(rawError, productionUrl)
|
|
3882
|
+
};
|
|
3883
|
+
}
|
|
3884
|
+
function buildSuccessPreviewResult(exitCode, fullOutput, repoRoot, productionUrl) {
|
|
3885
|
+
const isSuccess = isCommandSuccess(exitCode, fullOutput);
|
|
3886
|
+
const preview = buildPreviewFromOutput(fullOutput, repoRoot, productionUrl);
|
|
3887
|
+
return {
|
|
3888
|
+
preview: {
|
|
3889
|
+
executed: true,
|
|
3890
|
+
planSql: preview.planSql,
|
|
3891
|
+
hazards: preview.hazards,
|
|
3892
|
+
hazardDetails: preview.hazardDetails,
|
|
3893
|
+
hasChanges: preview.hasChanges,
|
|
3894
|
+
error: isSuccess ? null : preview.error
|
|
3895
|
+
}
|
|
3896
|
+
};
|
|
3897
|
+
}
|
|
3789
3898
|
var productionPreviewActor = fromPromise(
|
|
3790
3899
|
async ({ input }) => {
|
|
3791
3900
|
const { repoRoot, tmpDir, shouldExecute } = input;
|
|
@@ -3810,28 +3919,7 @@ var productionPreviewActor = fromPromise(
|
|
|
3810
3919
|
const fullOutput = `${stdout}
|
|
3811
3920
|
${stderr}`;
|
|
3812
3921
|
await writeLogFile(logFile, fullOutput);
|
|
3813
|
-
|
|
3814
|
-
const planSql = extractSqlFromLines(lines) || extractSqlFromSchemaChanges(fullOutput);
|
|
3815
|
-
const hazardDetails = extractHazardsWithContext(fullOutput, repoRoot);
|
|
3816
|
-
const hazards = hazardDetails.map((h) => `${h.type}: ${h.message}`);
|
|
3817
|
-
const hasNoChangesIndicator = detectNoChanges(fullOutput);
|
|
3818
|
-
const hasSqlPlan = planSql !== null && planSql.trim().length > 0;
|
|
3819
|
-
const hasHazards = hazards.length > 0;
|
|
3820
|
-
const hasChanges = hasNoChangesIndicator ? false : hasSqlPlan || hasHazards;
|
|
3821
|
-
const effectivePlanSql = hasChanges ? planSql?.substring(0, 5e3) || null : null;
|
|
3822
|
-
const isSuccess = isCommandSuccess(result.exitCode, fullOutput);
|
|
3823
|
-
const rawError = !isSuccess ? extractErrorMessage(fullOutput) : null;
|
|
3824
|
-
const errorMessage = rawError ? enhanceConnectionError(rawError, productionUrl) : null;
|
|
3825
|
-
return {
|
|
3826
|
-
preview: {
|
|
3827
|
-
executed: true,
|
|
3828
|
-
planSql: effectivePlanSql,
|
|
3829
|
-
hazards,
|
|
3830
|
-
hazardDetails: hazardDetails.length > 0 ? hazardDetails : void 0,
|
|
3831
|
-
hasChanges,
|
|
3832
|
-
error: errorMessage
|
|
3833
|
-
}
|
|
3834
|
-
};
|
|
3922
|
+
return buildSuccessPreviewResult(result.exitCode, fullOutput, repoRoot, productionUrl);
|
|
3835
3923
|
} catch (error) {
|
|
3836
3924
|
return buildErrorResult(error, productionUrl);
|
|
3837
3925
|
}
|
|
@@ -5783,6 +5871,9 @@ function getLayersForCorePhase(selectedLayers, mode) {
|
|
|
5783
5871
|
function hasE2ELayer(selectedLayers) {
|
|
5784
5872
|
return selectedLayers.includes(E2E_LAYER);
|
|
5785
5873
|
}
|
|
5874
|
+
function shouldReuseCiReferenceStats(context) {
|
|
5875
|
+
return context.mode !== "ci-local" && context.schemaApplied && context.schemaDrift !== null && context.schemaDrift?.hasDrift === false;
|
|
5876
|
+
}
|
|
5786
5877
|
function mergeLayerResults(coreResults, e2eResults) {
|
|
5787
5878
|
return { ...coreResults, ...e2eResults };
|
|
5788
5879
|
}
|
|
@@ -5879,7 +5970,7 @@ init_esm_shims();
|
|
|
5879
5970
|
var CI_STEPS = [
|
|
5880
5971
|
{ step: "setup", label: "\u74B0\u5883\u30BB\u30C3\u30C8\u30A2\u30C3\u30D7", detail: "\u30EA\u30DD\u30B8\u30C8\u30EA\u691C\u51FA\u3001Supabase\u8D77\u52D5" },
|
|
5881
5972
|
{ step: "syncSchema", label: "\u30B9\u30AD\u30FC\u30DE\u540C\u671F", detail: "pg-schema-diff \u2192 CI DB" },
|
|
5882
|
-
{ step: "applySeeds", label: "\u30B7\u30FC\u30C9\u9069\u7528", detail: "ci.sql +
|
|
5973
|
+
{ step: "applySeeds", label: "\u30B7\u30FC\u30C9\u9069\u7528", detail: "ci.sql + prerequisite seeds" },
|
|
5883
5974
|
{ step: "staticChecks", label: "\u9759\u7684\u30C1\u30A7\u30C3\u30AF", detail: "\u578B\u30C1\u30A7\u30C3\u30AF \u2225 lint" },
|
|
5884
5975
|
{ step: "build", label: "\u30D3\u30EB\u30C9", detail: "build \u2225 playwright install" },
|
|
5885
5976
|
{ step: "runTests", label: "\u30C6\u30B9\u30C8\u5B9F\u884C", detail: "L0-L3 (\u30D6\u30ED\u30C3\u30AD\u30F3\u30B0) \u2192 L4 (E2E)" },
|
|
@@ -6043,11 +6134,123 @@ function formatSkippedLayers(layerSkipReasons, originalSelectedLayers) {
|
|
|
6043
6134
|
return lines;
|
|
6044
6135
|
}
|
|
6045
6136
|
|
|
6046
|
-
// src/commands/ci/machine/formatters/sections/schema-
|
|
6137
|
+
// src/commands/ci/machine/formatters/sections/production-schema-status.ts
|
|
6047
6138
|
init_esm_shims();
|
|
6139
|
+
var COUNT_FIELDS = [
|
|
6140
|
+
"tables",
|
|
6141
|
+
"functions",
|
|
6142
|
+
"policies",
|
|
6143
|
+
"indexes",
|
|
6144
|
+
"triggers"
|
|
6145
|
+
];
|
|
6048
6146
|
function emptyStats2() {
|
|
6049
6147
|
return { tables: 0, functions: 0, policies: 0, indexes: 0, triggers: 0 };
|
|
6050
6148
|
}
|
|
6149
|
+
function findExpectedCountDrift(schemaName, field, expectedDrift) {
|
|
6150
|
+
return expectedDrift.find(
|
|
6151
|
+
(entry) => entry.field === field && (entry.schemas === void 0 || entry.schemas.includes(schemaName))
|
|
6152
|
+
) ?? null;
|
|
6153
|
+
}
|
|
6154
|
+
function buildCountDiffs(schemaStats, expectedDrift) {
|
|
6155
|
+
if (!schemaStats?.local || !schemaStats.production) return [];
|
|
6156
|
+
if (schemaStats.local.available === false || schemaStats.production.available === false) {
|
|
6157
|
+
return [];
|
|
6158
|
+
}
|
|
6159
|
+
const schemaNames = /* @__PURE__ */ new Set([
|
|
6160
|
+
...Object.keys(schemaStats.local.schemas),
|
|
6161
|
+
...Object.keys(schemaStats.production.schemas)
|
|
6162
|
+
]);
|
|
6163
|
+
const diffs = [];
|
|
6164
|
+
for (const schemaName of Array.from(schemaNames).sort()) {
|
|
6165
|
+
const reference = schemaStats.local.schemas[schemaName] ?? emptyStats2();
|
|
6166
|
+
const production = schemaStats.production.schemas[schemaName] ?? emptyStats2();
|
|
6167
|
+
for (const field of COUNT_FIELDS) {
|
|
6168
|
+
if (reference[field] === production[field]) continue;
|
|
6169
|
+
const expected = findExpectedCountDrift(schemaName, field, expectedDrift);
|
|
6170
|
+
diffs.push({
|
|
6171
|
+
schema: schemaName,
|
|
6172
|
+
field,
|
|
6173
|
+
value: production[field],
|
|
6174
|
+
reference: reference[field],
|
|
6175
|
+
delta: production[field] - reference[field],
|
|
6176
|
+
expected: expected !== null,
|
|
6177
|
+
reason: expected?.reason
|
|
6178
|
+
});
|
|
6179
|
+
}
|
|
6180
|
+
}
|
|
6181
|
+
return diffs;
|
|
6182
|
+
}
|
|
6183
|
+
function areIndexDiffsCoveredByExpectedDrift(indexDiff, countDiffs) {
|
|
6184
|
+
if (!indexDiff || !hasIndexDiff(indexDiff)) return false;
|
|
6185
|
+
const touchedSchemas = /* @__PURE__ */ new Set([
|
|
6186
|
+
...indexDiff.missing.map((index) => index.schema),
|
|
6187
|
+
...indexDiff.extra.map((index) => index.schema)
|
|
6188
|
+
]);
|
|
6189
|
+
if (touchedSchemas.size === 0) return false;
|
|
6190
|
+
const expectedIndexSchemas = new Set(
|
|
6191
|
+
countDiffs.filter((diff) => diff.field === "indexes" && diff.expected).map((diff) => diff.schema)
|
|
6192
|
+
);
|
|
6193
|
+
const unexpectedIndexSchemas = new Set(
|
|
6194
|
+
countDiffs.filter((diff) => diff.field === "indexes" && !diff.expected).map((diff) => diff.schema)
|
|
6195
|
+
);
|
|
6196
|
+
for (const schema of touchedSchemas) {
|
|
6197
|
+
if (unexpectedIndexSchemas.has(schema)) return false;
|
|
6198
|
+
if (!expectedIndexSchemas.has(schema)) return false;
|
|
6199
|
+
}
|
|
6200
|
+
return true;
|
|
6201
|
+
}
|
|
6202
|
+
function buildIndexDiff(schemaStats) {
|
|
6203
|
+
if (!schemaStats?.local || !schemaStats.production) return null;
|
|
6204
|
+
if (schemaStats.local.available === false || schemaStats.production.available === false) {
|
|
6205
|
+
return null;
|
|
6206
|
+
}
|
|
6207
|
+
return compareIndexLists(schemaStats.local, schemaStats.production);
|
|
6208
|
+
}
|
|
6209
|
+
function buildSemanticDiff(schemaStats) {
|
|
6210
|
+
const referenceSnapshot = schemaStats?.local?.canonicalSnapshot;
|
|
6211
|
+
const productionSnapshot = schemaStats?.production?.canonicalSnapshot;
|
|
6212
|
+
if (!referenceSnapshot || !productionSnapshot) return null;
|
|
6213
|
+
if (referenceSnapshot.available !== true || productionSnapshot.available !== true) return null;
|
|
6214
|
+
return compareCanonicalSnapshots(referenceSnapshot, productionSnapshot);
|
|
6215
|
+
}
|
|
6216
|
+
function getProductionSchemaSignals(productionPreview, schemaStats, expectedDrift = []) {
|
|
6217
|
+
const previewHasChanges = productionPreview?.hasChanges === true;
|
|
6218
|
+
const indexDiff = buildIndexDiff(schemaStats);
|
|
6219
|
+
const semanticDiff = buildSemanticDiff(schemaStats);
|
|
6220
|
+
const countDiffs = buildCountDiffs(schemaStats, expectedDrift);
|
|
6221
|
+
const hasIndexChanges = indexDiff ? hasIndexDiff(indexDiff) : false;
|
|
6222
|
+
const hasUnexpectedIndexChanges = hasIndexChanges && !areIndexDiffsCoveredByExpectedDrift(indexDiff, countDiffs);
|
|
6223
|
+
const hasSemanticChanges = semanticDiff ? hasCanonicalChanges(semanticDiff) : false;
|
|
6224
|
+
const hasCountChanges = countDiffs.length > 0;
|
|
6225
|
+
const hasUnexpectedCountChanges = countDiffs.some((diff) => !diff.expected);
|
|
6226
|
+
const knownDriftReasons = Array.from(
|
|
6227
|
+
new Set(countDiffs.filter((diff) => diff.expected).flatMap((diff) => diff.reason ?? []))
|
|
6228
|
+
);
|
|
6229
|
+
const hasKnownDriftOnly = hasCountChanges && !previewHasChanges && !hasUnexpectedCountChanges && !hasUnexpectedIndexChanges && !hasSemanticChanges;
|
|
6230
|
+
const requiresDeploy = previewHasChanges || hasUnexpectedIndexChanges || hasSemanticChanges || hasUnexpectedCountChanges;
|
|
6231
|
+
return {
|
|
6232
|
+
previewHasChanges,
|
|
6233
|
+
indexDiff,
|
|
6234
|
+
semanticDiff,
|
|
6235
|
+
countDiffs,
|
|
6236
|
+
hasIndexChanges,
|
|
6237
|
+
hasUnexpectedIndexChanges,
|
|
6238
|
+
hasSemanticChanges,
|
|
6239
|
+
hasCountChanges,
|
|
6240
|
+
hasUnexpectedCountChanges,
|
|
6241
|
+
hasKnownDriftOnly,
|
|
6242
|
+
knownDriftReasons,
|
|
6243
|
+
requiresDeploy,
|
|
6244
|
+
previewMissedChanges: !previewHasChanges && (hasUnexpectedIndexChanges || hasSemanticChanges || hasUnexpectedCountChanges),
|
|
6245
|
+
previewOnlyChanges: previewHasChanges && !hasUnexpectedIndexChanges && !hasSemanticChanges && !hasUnexpectedCountChanges
|
|
6246
|
+
};
|
|
6247
|
+
}
|
|
6248
|
+
|
|
6249
|
+
// src/commands/ci/machine/formatters/sections/schema-matrix.ts
|
|
6250
|
+
init_esm_shims();
|
|
6251
|
+
function emptyStats3() {
|
|
6252
|
+
return { tables: 0, functions: 0, policies: 0, indexes: 0, triggers: 0 };
|
|
6253
|
+
}
|
|
6051
6254
|
function isStatsAvailable(stats) {
|
|
6052
6255
|
return stats != null && stats.available !== false;
|
|
6053
6256
|
}
|
|
@@ -6127,8 +6330,8 @@ function getSortedActiveSchemas(schemaStats) {
|
|
|
6127
6330
|
function generateSchemaTableRows(schemaStats, sortedSchemas, hasProduction, expectedDrift, ciDiffBySchema, productionDiffBySchema) {
|
|
6128
6331
|
const lines = [];
|
|
6129
6332
|
for (const schemaName of sortedSchemas) {
|
|
6130
|
-
const localStats = schemaStats.local.schemas[schemaName] ??
|
|
6131
|
-
const ciStats = schemaStats.ci.schemas[schemaName] ??
|
|
6333
|
+
const localStats = schemaStats.local.schemas[schemaName] ?? emptyStats3();
|
|
6334
|
+
const ciStats = schemaStats.ci.schemas[schemaName] ?? emptyStats3();
|
|
6132
6335
|
const prodStats = schemaStats.production?.schemas[schemaName] ?? null;
|
|
6133
6336
|
const localCell = formatReferenceCell(localStats, isStatsAvailable(schemaStats.local));
|
|
6134
6337
|
const ciCell = formatCiCellWithDiff(
|
|
@@ -6285,7 +6488,7 @@ function generateExpectedDriftFootnotes(schemaStats, sortedSchemas, expectedDrif
|
|
|
6285
6488
|
if (!isStatsAvailable(schemaStats.local) || !isStatsAvailable(schemaStats.production)) return [];
|
|
6286
6489
|
const allReasons = /* @__PURE__ */ new Set();
|
|
6287
6490
|
for (const schemaName of sortedSchemas) {
|
|
6288
|
-
const localStats = schemaStats.local.schemas[schemaName] ??
|
|
6491
|
+
const localStats = schemaStats.local.schemas[schemaName] ?? emptyStats3();
|
|
6289
6492
|
const prodStats = schemaStats.production.schemas[schemaName];
|
|
6290
6493
|
if (!prodStats || !hasDisplayedStatsDiff(prodStats, localStats)) continue;
|
|
6291
6494
|
const fieldDiffs = getFieldDiffs(prodStats, localStats);
|
|
@@ -6507,23 +6710,6 @@ function classifyPreviewError(error) {
|
|
|
6507
6710
|
}
|
|
6508
6711
|
return { label: "\u672C\u756A\u30D7\u30EC\u30D3\u30E5\u30FC\u5931\u6557", guidance: "" };
|
|
6509
6712
|
}
|
|
6510
|
-
function generateIndexDiffWarning(schemaStats) {
|
|
6511
|
-
if (!schemaStats?.local || !schemaStats?.production) return [];
|
|
6512
|
-
if (schemaStats.local.available === false || schemaStats.production.available === false)
|
|
6513
|
-
return [];
|
|
6514
|
-
const indexDiff = compareIndexLists(schemaStats.local, schemaStats.production);
|
|
6515
|
-
if (!hasIndexDiff(indexDiff)) return [];
|
|
6516
|
-
const diffParts = [];
|
|
6517
|
-
if (indexDiff.missing.length > 0) diffParts.push(`${indexDiff.missing.length}\u4EF6\u4E0D\u8DB3`);
|
|
6518
|
-
if (indexDiff.extra.length > 0) diffParts.push(`${indexDiff.extra.length}\u4EF6\u4F59\u5206`);
|
|
6519
|
-
return [
|
|
6520
|
-
`> \u26A0\uFE0F **\u30A4\u30F3\u30C7\u30C3\u30AF\u30B9\u5DEE\u5206\u691C\u51FA** (${diffParts.join(", ")})`,
|
|
6521
|
-
">",
|
|
6522
|
-
"> pg-schema-diff \u3067\u306F\u691C\u51FA\u3055\u308C\u307E\u305B\u3093\u3067\u3057\u305F\u304C\u3001\u30A4\u30F3\u30C7\u30C3\u30AF\u30B9\u306E\u5DEE\u5206\u304C\u3042\u308A\u307E\u3059\u3002",
|
|
6523
|
-
"> \u30B9\u30AD\u30FC\u30DE\u30DE\u30C8\u30EA\u30C3\u30AF\u30B9\u306E\u8A73\u7D30\u3092\u78BA\u8A8D\u3057\u3066\u304F\u3060\u3055\u3044\u3002",
|
|
6524
|
-
""
|
|
6525
|
-
];
|
|
6526
|
-
}
|
|
6527
6713
|
function generatePreviewError(error) {
|
|
6528
6714
|
const { label, guidance } = classifyPreviewError(error);
|
|
6529
6715
|
const lines = [`> \u26A0\uFE0F **${label}**`];
|
|
@@ -6544,7 +6730,82 @@ function generatePreviewError(error) {
|
|
|
6544
6730
|
);
|
|
6545
6731
|
return lines;
|
|
6546
6732
|
}
|
|
6547
|
-
function
|
|
6733
|
+
function generatePreviewChangesDetectedSection(prodPreview, previewOnlyChanges) {
|
|
6734
|
+
const lines = [
|
|
6735
|
+
"> \u{1F536} **\u30B9\u30AD\u30FC\u30DE\u5909\u66F4\u691C\u51FA - \u672C\u756A\u30C7\u30D7\u30ED\u30A4\u304C\u5FC5\u8981\u3067\u3059**",
|
|
6736
|
+
">",
|
|
6737
|
+
"> \u3053\u306EPR\u3092\u30DE\u30FC\u30B8\u3057\u305F\u5F8C\u3001\u672C\u756A\u30C7\u30D7\u30ED\u30A4\u30EF\u30FC\u30AF\u30D5\u30ED\u30FC\u3092\u5B9F\u884C\u3057\u3066\u30B9\u30AD\u30FC\u30DE\u5909\u66F4\u3092\u9069\u7528\u3057\u3066\u304F\u3060\u3055\u3044\u3002",
|
|
6738
|
+
""
|
|
6739
|
+
];
|
|
6740
|
+
if (previewOnlyChanges) {
|
|
6741
|
+
lines.push(
|
|
6742
|
+
"> \u88DC\u8DB3: `Prod semantic diff` / count diff / index diff \u3067\u306F\u5DEE\u5206\u304C\u306A\u304F\u3001`production --check` \u306E\u307F\u304C\u5909\u66F4\u3092\u691C\u51FA\u3057\u307E\u3057\u305F\u3002",
|
|
6743
|
+
"> \u3053\u308C\u306F grants / ACL / ownership \u306A\u3069 canonical semantic diff \u306E\u5BFE\u8C61\u5916\u5DEE\u5206\u3067\u3042\u308B\u53EF\u80FD\u6027\u304C\u3042\u308A\u307E\u3059\u3002",
|
|
6744
|
+
""
|
|
6745
|
+
);
|
|
6746
|
+
}
|
|
6747
|
+
if (prodPreview.planSql) {
|
|
6748
|
+
lines.push(...generatePreviewDetails(prodPreview));
|
|
6749
|
+
}
|
|
6750
|
+
return lines;
|
|
6751
|
+
}
|
|
6752
|
+
function buildComparisonMismatchReasons(schemaStats, expectedDrift) {
|
|
6753
|
+
const signals = getProductionSchemaSignals(null, schemaStats, expectedDrift);
|
|
6754
|
+
const reasons = [];
|
|
6755
|
+
if (signals.hasSemanticChanges && signals.semanticDiff) {
|
|
6756
|
+
const diff = signals.semanticDiff;
|
|
6757
|
+
reasons.push(
|
|
6758
|
+
`semantic diff: changed ${diff.changed.length}, missing ${diff.missing.length}, extra ${diff.extra.length}, renamed ${diff.renamed.length}`
|
|
6759
|
+
);
|
|
6760
|
+
}
|
|
6761
|
+
if (signals.hasIndexChanges && signals.indexDiff) {
|
|
6762
|
+
reasons.push(
|
|
6763
|
+
`index diff: missing ${signals.indexDiff.missing.length}, extra ${signals.indexDiff.extra.length}`
|
|
6764
|
+
);
|
|
6765
|
+
}
|
|
6766
|
+
if (signals.hasUnexpectedCountChanges) {
|
|
6767
|
+
const unexpectedDiffs = signals.countDiffs.filter((diff) => !diff.expected);
|
|
6768
|
+
const formatted = unexpectedDiffs.slice(0, 6).map(
|
|
6769
|
+
(diff) => `${diff.schema}.${diff.field} ${diff.reference} -> ${diff.value} (${diff.delta >= 0 ? "+" : ""}${diff.delta})`
|
|
6770
|
+
);
|
|
6771
|
+
reasons.push(`count diff: ${formatted.join(", ")}${unexpectedDiffs.length > 6 ? ", ..." : ""}`);
|
|
6772
|
+
}
|
|
6773
|
+
return reasons;
|
|
6774
|
+
}
|
|
6775
|
+
function generateComparisonMismatchSection(schemaStats, expectedDrift) {
|
|
6776
|
+
const reasons = buildComparisonMismatchReasons(schemaStats, expectedDrift);
|
|
6777
|
+
const lines = [
|
|
6778
|
+
"> \u{1F536} **\u672C\u756ADB\u3068\u306E\u5DEE\u5206\u691C\u51FA - \u672C\u756A\u30C7\u30D7\u30ED\u30A4\u304C\u5FC5\u8981\u3067\u3059**",
|
|
6779
|
+
">",
|
|
6780
|
+
"> `runa db apply production --check` \u306F\u5909\u66F4\u306A\u3057\u5224\u5B9A\u3067\u3057\u305F\u304C\u3001Reference \u2194 Prod \u306E\u6BD4\u8F03\u3067\u5DEE\u5206\u3092\u691C\u51FA\u3057\u307E\u3057\u305F\u3002",
|
|
6781
|
+
"> \u30B9\u30AD\u30FC\u30DE\u30DE\u30C8\u30EA\u30C3\u30AF\u30B9\u306E `Prod semantic diff` \u3068\u8A73\u7D30\u30ED\u30B0\u3092\u78BA\u8A8D\u3057\u3066\u304F\u3060\u3055\u3044\u3002",
|
|
6782
|
+
""
|
|
6783
|
+
];
|
|
6784
|
+
for (const reason of reasons) {
|
|
6785
|
+
lines.push(`> - ${reason}`);
|
|
6786
|
+
}
|
|
6787
|
+
if (reasons.length > 0) {
|
|
6788
|
+
lines.push("");
|
|
6789
|
+
}
|
|
6790
|
+
return lines;
|
|
6791
|
+
}
|
|
6792
|
+
function generateKnownDriftOnlySection(schemaStats, expectedDrift) {
|
|
6793
|
+
const signals = getProductionSchemaSignals(null, schemaStats, expectedDrift);
|
|
6794
|
+
const lines = [
|
|
6795
|
+
"**\u672C\u756A\u30D7\u30EC\u30D3\u30E5\u30FC**: \u2705 \u672C\u756A\u30C7\u30D7\u30ED\u30A4\u304C\u5FC5\u8981\u306A\u30B9\u30AD\u30FC\u30DE\u5909\u66F4\u306F\u3042\u308A\u307E\u305B\u3093",
|
|
6796
|
+
"",
|
|
6797
|
+
"> \u{1F4CB} **\u65E2\u77E5\u306E\u5DEE\u5206\u306E\u307F\u691C\u51FA**",
|
|
6798
|
+
">",
|
|
6799
|
+
"> Reference \u2194 Prod \u306B\u306F `expectedDrift` \u3067\u8A31\u5BB9\u6E08\u307F\u306E\u5DEE\u5206\u306E\u307F\u304C\u3042\u308A\u307E\u3059\u3002\u8FFD\u52A0\u306E db apply/deploy \u306F\u4E0D\u8981\u3067\u3059\u3002"
|
|
6800
|
+
];
|
|
6801
|
+
for (const reason of signals.knownDriftReasons) {
|
|
6802
|
+
lines.push(`> - ${reason}`);
|
|
6803
|
+
}
|
|
6804
|
+
lines.push("");
|
|
6805
|
+
return lines;
|
|
6806
|
+
}
|
|
6807
|
+
function generateProductionPreviewSection(prodPreview, schemaDrift, schemaStats, expectedDrift) {
|
|
6808
|
+
const signals = getProductionSchemaSignals(prodPreview, schemaStats, expectedDrift);
|
|
6548
6809
|
if (!prodPreview?.executed) {
|
|
6549
6810
|
if (schemaDrift?.gitDiff?.filesChanged?.length) {
|
|
6550
6811
|
return [
|
|
@@ -6557,20 +6818,15 @@ function generateProductionPreviewSection(prodPreview, schemaDrift, schemaStats)
|
|
|
6557
6818
|
return [];
|
|
6558
6819
|
}
|
|
6559
6820
|
if (prodPreview.error) return generatePreviewError(prodPreview.error);
|
|
6560
|
-
if (
|
|
6561
|
-
|
|
6562
|
-
|
|
6563
|
-
|
|
6564
|
-
|
|
6565
|
-
|
|
6566
|
-
|
|
6567
|
-
|
|
6568
|
-
lines.push(...generatePreviewDetails(prodPreview));
|
|
6569
|
-
}
|
|
6570
|
-
return lines;
|
|
6821
|
+
if (signals.previewHasChanges) {
|
|
6822
|
+
return generatePreviewChangesDetectedSection(prodPreview, signals.previewOnlyChanges);
|
|
6823
|
+
}
|
|
6824
|
+
if (signals.previewMissedChanges) {
|
|
6825
|
+
return generateComparisonMismatchSection(schemaStats, expectedDrift);
|
|
6826
|
+
}
|
|
6827
|
+
if (signals.hasKnownDriftOnly) {
|
|
6828
|
+
return generateKnownDriftOnlySection(schemaStats, expectedDrift);
|
|
6571
6829
|
}
|
|
6572
|
-
const indexWarning = generateIndexDiffWarning(schemaStats);
|
|
6573
|
-
if (indexWarning.length > 0) return indexWarning;
|
|
6574
6830
|
return ["**\u672C\u756A\u30D7\u30EC\u30D3\u30E5\u30FC**: \u2705 \u672C\u756A\u306B\u9069\u7528\u3059\u308B\u30B9\u30AD\u30FC\u30DE\u5909\u66F4\u306F\u3042\u308A\u307E\u305B\u3093", ""];
|
|
6575
6831
|
}
|
|
6576
6832
|
function shouldShowDeployButton(productionPreview, schemaDrift, hasSchemaChanges2) {
|
|
@@ -6579,18 +6835,19 @@ function shouldShowDeployButton(productionPreview, schemaDrift, hasSchemaChanges
|
|
|
6579
6835
|
if (!productionPreview?.executed && schemaDrift?.gitDiff?.filesChanged?.length) return true;
|
|
6580
6836
|
return false;
|
|
6581
6837
|
}
|
|
6582
|
-
function generateDeploySection(exitCode, layerResults, gitBranchName, productionPreview, schemaDrift, schemaStats, env) {
|
|
6838
|
+
function generateDeploySection(exitCode, layerResults, gitBranchName, productionPreview, schemaDrift, schemaStats, expectedDrift, env) {
|
|
6583
6839
|
if (!checkIfDeployable(exitCode, layerResults)) return [];
|
|
6584
6840
|
const deployWorkflowUrl = env.repository ? `${env.serverUrl}/${env.repository}/actions/workflows/deploy-db.yml` : null;
|
|
6585
|
-
const
|
|
6586
|
-
const
|
|
6587
|
-
const hasSchemaChanges2 = hasPgSchemaDiffChanges || hasIndexChanges;
|
|
6841
|
+
const signals = getProductionSchemaSignals(productionPreview, schemaStats, expectedDrift);
|
|
6842
|
+
const hasSchemaChanges2 = signals.requiresDeploy;
|
|
6588
6843
|
const headerEmoji = hasSchemaChanges2 ? "\u{1F6A8}" : "\u{1F680}";
|
|
6589
6844
|
const headerText = hasSchemaChanges2 ? "\u672C\u756A\u30C7\u30D7\u30ED\u30A4 (\u30B9\u30AD\u30FC\u30DE\u5909\u66F4\u3042\u308A!)" : "\u672C\u756A\u30C7\u30D7\u30ED\u30A4";
|
|
6590
6845
|
const lines = ["---", "", `### ${headerEmoji} ${headerText}`, ""];
|
|
6591
6846
|
if (exitCode !== 0)
|
|
6592
6847
|
lines.push("> **\u6CE8**: Layer 4 (E2E) \u306F\u5931\u6557\u3057\u307E\u3057\u305F\u304C\u3001\u30B9\u30AD\u30FC\u30DE\u5909\u66F4\u306F\u30C7\u30D7\u30ED\u30A4\u53EF\u80FD\u3067\u3059\u3002", "");
|
|
6593
|
-
lines.push(
|
|
6848
|
+
lines.push(
|
|
6849
|
+
...generateProductionPreviewSection(productionPreview, schemaDrift, schemaStats, expectedDrift)
|
|
6850
|
+
);
|
|
6594
6851
|
const showButton = shouldShowDeployButton(productionPreview, schemaDrift, hasSchemaChanges2);
|
|
6595
6852
|
if (deployWorkflowUrl && showButton) {
|
|
6596
6853
|
const buttonText = hasSchemaChanges2 ? "\u26A1 \u30B9\u30AD\u30FC\u30DE\u5909\u66F4\u3092\u672C\u756A\u306B\u30C7\u30D7\u30ED\u30A4" : "\u25B6\uFE0F \u672C\u756A\u306B\u30C7\u30D7\u30ED\u30A4";
|
|
@@ -6630,6 +6887,7 @@ function generateIntermediateCommentBody(input) {
|
|
|
6630
6887
|
productionPreview,
|
|
6631
6888
|
schemaDrift,
|
|
6632
6889
|
input.schemaStats ?? null,
|
|
6890
|
+
input.expectedDrift ?? [],
|
|
6633
6891
|
env
|
|
6634
6892
|
),
|
|
6635
6893
|
"<sub>\u{1F916} RUNA CI \u751F\u6210 (\u4E2D\u9593\u66F4\u65B0)</sub>"
|
|
@@ -6665,6 +6923,7 @@ function generateCommentBody(input) {
|
|
|
6665
6923
|
input.productionPreview,
|
|
6666
6924
|
schemaDrift,
|
|
6667
6925
|
input.schemaStats ?? null,
|
|
6926
|
+
input.expectedDrift ?? [],
|
|
6668
6927
|
env
|
|
6669
6928
|
),
|
|
6670
6929
|
"<sub>\u{1F916} RUNA CI \u751F\u6210</sub>"
|
|
@@ -6714,7 +6973,7 @@ function formatSyncSchemaDetail(schemaDrift) {
|
|
|
6714
6973
|
if (!hasGitChanges) {
|
|
6715
6974
|
const hasSqlContent = schemaDrift.beforeSql && schemaDrift.beforeSql.trim().length > 0;
|
|
6716
6975
|
if (hasSqlContent) {
|
|
6717
|
-
return "ci.sql +
|
|
6976
|
+
return "ci.sql + prerequisite seeds";
|
|
6718
6977
|
}
|
|
6719
6978
|
return "SQL\u30D5\u30A1\u30A4\u30EB\u5909\u66F4\u306A\u3057";
|
|
6720
6979
|
}
|
|
@@ -6754,7 +7013,7 @@ function formatRunTestsDetail(layerResults) {
|
|
|
6754
7013
|
}
|
|
6755
7014
|
var stepDetailHandlers = {
|
|
6756
7015
|
syncSchema: (_status, schemaDrift) => formatSyncSchemaDetail(schemaDrift),
|
|
6757
|
-
applySeeds: () => "ci.sql +
|
|
7016
|
+
applySeeds: () => "ci.sql + prerequisite seeds",
|
|
6758
7017
|
staticChecks: (status) => status === "passed" ? "\u578B\u30C1\u30A7\u30C3\u30AF \u2713 lint \u2713" : "\u578B\u30C1\u30A7\u30C3\u30AF \u2717 \u307E\u305F\u306F lint \u2717",
|
|
6759
7018
|
build: (status) => status === "passed" ? "\u30A2\u30D7\u30EA\u30D3\u30EB\u30C9\u5B8C\u4E86, Playwright\u6E96\u5099\u5B8C\u4E86" : "\u30D3\u30EB\u30C9\u5931\u6557",
|
|
6760
7019
|
runTests: (_status, _schemaDrift, layerResults) => formatRunTestsDetail(layerResults)
|
|
@@ -7182,7 +7441,7 @@ function createInitialContext(input) {
|
|
|
7182
7441
|
function createOutput(context) {
|
|
7183
7442
|
return {
|
|
7184
7443
|
mode: context.mode,
|
|
7185
|
-
status: context.error ? "
|
|
7444
|
+
status: context.exitCode === 0 && !context.error ? "success" : "failure",
|
|
7186
7445
|
repoKind: context.repoKind,
|
|
7187
7446
|
steps: {},
|
|
7188
7447
|
// Populated from summary
|
|
@@ -7751,10 +8010,17 @@ var ciMachine = setup({
|
|
|
7751
8010
|
databaseUrl: context.supabase?.databaseUrlRaw ?? "",
|
|
7752
8011
|
mode: context.mode
|
|
7753
8012
|
}),
|
|
7754
|
-
onDone:
|
|
7755
|
-
|
|
7756
|
-
|
|
7757
|
-
|
|
8013
|
+
onDone: [
|
|
8014
|
+
{
|
|
8015
|
+
guard: "isCiLocalMode",
|
|
8016
|
+
actions: assign({ seedsApplied: ({ event }) => event.output.applied }),
|
|
8017
|
+
target: "productionPreview"
|
|
8018
|
+
},
|
|
8019
|
+
{
|
|
8020
|
+
actions: assign({ seedsApplied: ({ event }) => event.output.applied }),
|
|
8021
|
+
target: "postSeedPr"
|
|
8022
|
+
}
|
|
8023
|
+
],
|
|
7758
8024
|
onError: {
|
|
7759
8025
|
// Seeds failure is CRITICAL - blocks CI (Layer 2/3 tests depend on seeds)
|
|
7760
8026
|
target: "failed",
|
|
@@ -7766,6 +8032,371 @@ var ciMachine = setup({
|
|
|
7766
8032
|
}
|
|
7767
8033
|
},
|
|
7768
8034
|
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
8035
|
+
// Post-Seed PR Phase
|
|
8036
|
+
// CI PR path only: run observability work in parallel with the critical build/test path
|
|
8037
|
+
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
8038
|
+
postSeedPr: {
|
|
8039
|
+
type: "parallel",
|
|
8040
|
+
meta: {
|
|
8041
|
+
e2e: {
|
|
8042
|
+
observable: "log",
|
|
8043
|
+
assertions: [{ type: "log", contains: "Database roles configured" }]
|
|
8044
|
+
}
|
|
8045
|
+
},
|
|
8046
|
+
states: {
|
|
8047
|
+
execution: {
|
|
8048
|
+
initial: "setupRoles",
|
|
8049
|
+
states: {
|
|
8050
|
+
setupRoles: {
|
|
8051
|
+
invoke: {
|
|
8052
|
+
src: "setupRoles",
|
|
8053
|
+
input: ({ context }) => createSetupRolesInput(context),
|
|
8054
|
+
onDone: {
|
|
8055
|
+
target: "staticChecks",
|
|
8056
|
+
actions: assign({
|
|
8057
|
+
rolesSetup: true,
|
|
8058
|
+
supabase: ({ context, event }) => mergeSetupRolesSupabase(context, event.output.appDatabaseUrl)
|
|
8059
|
+
})
|
|
8060
|
+
},
|
|
8061
|
+
onError: {
|
|
8062
|
+
target: "staticChecks",
|
|
8063
|
+
actions: assign({ rolesSetup: false })
|
|
8064
|
+
}
|
|
8065
|
+
}
|
|
8066
|
+
},
|
|
8067
|
+
staticChecks: {
|
|
8068
|
+
always: [{ guard: "shouldSkipStaticChecks", target: "buildAndPlaywright" }],
|
|
8069
|
+
invoke: {
|
|
8070
|
+
src: "staticChecks",
|
|
8071
|
+
input: ({ context }) => createStaticChecksInput(context),
|
|
8072
|
+
onDone: [
|
|
8073
|
+
{
|
|
8074
|
+
guard: ({ event }) => !event.output.passed,
|
|
8075
|
+
target: "failed",
|
|
8076
|
+
actions: assign({
|
|
8077
|
+
staticChecksPassed: false,
|
|
8078
|
+
error: ({ event }) => event.output.error ?? "Static checks failed"
|
|
8079
|
+
})
|
|
8080
|
+
},
|
|
8081
|
+
{
|
|
8082
|
+
target: "buildAndPlaywright",
|
|
8083
|
+
actions: assign({ staticChecksPassed: true })
|
|
8084
|
+
}
|
|
8085
|
+
],
|
|
8086
|
+
onError: {
|
|
8087
|
+
target: "failed",
|
|
8088
|
+
actions: assign({
|
|
8089
|
+
staticChecksPassed: false,
|
|
8090
|
+
error: ({ event }) => extractErrorMessage2(event, "Static checks failed")
|
|
8091
|
+
})
|
|
8092
|
+
}
|
|
8093
|
+
}
|
|
8094
|
+
},
|
|
8095
|
+
buildAndPlaywright: {
|
|
8096
|
+
always: [{ guard: "shouldSkipBuild", target: "appStart" }],
|
|
8097
|
+
invoke: {
|
|
8098
|
+
src: "buildAndPlaywright",
|
|
8099
|
+
input: ({ context }) => ({
|
|
8100
|
+
...createBuildAndPlaywrightInput(context),
|
|
8101
|
+
skipPlaywright: shouldSkipPlaywrightInstall(context)
|
|
8102
|
+
}),
|
|
8103
|
+
onDone: [
|
|
8104
|
+
{
|
|
8105
|
+
guard: ({ event }) => !event.output.buildPassed,
|
|
8106
|
+
target: "failed",
|
|
8107
|
+
actions: assign({
|
|
8108
|
+
appBuildPassed: false,
|
|
8109
|
+
playwrightInstalled: ({ event }) => event.output.playwrightInstalled,
|
|
8110
|
+
manifestGenerated: ({ event }) => event.output.manifestGenerated,
|
|
8111
|
+
error: ({ event }) => event.output.buildError ?? "App build failed"
|
|
8112
|
+
})
|
|
8113
|
+
},
|
|
8114
|
+
{
|
|
8115
|
+
target: "appStart",
|
|
8116
|
+
actions: assign({
|
|
8117
|
+
appBuildPassed: true,
|
|
8118
|
+
playwrightInstalled: ({ event }) => event.output.playwrightInstalled,
|
|
8119
|
+
manifestGenerated: ({ event }) => event.output.manifestGenerated
|
|
8120
|
+
})
|
|
8121
|
+
}
|
|
8122
|
+
],
|
|
8123
|
+
onError: {
|
|
8124
|
+
target: "failed",
|
|
8125
|
+
actions: assign({
|
|
8126
|
+
appBuildPassed: false,
|
|
8127
|
+
error: ({ event }) => extractErrorMessage2(event, "App build failed")
|
|
8128
|
+
})
|
|
8129
|
+
}
|
|
8130
|
+
}
|
|
8131
|
+
},
|
|
8132
|
+
appStart: {
|
|
8133
|
+
always: [{ guard: "shouldSkipAppStart", target: "capabilities" }],
|
|
8134
|
+
invoke: {
|
|
8135
|
+
src: "appStart",
|
|
8136
|
+
input: ({ context }) => createAppStartInput(context),
|
|
8137
|
+
onDone: [
|
|
8138
|
+
{
|
|
8139
|
+
guard: ({ event }) => !event.output.started,
|
|
8140
|
+
target: "failed",
|
|
8141
|
+
actions: assign({
|
|
8142
|
+
appStarted: false,
|
|
8143
|
+
error: ({ event }) => event.output.error ?? "App start failed"
|
|
8144
|
+
})
|
|
8145
|
+
},
|
|
8146
|
+
{
|
|
8147
|
+
target: "capabilities",
|
|
8148
|
+
actions: assign({
|
|
8149
|
+
appStarted: true,
|
|
8150
|
+
appPid: ({ event }) => event.output.pid ?? null,
|
|
8151
|
+
baseUrl: ({ event }) => event.output.baseUrl ?? null
|
|
8152
|
+
})
|
|
8153
|
+
}
|
|
8154
|
+
],
|
|
8155
|
+
onError: {
|
|
8156
|
+
target: "failed",
|
|
8157
|
+
actions: assign({
|
|
8158
|
+
appStarted: false,
|
|
8159
|
+
error: ({ event }) => extractErrorMessage2(event, "App start failed")
|
|
8160
|
+
})
|
|
8161
|
+
}
|
|
8162
|
+
}
|
|
8163
|
+
},
|
|
8164
|
+
capabilities: {
|
|
8165
|
+
invoke: {
|
|
8166
|
+
src: "capabilities",
|
|
8167
|
+
input: ({ context }) => createCapabilitiesInput(context),
|
|
8168
|
+
onDone: {
|
|
8169
|
+
target: "runCoreTests",
|
|
8170
|
+
actions: assign({
|
|
8171
|
+
capabilities: ({ event }) => event.output.detected?.capabilities ?? [],
|
|
8172
|
+
selectedLayers: ({ context, event }) => filterRunnableLayers(context.selectedLayers, event.output.layerContent),
|
|
8173
|
+
layerSkipReasons: ({ event }) => extractLayerSkipReasons(event.output.layerContent)
|
|
8174
|
+
})
|
|
8175
|
+
},
|
|
8176
|
+
onError: {
|
|
8177
|
+
target: "runCoreTests"
|
|
8178
|
+
}
|
|
8179
|
+
}
|
|
8180
|
+
},
|
|
8181
|
+
runCoreTests: {
|
|
8182
|
+
invoke: {
|
|
8183
|
+
src: "runLayers",
|
|
8184
|
+
input: ({ context }) => createRunCoreTestsInput(context),
|
|
8185
|
+
onDone: [
|
|
8186
|
+
{
|
|
8187
|
+
guard: ({ event }) => {
|
|
8188
|
+
const failedLayers = event.output.failedLayers ?? [];
|
|
8189
|
+
return failedLayers.length > 0;
|
|
8190
|
+
},
|
|
8191
|
+
target: "coreTestsFailed",
|
|
8192
|
+
actions: assign({
|
|
8193
|
+
testsRun: true,
|
|
8194
|
+
layerResults: ({ event }) => deriveCoreLayerResults(event.output)
|
|
8195
|
+
})
|
|
8196
|
+
},
|
|
8197
|
+
{
|
|
8198
|
+
target: "coreTestsComplete",
|
|
8199
|
+
actions: assign({
|
|
8200
|
+
testsRun: true,
|
|
8201
|
+
layerResults: ({ context, event }) => {
|
|
8202
|
+
const nextResults = convertLayerResults(event.output.results);
|
|
8203
|
+
return mergeLayerResults(context.layerResults, nextResults);
|
|
8204
|
+
}
|
|
8205
|
+
})
|
|
8206
|
+
}
|
|
8207
|
+
],
|
|
8208
|
+
onError: {
|
|
8209
|
+
target: "coreTestsFailed",
|
|
8210
|
+
actions: assign({
|
|
8211
|
+
testsRun: true,
|
|
8212
|
+
error: ({ event }) => extractErrorMessage2(event, "Core tests failed")
|
|
8213
|
+
})
|
|
8214
|
+
}
|
|
8215
|
+
}
|
|
8216
|
+
},
|
|
8217
|
+
coreTestsComplete: {
|
|
8218
|
+
always: [
|
|
8219
|
+
{ guard: ({ context }) => hasE2ELayer(context.selectedLayers), target: "e2ePhase" },
|
|
8220
|
+
{
|
|
8221
|
+
target: "done",
|
|
8222
|
+
actions: assign({
|
|
8223
|
+
exitCode: ({ context }) => computeExitCodeFromLayerResults(context.layerResults)
|
|
8224
|
+
})
|
|
8225
|
+
}
|
|
8226
|
+
]
|
|
8227
|
+
},
|
|
8228
|
+
coreTestsFailed: {
|
|
8229
|
+
entry: assign({
|
|
8230
|
+
exitCode: ({ context }) => {
|
|
8231
|
+
if (Object.keys(context.layerResults).length === 0) {
|
|
8232
|
+
return 1;
|
|
8233
|
+
}
|
|
8234
|
+
return computeExitCodeFromLayerResults(context.layerResults);
|
|
8235
|
+
}
|
|
8236
|
+
}),
|
|
8237
|
+
always: [{ target: "#ci.finalize" }]
|
|
8238
|
+
},
|
|
8239
|
+
e2ePhase: {
|
|
8240
|
+
type: "parallel",
|
|
8241
|
+
states: {
|
|
8242
|
+
intermediateComment: {
|
|
8243
|
+
initial: "checking",
|
|
8244
|
+
states: {
|
|
8245
|
+
checking: {
|
|
8246
|
+
always: [
|
|
8247
|
+
{
|
|
8248
|
+
guard: ({ context }) => !context.prContext?.prNumber,
|
|
8249
|
+
target: "done"
|
|
8250
|
+
},
|
|
8251
|
+
{
|
|
8252
|
+
guard: ({ context }) => !shouldPostGitHubComment(context),
|
|
8253
|
+
target: "done"
|
|
8254
|
+
},
|
|
8255
|
+
{ target: "posting" }
|
|
8256
|
+
]
|
|
8257
|
+
},
|
|
8258
|
+
posting: {
|
|
8259
|
+
invoke: {
|
|
8260
|
+
src: "upsertComment",
|
|
8261
|
+
input: ({ context }) => createIntermediateCommentRequest(context),
|
|
8262
|
+
onDone: "done",
|
|
8263
|
+
onError: "done"
|
|
8264
|
+
}
|
|
8265
|
+
},
|
|
8266
|
+
done: { type: "final" }
|
|
8267
|
+
}
|
|
8268
|
+
},
|
|
8269
|
+
e2eTests: {
|
|
8270
|
+
initial: "running",
|
|
8271
|
+
states: {
|
|
8272
|
+
running: {
|
|
8273
|
+
invoke: {
|
|
8274
|
+
src: "runLayers",
|
|
8275
|
+
input: ({ context }) => createE2ERunLayersInput(context),
|
|
8276
|
+
onDone: {
|
|
8277
|
+
target: "done",
|
|
8278
|
+
actions: assign({
|
|
8279
|
+
layerResults: ({ context, event }) => {
|
|
8280
|
+
const e2eResults = convertLayerResults(event.output.results);
|
|
8281
|
+
return mergeLayerResults(context.layerResults, e2eResults);
|
|
8282
|
+
}
|
|
8283
|
+
})
|
|
8284
|
+
},
|
|
8285
|
+
onError: {
|
|
8286
|
+
target: "done",
|
|
8287
|
+
actions: assign({
|
|
8288
|
+
layerResults: ({ context }) => ({
|
|
8289
|
+
...context.layerResults,
|
|
8290
|
+
[E2E_LAYER]: { status: "failed", exitCode: 1 }
|
|
8291
|
+
})
|
|
8292
|
+
})
|
|
8293
|
+
}
|
|
8294
|
+
}
|
|
8295
|
+
},
|
|
8296
|
+
done: { type: "final" }
|
|
8297
|
+
}
|
|
8298
|
+
}
|
|
8299
|
+
},
|
|
8300
|
+
onDone: [
|
|
8301
|
+
{
|
|
8302
|
+
guard: ({ context }) => Object.values(context.layerResults).some(
|
|
8303
|
+
(result) => result.status === "failed"
|
|
8304
|
+
),
|
|
8305
|
+
target: "failed",
|
|
8306
|
+
actions: assign({
|
|
8307
|
+
exitCode: ({ context }) => computeExitCodeFromLayerResults(context.layerResults)
|
|
8308
|
+
})
|
|
8309
|
+
},
|
|
8310
|
+
{
|
|
8311
|
+
target: "done",
|
|
8312
|
+
actions: assign({
|
|
8313
|
+
exitCode: ({ context }) => computeExitCodeFromLayerResults(context.layerResults)
|
|
8314
|
+
})
|
|
8315
|
+
}
|
|
8316
|
+
]
|
|
8317
|
+
},
|
|
8318
|
+
failed: {
|
|
8319
|
+
always: [
|
|
8320
|
+
{
|
|
8321
|
+
target: "#ci.finalize",
|
|
8322
|
+
actions: assign({
|
|
8323
|
+
exitCode: ({ context }) => context.exitCode !== 0 ? context.exitCode : 1
|
|
8324
|
+
})
|
|
8325
|
+
}
|
|
8326
|
+
]
|
|
8327
|
+
},
|
|
8328
|
+
done: {
|
|
8329
|
+
type: "final"
|
|
8330
|
+
}
|
|
8331
|
+
}
|
|
8332
|
+
},
|
|
8333
|
+
observability: {
|
|
8334
|
+
initial: "productionPreview",
|
|
8335
|
+
states: {
|
|
8336
|
+
productionPreview: {
|
|
8337
|
+
invoke: {
|
|
8338
|
+
src: "productionPreview",
|
|
8339
|
+
input: ({ context }) => ({
|
|
8340
|
+
repoRoot: assertRepoRoot(context),
|
|
8341
|
+
tmpDir: assertTmpDir(context),
|
|
8342
|
+
shouldExecute: Boolean(context.input.productionDatabaseUrl?.trim())
|
|
8343
|
+
}),
|
|
8344
|
+
onDone: {
|
|
8345
|
+
target: "collectSchemaStats",
|
|
8346
|
+
actions: assign({
|
|
8347
|
+
productionPreview: ({ event }) => event.output.preview
|
|
8348
|
+
})
|
|
8349
|
+
},
|
|
8350
|
+
onError: {
|
|
8351
|
+
target: "collectSchemaStats",
|
|
8352
|
+
actions: assign({
|
|
8353
|
+
productionPreview: ({ event }) => ({
|
|
8354
|
+
executed: true,
|
|
8355
|
+
planSql: null,
|
|
8356
|
+
hazards: [],
|
|
8357
|
+
hasChanges: false,
|
|
8358
|
+
error: event.error instanceof Error ? event.error.message : "Production preview failed"
|
|
8359
|
+
})
|
|
8360
|
+
})
|
|
8361
|
+
}
|
|
8362
|
+
}
|
|
8363
|
+
},
|
|
8364
|
+
collectSchemaStats: {
|
|
8365
|
+
invoke: {
|
|
8366
|
+
src: "collectSchemaStats",
|
|
8367
|
+
input: ({ context }) => ({
|
|
8368
|
+
repoRoot: context.repoRoot ?? process.cwd(),
|
|
8369
|
+
referenceDbUrl: context.supabase?.databaseUrlRaw ?? null,
|
|
8370
|
+
ciDbUrl: context.supabase?.databaseUrlRaw ?? null,
|
|
8371
|
+
referenceStrategy: shouldReuseCiReferenceStats(context) ? "reuse-ci" : "rebuild",
|
|
8372
|
+
queryProduction: true,
|
|
8373
|
+
tmpDir: context.tmpDir ?? process.cwd()
|
|
8374
|
+
}),
|
|
8375
|
+
onDone: {
|
|
8376
|
+
target: "done",
|
|
8377
|
+
actions: assign({
|
|
8378
|
+
schemaStats: ({ event }) => event.output.schemaStats
|
|
8379
|
+
})
|
|
8380
|
+
},
|
|
8381
|
+
onError: {
|
|
8382
|
+
target: "done",
|
|
8383
|
+
actions: assign({
|
|
8384
|
+
schemaStats: () => null
|
|
8385
|
+
})
|
|
8386
|
+
}
|
|
8387
|
+
}
|
|
8388
|
+
},
|
|
8389
|
+
done: {
|
|
8390
|
+
type: "final"
|
|
8391
|
+
}
|
|
8392
|
+
}
|
|
8393
|
+
}
|
|
8394
|
+
},
|
|
8395
|
+
onDone: {
|
|
8396
|
+
target: "finalize"
|
|
8397
|
+
}
|
|
8398
|
+
},
|
|
8399
|
+
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
7769
8400
|
// Production Preview (ci-pr modes only, when schema changes exist)
|
|
7770
8401
|
// Runs dry-run against production to show what SQL would be applied
|
|
7771
8402
|
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
@@ -7825,7 +8456,7 @@ var ciMachine = setup({
|
|
|
7825
8456
|
repoRoot: context.repoRoot ?? process.cwd(),
|
|
7826
8457
|
referenceDbUrl: context.supabase?.databaseUrlRaw ?? null,
|
|
7827
8458
|
ciDbUrl: context.supabase?.databaseUrlRaw ?? null,
|
|
7828
|
-
|
|
8459
|
+
referenceStrategy: shouldReuseCiReferenceStats(context) ? "reuse-ci" : "rebuild",
|
|
7829
8460
|
queryProduction: context.mode !== "ci-local",
|
|
7830
8461
|
tmpDir: context.tmpDir ?? process.cwd()
|
|
7831
8462
|
}),
|
|
@@ -8361,6 +8992,19 @@ var STATE_TO_STEP = {
|
|
|
8361
8992
|
pullProduction: "syncSchema",
|
|
8362
8993
|
syncSchema: "syncSchema",
|
|
8363
8994
|
applySeeds: "applySeeds",
|
|
8995
|
+
postSeedPr: "applySeeds",
|
|
8996
|
+
"postSeedPr.execution.setupRoles": "applySeeds",
|
|
8997
|
+
"postSeedPr.execution.staticChecks": "staticChecks",
|
|
8998
|
+
"postSeedPr.execution.buildAndPlaywright": "build",
|
|
8999
|
+
"postSeedPr.execution.appStart": "build",
|
|
9000
|
+
"postSeedPr.execution.capabilities": "build",
|
|
9001
|
+
"postSeedPr.execution.runCoreTests": "runTests",
|
|
9002
|
+
"postSeedPr.execution.coreTestsComplete": "runTests",
|
|
9003
|
+
"postSeedPr.execution.coreTestsFailed": "runTests",
|
|
9004
|
+
"postSeedPr.execution.e2ePhase": "runTests",
|
|
9005
|
+
"postSeedPr.execution.done": "runTests",
|
|
9006
|
+
"postSeedPr.observability.productionPreview": "applySeeds",
|
|
9007
|
+
"postSeedPr.observability.collectSchemaStats": "applySeeds",
|
|
8364
9008
|
decidePath: "applySeeds",
|
|
8365
9009
|
installPgTap: "applySeeds",
|
|
8366
9010
|
setupRoles: "applySeeds",
|
|
@@ -8398,6 +9042,17 @@ var STEP_ORDER = [
|
|
|
8398
9042
|
"runTests",
|
|
8399
9043
|
"finalize"
|
|
8400
9044
|
];
|
|
9045
|
+
function resolveStepForState(state) {
|
|
9046
|
+
let cursor = state;
|
|
9047
|
+
while (cursor.length > 0) {
|
|
9048
|
+
const step = STATE_TO_STEP[cursor];
|
|
9049
|
+
if (step) return step;
|
|
9050
|
+
const nextDot = cursor.lastIndexOf(".");
|
|
9051
|
+
if (nextDot === -1) break;
|
|
9052
|
+
cursor = cursor.slice(0, nextDot);
|
|
9053
|
+
}
|
|
9054
|
+
return void 0;
|
|
9055
|
+
}
|
|
8401
9056
|
function getCompletedSteps(currentStep) {
|
|
8402
9057
|
const currentIndex = STEP_ORDER.indexOf(currentStep);
|
|
8403
9058
|
if (currentIndex <= 0) return [];
|
|
@@ -8486,8 +9141,8 @@ function determineFailedStep(context) {
|
|
|
8486
9141
|
function handleProgressCommentUpdate(snapshot, prevState) {
|
|
8487
9142
|
const currentState = getStateName(snapshot);
|
|
8488
9143
|
const context = snapshot.context;
|
|
8489
|
-
const currentStep =
|
|
8490
|
-
const prevStep =
|
|
9144
|
+
const currentStep = resolveStepForState(currentState);
|
|
9145
|
+
const prevStep = resolveStepForState(prevState);
|
|
8491
9146
|
if (!currentStep || currentStep === prevStep) return;
|
|
8492
9147
|
recordStepTiming(currentStep);
|
|
8493
9148
|
if (currentStep === "finalize") {
|
|
@@ -9054,6 +9709,75 @@ var stateLogHandlers2 = {
|
|
|
9054
9709
|
logSection3("Database: Apply Seeds (db seed)");
|
|
9055
9710
|
logger.info("Running: pnpm exec runa db seed ci --auto-approve");
|
|
9056
9711
|
},
|
|
9712
|
+
"postSeedPr.execution.setupRoles": (ctx, logger) => {
|
|
9713
|
+
if (ctx.seedsApplied) {
|
|
9714
|
+
logger.success("Seeds applied successfully");
|
|
9715
|
+
}
|
|
9716
|
+
logSection3("Database: Setup Roles (db:setup-roles)");
|
|
9717
|
+
logger.info("Running: pnpm db:setup-roles");
|
|
9718
|
+
},
|
|
9719
|
+
"postSeedPr.observability.productionPreview": (ctx, logger) => {
|
|
9720
|
+
if (ctx.seedsApplied) {
|
|
9721
|
+
logger.success("Seeds applied successfully");
|
|
9722
|
+
}
|
|
9723
|
+
logSection3("Database: Production Preview (dry-run)");
|
|
9724
|
+
logger.info("Running: pnpm exec runa db apply production --check");
|
|
9725
|
+
},
|
|
9726
|
+
"postSeedPr.observability.collectSchemaStats": (ctx, logger) => {
|
|
9727
|
+
if (ctx.productionPreview?.executed) {
|
|
9728
|
+
if (ctx.productionPreview.hasChanges) {
|
|
9729
|
+
logger.info("Production preview: schema changes detected");
|
|
9730
|
+
} else {
|
|
9731
|
+
logger.success("Production preview: no schema changes");
|
|
9732
|
+
}
|
|
9733
|
+
}
|
|
9734
|
+
logSection3("Database: Collect Schema Statistics");
|
|
9735
|
+
logger.info("Comparing Local/CI/Production schemas...");
|
|
9736
|
+
},
|
|
9737
|
+
"postSeedPr.execution.staticChecks": (ctx, logger) => {
|
|
9738
|
+
if (ctx.rolesSetup) {
|
|
9739
|
+
logger.success("Database roles configured");
|
|
9740
|
+
}
|
|
9741
|
+
logSection3("Static Checks: type-check + lint (parallel)");
|
|
9742
|
+
logger.info("Running: pnpm type-check || pnpm lint");
|
|
9743
|
+
},
|
|
9744
|
+
"postSeedPr.execution.buildAndPlaywright": (ctx, logger) => {
|
|
9745
|
+
if (ctx.staticChecksPassed) {
|
|
9746
|
+
logger.success("Static checks passed");
|
|
9747
|
+
}
|
|
9748
|
+
logSection3("Build: App Build + Playwright Install (parallel)");
|
|
9749
|
+
logger.info("Running: pnpm build || pnpm exec playwright install chromium");
|
|
9750
|
+
},
|
|
9751
|
+
"postSeedPr.execution.appStart": (ctx, logger) => {
|
|
9752
|
+
if (ctx.appBuildPassed) {
|
|
9753
|
+
logger.success("App build completed");
|
|
9754
|
+
}
|
|
9755
|
+
if (ctx.manifestGenerated) {
|
|
9756
|
+
logger.success("Manifest generated (Layer 3 ready)");
|
|
9757
|
+
} else if (ctx.manifestGenerated === false) {
|
|
9758
|
+
logger.warn("Manifest generation failed (Layer 3 may be skipped)");
|
|
9759
|
+
}
|
|
9760
|
+
if (ctx.playwrightInstalled) {
|
|
9761
|
+
logger.success("Playwright browsers installed");
|
|
9762
|
+
}
|
|
9763
|
+
logSection3("Start Application");
|
|
9764
|
+
logger.info(`Starting app on port ${ctx.app?.port ?? 3e3}...`);
|
|
9765
|
+
},
|
|
9766
|
+
"postSeedPr.execution.capabilities": (ctx, logger) => {
|
|
9767
|
+
if (ctx.appStarted) {
|
|
9768
|
+
logger.success(`App started at ${ctx.baseUrl ?? "http://localhost:3000"}`);
|
|
9769
|
+
}
|
|
9770
|
+
logSection3("Detect Test Capabilities");
|
|
9771
|
+
},
|
|
9772
|
+
"postSeedPr.execution.runCoreTests": (ctx, logger) => {
|
|
9773
|
+
const coreLayers = ctx.selectedLayers.filter((l) => l !== 4);
|
|
9774
|
+
logSection3(`Core Tests: Layer ${coreLayers.join(", ")}`);
|
|
9775
|
+
logger.info(`Running core layers: ${coreLayers.map((l) => `test layer${l}`).join(", ")}`);
|
|
9776
|
+
},
|
|
9777
|
+
"postSeedPr.execution.e2ePhase": (_ctx, logger) => {
|
|
9778
|
+
logSection3("E2E Phase: Layer 4 + Intermediate Comment");
|
|
9779
|
+
logger.info("Running Layer 4 E2E tests (parallel with intermediate PR comment)");
|
|
9780
|
+
},
|
|
9057
9781
|
productionPreview: (ctx, logger) => {
|
|
9058
9782
|
if (ctx.seedsApplied) {
|
|
9059
9783
|
logger.success("Seeds applied successfully");
|
|
@@ -9260,7 +9984,7 @@ async function runCiPrCommand(options) {
|
|
|
9260
9984
|
{ id: "setup", description: "Start local Supabase instance" },
|
|
9261
9985
|
{
|
|
9262
9986
|
id: "db",
|
|
9263
|
-
description: "db apply \u2192 db seed \u2192 production preview \
|
|
9987
|
+
description: "db apply \u2192 db seed \u2192 (production preview \u2225 schema stats \u2225 db:setup-roles)"
|
|
9264
9988
|
},
|
|
9265
9989
|
...skipStaticChecks ? [] : [{ id: "static", description: "pnpm type-check + pnpm lint (parallel)" }],
|
|
9266
9990
|
...skipBuild ? [] : [{ id: "build", description: "pnpm build \u2192 manifest:generate + playwright install" }],
|