@runa-ai/runa-cli 0.8.0 → 0.10.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-HQMSVN6N.js → build-P2A6345N.js} +2 -2
- package/dist/{check-PCSQPYDM.js → check-4TZHNOZU.js} +4 -4
- package/dist/{chunk-DRSUEMAK.js → chunk-B7C7CLW2.js} +2 -5
- package/dist/{chunk-B3POLMII.js → chunk-BQ336L5T.js} +1 -1
- package/dist/{chunk-6FAU4IGR.js → chunk-ELXXQIGW.js} +4 -1
- package/dist/{chunk-RB2ZUS76.js → chunk-EXR4J2JT.js} +52 -16
- package/dist/{chunk-JMJP4A47.js → chunk-GT5DMS5R.js} +20 -2
- package/dist/{chunk-3JO6YP3T.js → chunk-IEKYTCYA.js} +1 -1
- package/dist/{chunk-WPMR7RQ4.js → chunk-IWVXI5O4.js} +2 -2
- package/dist/chunk-KUH3G522.js +72 -0
- package/dist/{chunk-VSH3IXDQ.js → chunk-MAFJAA2P.js} +1 -1
- package/dist/{chunk-CCKG5R4Y.js → chunk-MILCC3B6.js} +1 -1
- package/dist/{chunk-5NKWR4FF.js → chunk-OERS32LW.js} +1 -1
- package/dist/{chunk-GHQH6UC5.js → chunk-OXQISY3J.js} +1 -1
- package/dist/chunk-QDOR3GTD.js +9043 -0
- package/dist/{chunk-2QX7T24B.js → chunk-QKGL6Q2S.js} +1 -1
- package/dist/{chunk-OBYZDT2E.js → chunk-URWDB7YL.js} +15 -78
- package/dist/{chunk-ZYT7OQJB.js → chunk-WGRVAGSR.js} +6 -6
- package/dist/chunk-ZWDWFMOX.js +1514 -0
- package/dist/{ci-ZK3LKYFX.js → ci-FLTJ2UXB.js} +992 -849
- package/dist/{cli-ZY5VRIJA.js → cli-THEA6T7N.js} +31 -31
- package/dist/commands/ci/commands/ci-prod-db-operations.d.ts +12 -17
- package/dist/commands/ci/commands/ci-prod-utils.d.ts +7 -0
- package/dist/commands/ci/commands/layer4-discovery.d.ts +2 -0
- package/dist/commands/ci/machine/actors/db/production-preview.d.ts +4 -3
- package/dist/commands/ci/machine/actors/db/sync-schema.d.ts +5 -1
- package/dist/commands/ci/machine/actors/test/capabilities.d.ts +2 -13
- package/dist/commands/ci/machine/actors/test/index.d.ts +1 -0
- package/dist/commands/ci/machine/actors/test/layer-content.d.ts +11 -0
- package/dist/commands/ci/machine/commands/ci-pr-internal-profile.d.ts +7 -0
- package/dist/commands/ci/machine/commands/ci-step-registry.d.ts +25 -0
- package/dist/commands/ci/machine/commands/step-telemetry.d.ts +1 -2
- package/dist/commands/ci/machine/contract.d.ts +3 -0
- package/dist/commands/ci/machine/guards.d.ts +3 -10
- package/dist/commands/ci/machine/helpers.d.ts +1 -1
- package/dist/commands/ci/machine/machine-execution-helpers.d.ts +5 -2
- package/dist/commands/ci/machine/machine.d.ts +24 -30
- package/dist/commands/ci/machine/selectors.d.ts +6 -0
- package/dist/commands/ci/machine/types.d.ts +3 -1
- package/dist/commands/ci/utils/ci-logging.d.ts +16 -0
- package/dist/commands/ci/utils/rls-verification.d.ts +3 -2
- package/dist/commands/db/apply/actors/pg-schema-diff-actors.d.ts +1 -0
- package/dist/commands/db/apply/contract.d.ts +209 -0
- package/dist/commands/db/apply/helpers/fresh-db-handler.d.ts +2 -1
- package/dist/commands/db/apply/helpers/index.d.ts +3 -1
- package/dist/commands/db/apply/helpers/plan-ast-sql-helpers.d.ts +19 -0
- package/dist/commands/db/apply/helpers/plan-ast.d.ts +1 -2
- package/dist/commands/db/apply/helpers/plan-check-filter.d.ts +0 -14
- package/dist/commands/db/apply/helpers/plan-validator.d.ts +34 -0
- package/dist/commands/db/apply/helpers/planner-artifact.d.ts +65 -0
- package/dist/commands/db/apply/helpers/retry-logic.d.ts +5 -0
- package/dist/commands/db/apply/machine.d.ts +50 -15
- package/dist/commands/db/commands/db-apply-error.d.ts +6 -1
- package/dist/commands/db/commands/db-apply.d.ts +5 -0
- package/dist/commands/db/commands/db-plan.d.ts +3 -0
- package/dist/commands/db/commands/db-preview-profile.d.ts +23 -0
- package/dist/commands/db/commands/db-preview.d.ts +3 -0
- package/dist/commands/db/sync/actors.d.ts +1 -0
- package/dist/commands/db/sync/contract.d.ts +16 -0
- package/dist/commands/db/sync/guardrail-orchestrator.d.ts +15 -0
- package/dist/commands/db/sync/guardrail-reporting.d.ts +12 -0
- package/dist/commands/db/sync/index.d.ts +4 -0
- package/dist/commands/db/sync/machine.d.ts +18 -13
- package/dist/commands/db/sync/schema-guardrail-config-test-support.d.ts +15 -0
- package/dist/commands/db/sync/schema-guardrail-config.d.ts +11 -0
- package/dist/commands/db/sync/schema-guardrail-ddl-order.d.ts +36 -0
- package/dist/commands/db/sync/schema-guardrail-graph-guidance.d.ts +15 -0
- package/dist/commands/db/sync/schema-guardrail-graph-metadata.d.ts +41 -0
- package/dist/commands/db/sync/schema-guardrail-graph-nodes.d.ts +61 -0
- package/dist/commands/db/sync/schema-guardrail-graph-sql-helpers.d.ts +31 -0
- package/dist/commands/db/sync/schema-guardrail-graph-types.d.ts +56 -0
- package/dist/commands/db/sync/schema-guardrail-graph.d.ts +20 -0
- package/dist/commands/db/sync/schema-guardrail-local-blockers.d.ts +7 -0
- package/dist/commands/db/sync/schema-guardrail-phases.d.ts +26 -0
- package/dist/commands/db/sync/schema-guardrail-production-check.d.ts +23 -0
- package/dist/commands/db/sync/schema-guardrail-rewrite.d.ts +46 -0
- package/dist/commands/db/sync/schema-guardrail-runtime.d.ts +5 -0
- package/dist/commands/db/sync/schema-guardrail-semantic-warnings.d.ts +9 -0
- package/dist/commands/db/sync/schema-guardrail-types.d.ts +243 -0
- package/dist/commands/db/sync/schema-guardrail.d.ts +10 -0
- package/dist/commands/db/utils/declarative-dependency-sql-utils.d.ts +1 -1
- package/dist/commands/db/utils/duplicate-function-ownership.d.ts +27 -1
- package/dist/commands/db/utils/policy-cross-schema-refs.d.ts +12 -0
- package/dist/commands/db/utils/sql-table-extractor.d.ts +6 -0
- package/dist/commands/test/commands/layer4-prereqs.d.ts +15 -0
- package/dist/{config-loader-GT3HAQ7U.js → config-loader-N5ODNMD5.js} +2 -2
- package/dist/db-IDKQ44VX.js +12757 -0
- package/dist/{dev-GB5ERUVR.js → dev-LGSMDFJN.js} +7 -6
- package/dist/{doctor-ROSWSMLH.js → doctor-GYX73IEW.js} +4 -4
- package/dist/{env-WP74UUMO.js → env-KYR6Q7WO.js} +15 -10
- package/dist/{env-files-HRNUGZ5O.js → env-files-ONBC47I6.js} +3 -3
- package/dist/{hotfix-TOSGTVCW.js → hotfix-RJIAPLAM.js} +4 -4
- package/dist/index.js +3 -3
- package/dist/{init-35JLDFHI.js → init-2O6ODG5Z.js} +2 -2
- package/dist/{inject-test-attrs-XN4I2AOR.js → inject-test-attrs-F5A346UV.js} +3 -3
- package/dist/{manifest-EGCAZ4TK.js → manifest-CI4BRWEB.js} +2 -2
- package/dist/{observability-CJA5UFIC.js → observability-WNSCJ5FV.js} +2 -2
- package/dist/pg-schema-diff-helpers-7377FS2D.js +7 -0
- package/dist/{sdk-XK6HQU7S.js → sdk-BTIVPEE5.js} +1 -1
- package/dist/{template-check-BDFMT6ZO.js → template-check-VNNQQXCX.js} +10 -0
- package/dist/{test-V4KQL574.js → test-QCPN6Z47.js} +74 -46
- package/dist/{upgrade-7L4JIE4K.js → upgrade-QZKEI3NJ.js} +2 -2
- package/dist/utils/db-url-utils.d.ts +4 -77
- package/dist/{vuln-check-G6I4YYDC.js → vuln-check-JRPMUHLF.js} +1 -1
- package/dist/{vuln-checker-CT2AYPIS.js → vuln-checker-Q7LSHUHJ.js} +1 -1
- package/dist/{watch-AL4LCBRM.js → watch-RFVCEQLH.js} +3 -3
- package/dist/{workflow-UZIZ2JUS.js → workflow-UOG6ZZMH.js} +3 -3
- package/package.json +3 -3
- package/dist/chunk-6E2DRXIL.js +0 -452
- package/dist/db-EPI2DQYN.js +0 -18275
|
@@ -1,24 +1,33 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { createRequire } from 'module';
|
|
3
|
-
import { normalizeDatabaseUrlForDdl, parseBoolish, enhanceConnectionError, isIdempotentRoleHazard, detectAppSchemas, formatSchemasForSql } from './chunk-
|
|
3
|
+
import { normalizeDatabaseUrlForDdl, parseBoolish, enhanceConnectionError, isIdempotentRoleHazard, detectAppSchemas, formatSchemasForSql, getDbPlanArtifactPath, runDbApply } from './chunk-QDOR3GTD.js';
|
|
4
|
+
import './chunk-WGRVAGSR.js';
|
|
5
|
+
import './chunk-ZWDWFMOX.js';
|
|
6
|
+
import './chunk-UHDAYPHH.js';
|
|
7
|
+
import './chunk-Y5ANTCKE.js';
|
|
8
|
+
import './chunk-IWVXI5O4.js';
|
|
9
|
+
import './chunk-OXQISY3J.js';
|
|
10
|
+
import './chunk-B7C7CLW2.js';
|
|
4
11
|
import './chunk-QDF7QXBL.js';
|
|
5
12
|
import { getSnapshotStateName, getSnapshotStatePaths, isSnapshotComplete } from './chunk-XVNDDHAF.js';
|
|
6
|
-
import { createInitialSummary, resolveMode, appendGithubStepSummary, buildCiProdApplyStepSummaryMarkdown, setSummaryErrorFromUnknown, writeEnvLocal, startAppBackground, waitForAppReady, executePrSetupBase, createErrorOutput, requireCiAutoApprove, resolveProdApplyInputs, parseIntOr, addGithubMask } from './chunk-
|
|
13
|
+
import { createInitialSummary, resolveMode, appendGithubStepSummary, buildCiProdApplyStepSummaryMarkdown, setSummaryErrorFromUnknown, writeEnvLocal, startAppBackground, waitForAppReady, executePrSetupBase, createErrorOutput, requireCiAutoApprove, resolveProdApplyInputs, parseIntOr, classifyCiProdApplyError, addGithubMask } from './chunk-EXR4J2JT.js';
|
|
14
|
+
import './chunk-URWDB7YL.js';
|
|
7
15
|
import { parsePostgresUrl, buildPsqlArgs, buildPsqlEnv, psqlSyncQuery } from './chunk-A6A7JIRD.js';
|
|
8
|
-
import { ensureRunaTmpDir, runLogged } from './chunk-
|
|
16
|
+
import { ensureRunaTmpDir, runLogged } from './chunk-ELXXQIGW.js';
|
|
9
17
|
import { createMachineStateChangeLogger } from './chunk-5FT3F36G.js';
|
|
10
18
|
import { getSafeEnv, getFilteredEnv, redactSecrets } from './chunk-II7VYQEM.js';
|
|
11
19
|
import { init_constants, init_local_supabase, detectLocalSupabasePorts } from './chunk-QSEF4T3Y.js';
|
|
12
20
|
import { emitJsonSuccess } from './chunk-KE6QJBZG.js';
|
|
13
21
|
import './chunk-WJXC4MVY.js';
|
|
14
22
|
import { setOutputFormat } from './chunk-HKUWEGUX.js';
|
|
15
|
-
import './chunk-
|
|
23
|
+
import './chunk-OERS32LW.js';
|
|
24
|
+
import './chunk-GT5DMS5R.js';
|
|
16
25
|
import { init_esm_shims } from './chunk-VRXHCR5K.js';
|
|
17
26
|
import { Command } from 'commander';
|
|
18
27
|
import { spawnSync, spawn, execFileSync } from 'child_process';
|
|
19
28
|
import { mkdir, writeFile, readFile } from 'fs/promises';
|
|
20
29
|
import path4, { join } from 'path';
|
|
21
|
-
import { CommandOutcomeSchema, createCLILogger, CLIError, syncFromProduction, GITHUB_API, getClassificationForProfile, formatDuration,
|
|
30
|
+
import { CommandOutcomeSchema, createCLILogger, CLIError, syncFromProduction, GITHUB_API, getClassificationForProfile, formatDuration, buildCommandOutcomeSummary, deriveCommandExitMode, getStatusIcon, DATABASE_PACKAGE_CANDIDATES, detectDatabasePackage } from '@runa-ai/runa';
|
|
22
31
|
import { z } from 'zod';
|
|
23
32
|
import { existsSync, createWriteStream, readFileSync, readdirSync, statSync, promises, lstatSync } from 'fs';
|
|
24
33
|
import { resolve4 } from 'dns/promises';
|
|
@@ -27,6 +36,7 @@ import { fromPromise, setup, assign, createActor } from 'xstate';
|
|
|
27
36
|
import { randomUUID, createHash } from 'crypto';
|
|
28
37
|
import { execa } from 'execa';
|
|
29
38
|
import postgres from 'postgres';
|
|
39
|
+
import { resolveLayer4DiscoverySync } from '@runa-ai/runa/test-config';
|
|
30
40
|
import { glob } from 'glob';
|
|
31
41
|
|
|
32
42
|
createRequire(import.meta.url);
|
|
@@ -293,29 +303,30 @@ async function loadCiConfig(params) {
|
|
|
293
303
|
|
|
294
304
|
// src/commands/ci/utils/ci-logging.ts
|
|
295
305
|
init_esm_shims();
|
|
306
|
+
var write = (msg) => process.stderr.write(msg + "\n");
|
|
296
307
|
function logSection(title) {
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
308
|
+
write("");
|
|
309
|
+
write("\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501");
|
|
310
|
+
write(title);
|
|
311
|
+
write("\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501");
|
|
312
|
+
write("");
|
|
302
313
|
}
|
|
303
314
|
function logKeyValueTable(params) {
|
|
304
315
|
logSection(params.title);
|
|
305
316
|
for (const [k, v] of params.rows) {
|
|
306
|
-
|
|
317
|
+
write(`${k}: ${v}`);
|
|
307
318
|
}
|
|
308
319
|
}
|
|
309
320
|
function logPlan(steps) {
|
|
310
321
|
logSection("Plan");
|
|
311
322
|
for (const step of steps) {
|
|
312
|
-
|
|
323
|
+
write(`- ${step.id}: ${step.description}`);
|
|
313
324
|
}
|
|
314
325
|
}
|
|
315
326
|
function logNextActions(items) {
|
|
316
327
|
logSection("Next");
|
|
317
328
|
for (const item of items) {
|
|
318
|
-
|
|
329
|
+
write(`- ${item}`);
|
|
319
330
|
}
|
|
320
331
|
}
|
|
321
332
|
|
|
@@ -483,7 +494,7 @@ async function githubRequest(params) {
|
|
|
483
494
|
url.searchParams.set(k, String(v));
|
|
484
495
|
}
|
|
485
496
|
const startTime = Date.now();
|
|
486
|
-
|
|
497
|
+
process.stderr.write(`[DEBUG] GitHub API: ${params.method} ${params.path} (starting)`);
|
|
487
498
|
const controller = new AbortController();
|
|
488
499
|
const timeoutId = setTimeout(() => controller.abort(), GITHUB_API_TIMEOUT_MS);
|
|
489
500
|
try {
|
|
@@ -502,7 +513,7 @@ async function githubRequest(params) {
|
|
|
502
513
|
const duration = Date.now() - startTime;
|
|
503
514
|
if (!res.ok) {
|
|
504
515
|
const text = await res.text().catch(() => "");
|
|
505
|
-
|
|
516
|
+
process.stderr.write(
|
|
506
517
|
`[DEBUG] GitHub API: ${params.method} ${params.path} failed (${res.status}) after ${duration}ms`
|
|
507
518
|
);
|
|
508
519
|
throw new CLIError(
|
|
@@ -513,7 +524,7 @@ async function githubRequest(params) {
|
|
|
513
524
|
20
|
|
514
525
|
);
|
|
515
526
|
}
|
|
516
|
-
|
|
527
|
+
process.stderr.write(
|
|
517
528
|
`[DEBUG] GitHub API: ${params.method} ${params.path} succeeded (${res.status}) in ${duration}ms`
|
|
518
529
|
);
|
|
519
530
|
if (res.status === 204) return null;
|
|
@@ -523,7 +534,7 @@ async function githubRequest(params) {
|
|
|
523
534
|
clearTimeout(timeoutId);
|
|
524
535
|
const duration = Date.now() - startTime;
|
|
525
536
|
if (error instanceof Error && error.name === "AbortError") {
|
|
526
|
-
|
|
537
|
+
process.stderr.write(
|
|
527
538
|
`[DEBUG] GitHub API: ${params.method} ${params.path} timed out after ${duration}ms`
|
|
528
539
|
);
|
|
529
540
|
throw new CLIError(
|
|
@@ -534,7 +545,7 @@ async function githubRequest(params) {
|
|
|
534
545
|
21
|
|
535
546
|
);
|
|
536
547
|
}
|
|
537
|
-
|
|
548
|
+
process.stderr.write(
|
|
538
549
|
`[DEBUG] GitHub API: ${params.method} ${params.path} error after ${duration}ms: ${error instanceof Error ? error.message : String(error)}`
|
|
539
550
|
);
|
|
540
551
|
throw error;
|
|
@@ -661,17 +672,6 @@ async function detectStack(repoRoot, tmpDir, productionDbUrlAdmin) {
|
|
|
661
672
|
});
|
|
662
673
|
return String(res.stdout ?? "").trim();
|
|
663
674
|
}
|
|
664
|
-
function runGitDiffQuiet(repoRoot, diffPaths) {
|
|
665
|
-
const result = spawnSync("git", ["diff", "--quiet", "origin/main", "HEAD", "--", ...diffPaths], {
|
|
666
|
-
cwd: repoRoot,
|
|
667
|
-
stdio: "ignore",
|
|
668
|
-
timeout: 3e4
|
|
669
|
-
});
|
|
670
|
-
if (result.error || result.signal === "SIGTERM") {
|
|
671
|
-
return null;
|
|
672
|
-
}
|
|
673
|
-
return result.status !== 0;
|
|
674
|
-
}
|
|
675
675
|
function getSqlSchemaDiffPaths(repoRoot) {
|
|
676
676
|
const declarativeSqlPath = path4.join(repoRoot, "supabase/schemas/declarative");
|
|
677
677
|
const idempotentSqlPath = path4.join(repoRoot, "supabase/schemas/idempotent");
|
|
@@ -688,6 +688,38 @@ function getLegacySchemaDiffPath(repoRoot) {
|
|
|
688
688
|
const schemaPath = path4.join(detected, "src", "schema");
|
|
689
689
|
return existsSync(path4.join(repoRoot, schemaPath)) ? `${schemaPath}/` : null;
|
|
690
690
|
}
|
|
691
|
+
function resolveSchemaDiffTarget(repoRoot) {
|
|
692
|
+
const sqlDiffPaths = getSqlSchemaDiffPaths(repoRoot);
|
|
693
|
+
if (sqlDiffPaths.length > 0) {
|
|
694
|
+
return {
|
|
695
|
+
label: "git diff schema (supabase/schemas)",
|
|
696
|
+
paths: sqlDiffPaths
|
|
697
|
+
};
|
|
698
|
+
}
|
|
699
|
+
const legacySchemaPath = getLegacySchemaDiffPath(repoRoot);
|
|
700
|
+
if (legacySchemaPath) {
|
|
701
|
+
return {
|
|
702
|
+
label: "git diff schema (legacy drizzle schema)",
|
|
703
|
+
paths: [legacySchemaPath]
|
|
704
|
+
};
|
|
705
|
+
}
|
|
706
|
+
for (const candidate of DATABASE_PACKAGE_CANDIDATES) {
|
|
707
|
+
const candidatePath = path4.join("packages", candidate, "src", "schema");
|
|
708
|
+
if (existsSync(path4.join(repoRoot, candidatePath))) {
|
|
709
|
+
return {
|
|
710
|
+
label: `git diff schema (packages/${candidate})`,
|
|
711
|
+
paths: [`${candidatePath}/`]
|
|
712
|
+
};
|
|
713
|
+
}
|
|
714
|
+
}
|
|
715
|
+
if (existsSync(path4.join(repoRoot, "src", "schema"))) {
|
|
716
|
+
return {
|
|
717
|
+
label: "git diff schema (src/schema)",
|
|
718
|
+
paths: ["src/schema/"]
|
|
719
|
+
};
|
|
720
|
+
}
|
|
721
|
+
return null;
|
|
722
|
+
}
|
|
691
723
|
function checkIfInitialDeployment(repoRoot, productionDbUrl) {
|
|
692
724
|
const schemasDir = path4.join(repoRoot, "supabase", "schemas", "declarative");
|
|
693
725
|
let schemas;
|
|
@@ -754,71 +786,32 @@ function checkIfInitialDeployment(repoRoot, productionDbUrl) {
|
|
|
754
786
|
};
|
|
755
787
|
}
|
|
756
788
|
}
|
|
757
|
-
function
|
|
758
|
-
const
|
|
759
|
-
if (
|
|
760
|
-
return
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
789
|
+
async function collectSchemaChangeSummary(repoRoot, tmpDir) {
|
|
790
|
+
const target = resolveSchemaDiffTarget(repoRoot);
|
|
791
|
+
if (!target) {
|
|
792
|
+
return {
|
|
793
|
+
hasChanges: null,
|
|
794
|
+
diffLogPath: null,
|
|
795
|
+
targetLabel: null,
|
|
796
|
+
diffPaths: []
|
|
797
|
+
};
|
|
765
798
|
}
|
|
766
|
-
return null;
|
|
767
|
-
}
|
|
768
|
-
async function showSchemaDiff(repoRoot, tmpDir) {
|
|
769
799
|
const diffLog = path4.join(tmpDir, "schema-diff.log");
|
|
770
|
-
const
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
const schemaPath = path4.join(detected, "src", "schema");
|
|
786
|
-
if (existsSync(path4.join(repoRoot, schemaPath))) {
|
|
787
|
-
await runLogged({
|
|
788
|
-
cwd: repoRoot,
|
|
789
|
-
env: safeEnv,
|
|
790
|
-
label: `git diff schema (${detected})`,
|
|
791
|
-
command: "git",
|
|
792
|
-
args: ["diff", "origin/main", "HEAD", "--", `${schemaPath}/`],
|
|
793
|
-
logFile: diffLog
|
|
794
|
-
});
|
|
795
|
-
return;
|
|
796
|
-
}
|
|
797
|
-
}
|
|
798
|
-
for (const candidate of DATABASE_PACKAGE_CANDIDATES) {
|
|
799
|
-
const candidatePath = path4.join("packages", candidate, "src", "schema");
|
|
800
|
-
if (existsSync(path4.join(repoRoot, candidatePath))) {
|
|
801
|
-
await runLogged({
|
|
802
|
-
cwd: repoRoot,
|
|
803
|
-
env: safeEnv,
|
|
804
|
-
label: `git diff schema (packages/${candidate})`,
|
|
805
|
-
command: "git",
|
|
806
|
-
args: ["diff", "origin/main", "HEAD", "--", `${candidatePath}/`],
|
|
807
|
-
logFile: diffLog
|
|
808
|
-
});
|
|
809
|
-
return;
|
|
810
|
-
}
|
|
811
|
-
}
|
|
812
|
-
if (existsSync(path4.join(repoRoot, "src", "schema"))) {
|
|
813
|
-
await runLogged({
|
|
814
|
-
cwd: repoRoot,
|
|
815
|
-
env: safeEnv,
|
|
816
|
-
label: "git diff schema (src/schema)",
|
|
817
|
-
command: "git",
|
|
818
|
-
args: ["diff", "origin/main", "HEAD", "--", "src/schema/"],
|
|
819
|
-
logFile: diffLog
|
|
820
|
-
});
|
|
821
|
-
}
|
|
800
|
+
const diffResult = await runLogged({
|
|
801
|
+
cwd: repoRoot,
|
|
802
|
+
env: getSafeEnv(),
|
|
803
|
+
label: target.label,
|
|
804
|
+
command: "git",
|
|
805
|
+
args: ["diff", "origin/main", "HEAD", "--", ...target.paths],
|
|
806
|
+
logFile: diffLog
|
|
807
|
+
});
|
|
808
|
+
const rawDiff = String(diffResult.stdout ?? "");
|
|
809
|
+
return {
|
|
810
|
+
hasChanges: rawDiff.trim().length > 0,
|
|
811
|
+
diffLogPath: diffLog,
|
|
812
|
+
targetLabel: target.label,
|
|
813
|
+
diffPaths: target.paths
|
|
814
|
+
};
|
|
822
815
|
}
|
|
823
816
|
async function detectRisks(repoRoot, tmpDir, timeoutMs) {
|
|
824
817
|
const logFile = path4.join(tmpDir, "db-risks.log");
|
|
@@ -886,15 +879,12 @@ async function snapshotRestoreLatest(repoRoot, tmpDir, productionDbUrlAdmin, tim
|
|
|
886
879
|
timeoutMs
|
|
887
880
|
});
|
|
888
881
|
}
|
|
889
|
-
function
|
|
890
|
-
const hazards = [];
|
|
882
|
+
function summarizePlanSql(sql) {
|
|
891
883
|
let statements = 0;
|
|
892
884
|
const changes = [];
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
const
|
|
896
|
-
const alterMatches = logContent.match(/ALTER\s+(TABLE|COLUMN|POLICY)/gi);
|
|
897
|
-
const dropMatches = logContent.match(/DROP\s+(TABLE|INDEX|POLICY|FUNCTION|TRIGGER)/gi);
|
|
885
|
+
const createMatches = sql.match(/CREATE\s+(TABLE|INDEX|POLICY|FUNCTION|TRIGGER)/gi);
|
|
886
|
+
const alterMatches = sql.match(/ALTER\s+(TABLE|COLUMN|POLICY)/gi);
|
|
887
|
+
const dropMatches = sql.match(/DROP\s+(TABLE|INDEX|POLICY|FUNCTION|TRIGGER)/gi);
|
|
898
888
|
const creates = createMatches?.length ?? 0;
|
|
899
889
|
const alters = alterMatches?.length ?? 0;
|
|
900
890
|
const drops = dropMatches?.length ?? 0;
|
|
@@ -902,87 +892,123 @@ function parseApplyLog(logContent) {
|
|
|
902
892
|
if (creates > 0) changes.push(`${creates} CREATE`);
|
|
903
893
|
if (alters > 0) changes.push(`${alters} ALTER`);
|
|
904
894
|
if (drops > 0) changes.push(`${drops} DROP`);
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
for (const match of hazardMatches) {
|
|
908
|
-
hazardSet.add(`${match[1]}: ${match[2]}`);
|
|
909
|
-
}
|
|
910
|
-
hazards.push(...hazardSet);
|
|
911
|
-
const retryMatch = logContent.match(/Retry (\d+)\/(\d+) after (\d+)ms/g);
|
|
912
|
-
if (retryMatch) {
|
|
913
|
-
retryAttempts = retryMatch.length;
|
|
914
|
-
const waitMatches = logContent.matchAll(/after (\d+)ms/g);
|
|
915
|
-
retryWaitMs = 0;
|
|
916
|
-
for (const m of waitMatches) {
|
|
917
|
-
retryWaitMs += Number.parseInt(m[1], 10);
|
|
918
|
-
}
|
|
895
|
+
if (!sql.trim()) {
|
|
896
|
+
return { statements: 0, summary: "No schema changes" };
|
|
919
897
|
}
|
|
920
|
-
const
|
|
921
|
-
|
|
922
|
-
|
|
898
|
+
const summary = changes.length > 0 ? changes.join(", ") : "Schema applied";
|
|
899
|
+
return { statements, summary };
|
|
900
|
+
}
|
|
901
|
+
function summarizeApplyResult(result) {
|
|
902
|
+
const sql = result.filteredPlanSql ?? result.planSql ?? "";
|
|
903
|
+
const summaryFromSql = summarizePlanSql(sql);
|
|
904
|
+
const statements = result.planSummary?.effectiveStatements ?? summaryFromSql.statements;
|
|
905
|
+
if (!sql.trim() || statements === 0 || result.planner?.hasChanges === false) {
|
|
906
|
+
return {
|
|
907
|
+
statements: 0,
|
|
908
|
+
summary: "No schema changes"
|
|
909
|
+
};
|
|
923
910
|
}
|
|
924
|
-
if (
|
|
925
|
-
return {
|
|
911
|
+
if (summaryFromSql.summary !== "Schema applied") {
|
|
912
|
+
return {
|
|
913
|
+
statements,
|
|
914
|
+
summary: summaryFromSql.summary
|
|
915
|
+
};
|
|
926
916
|
}
|
|
927
|
-
|
|
928
|
-
|
|
917
|
+
return {
|
|
918
|
+
statements,
|
|
919
|
+
summary: `${statements} structural statement${statements === 1 ? "" : "s"} applied`
|
|
920
|
+
};
|
|
929
921
|
}
|
|
930
|
-
async function
|
|
931
|
-
const
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
922
|
+
async function withTemporaryEnv(overrides, run) {
|
|
923
|
+
const previous = /* @__PURE__ */ new Map();
|
|
924
|
+
for (const [key, value] of Object.entries(overrides)) {
|
|
925
|
+
previous.set(key, process.env[key]);
|
|
926
|
+
if (value === void 0) {
|
|
927
|
+
delete process.env[key];
|
|
928
|
+
} else {
|
|
929
|
+
process.env[key] = value;
|
|
930
|
+
}
|
|
931
|
+
}
|
|
932
|
+
try {
|
|
933
|
+
return await run();
|
|
934
|
+
} finally {
|
|
935
|
+
for (const [key, value] of previous.entries()) {
|
|
936
|
+
if (value === void 0) {
|
|
937
|
+
delete process.env[key];
|
|
938
|
+
} else {
|
|
939
|
+
process.env[key] = value;
|
|
940
|
+
}
|
|
941
|
+
}
|
|
943
942
|
}
|
|
944
|
-
|
|
945
|
-
|
|
943
|
+
}
|
|
944
|
+
async function withTimeout(promise, timeoutMs, label) {
|
|
945
|
+
if (!timeoutMs || timeoutMs <= 0) {
|
|
946
|
+
return promise;
|
|
946
947
|
}
|
|
947
|
-
|
|
948
|
-
|
|
948
|
+
let timer;
|
|
949
|
+
try {
|
|
950
|
+
return await Promise.race([
|
|
951
|
+
promise,
|
|
952
|
+
new Promise((_, reject) => {
|
|
953
|
+
timer = setTimeout(() => {
|
|
954
|
+
reject(new Error(`${label} timed out after ${timeoutMs}ms`));
|
|
955
|
+
}, timeoutMs);
|
|
956
|
+
timer.unref?.();
|
|
957
|
+
})
|
|
958
|
+
]);
|
|
959
|
+
} finally {
|
|
960
|
+
if (timer) {
|
|
961
|
+
clearTimeout(timer);
|
|
962
|
+
}
|
|
949
963
|
}
|
|
950
|
-
|
|
964
|
+
}
|
|
965
|
+
async function applyProductionSchema(repoRoot, tmpDir, productionDbUrlAdmin, productionDbUrl, options) {
|
|
966
|
+
const logPath = path4.join(tmpDir, "db-apply-production.json");
|
|
951
967
|
const startTime = Date.now();
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
968
|
+
const plannerArtifactPath = getDbPlanArtifactPath({
|
|
969
|
+
repoRoot,
|
|
970
|
+
environment: "production",
|
|
971
|
+
previewProfile: "full"
|
|
972
|
+
});
|
|
973
|
+
const result = await withTemporaryEnv(
|
|
974
|
+
{
|
|
955
975
|
...getFilteredEnv(),
|
|
956
|
-
// PRD Naming Convention:
|
|
957
|
-
// - GH_DATABASE_URL_ADMIN = postgres role (DDL capable, for pg-schema-diff)
|
|
958
|
-
// - GH_DATABASE_URL = drizzle_app role (app runtime)
|
|
959
976
|
GH_DATABASE_URL_ADMIN: productionDbUrlAdmin,
|
|
960
977
|
GH_DATABASE_URL: productionDbUrl || productionDbUrlAdmin
|
|
961
978
|
},
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
979
|
+
() => withTimeout(
|
|
980
|
+
runDbApply("production", {
|
|
981
|
+
verbose: true,
|
|
982
|
+
seed: false,
|
|
983
|
+
autoApprove: true,
|
|
984
|
+
allowDataLoss: options?.allowDataLoss,
|
|
985
|
+
confirmAuthzUpdate: options?.confirmAuthzUpdate,
|
|
986
|
+
maxLockWaitMs: options?.maxLockWaitMs,
|
|
987
|
+
commandLabel: `runa db apply production --from-plan ${path4.relative(repoRoot, plannerArtifactPath)}`,
|
|
988
|
+
fromPlan: plannerArtifactPath
|
|
989
|
+
}),
|
|
990
|
+
options?.timeoutMs,
|
|
991
|
+
"db apply production"
|
|
992
|
+
)
|
|
993
|
+
);
|
|
994
|
+
await writeFile(logPath, `${JSON.stringify(result, null, 2)}
|
|
995
|
+
`, "utf-8");
|
|
996
|
+
if (result.error) {
|
|
997
|
+
throw new Error(result.error);
|
|
974
998
|
}
|
|
975
|
-
const
|
|
999
|
+
const totalMs = Date.now() - startTime;
|
|
1000
|
+
const parsed = summarizeApplyResult(result);
|
|
976
1001
|
return {
|
|
977
1002
|
statementsExecuted: parsed.statements,
|
|
978
|
-
hazards:
|
|
1003
|
+
hazards: result.hazards,
|
|
979
1004
|
changeSummary: parsed.summary,
|
|
980
1005
|
logPath,
|
|
981
1006
|
metrics: {
|
|
982
|
-
totalMs,
|
|
983
|
-
retryAttempts:
|
|
984
|
-
retryWaitMs:
|
|
985
|
-
}
|
|
1007
|
+
totalMs: result.metrics?.totalMs ?? totalMs,
|
|
1008
|
+
retryAttempts: result.metrics?.retryAttempts,
|
|
1009
|
+
retryWaitMs: result.metrics?.retryWaitMs
|
|
1010
|
+
},
|
|
1011
|
+
planner: result.planner
|
|
986
1012
|
};
|
|
987
1013
|
}
|
|
988
1014
|
async function auditRecord(repoRoot, tmpDir, productionDbUrlAdmin, params) {
|
|
@@ -996,30 +1022,17 @@ async function auditRecord(repoRoot, tmpDir, productionDbUrlAdmin, params) {
|
|
|
996
1022
|
logFile: path4.join(tmpDir, "git-commit-message.log")
|
|
997
1023
|
});
|
|
998
1024
|
const commitMsg = String(msg.stdout ?? "").trim();
|
|
999
|
-
const
|
|
1000
|
-
const
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
}
|
|
1011
|
-
schemaPaths.push("src/schema/");
|
|
1012
|
-
}
|
|
1013
|
-
}
|
|
1014
|
-
const diff = await runLogged({
|
|
1015
|
-
cwd: repoRoot,
|
|
1016
|
-
env: safeEnv,
|
|
1017
|
-
label: "git diff (schema)",
|
|
1018
|
-
command: "git",
|
|
1019
|
-
args: ["diff", "origin/main", "HEAD", "--", ...schemaPaths],
|
|
1020
|
-
logFile: path4.join(tmpDir, "git-schema-diff.log")
|
|
1021
|
-
});
|
|
1022
|
-
const diffRaw = String(diff.stdout ?? "");
|
|
1025
|
+
const target = resolveSchemaDiffTarget(repoRoot);
|
|
1026
|
+
const diffRaw = target ? String(
|
|
1027
|
+
(await runLogged({
|
|
1028
|
+
cwd: repoRoot,
|
|
1029
|
+
env: safeEnv,
|
|
1030
|
+
label: target.label,
|
|
1031
|
+
command: "git",
|
|
1032
|
+
args: ["diff", "origin/main", "HEAD", "--", ...target.paths],
|
|
1033
|
+
logFile: path4.join(tmpDir, "git-schema-diff.log")
|
|
1034
|
+
})).stdout ?? ""
|
|
1035
|
+
) : "";
|
|
1023
1036
|
const diffLimited = diffRaw.length > 1e4 ? diffRaw.slice(0, 1e4) : diffRaw;
|
|
1024
1037
|
await runLogged({
|
|
1025
1038
|
cwd: repoRoot,
|
|
@@ -1079,6 +1092,24 @@ async function notifyDeployment(repoRoot, tmpDir, params) {
|
|
|
1079
1092
|
}
|
|
1080
1093
|
|
|
1081
1094
|
// src/commands/ci/commands/ci-prod-github.ts
|
|
1095
|
+
function formatPlannerSource(result) {
|
|
1096
|
+
const planner = result.planner;
|
|
1097
|
+
if (!planner) return null;
|
|
1098
|
+
if (planner.source === "artifact") {
|
|
1099
|
+
return "Reused checked full preview artifact";
|
|
1100
|
+
}
|
|
1101
|
+
if (planner.reuseAttempted) {
|
|
1102
|
+
return "Inline planning after checked artifact fallback";
|
|
1103
|
+
}
|
|
1104
|
+
return "Inline planning";
|
|
1105
|
+
}
|
|
1106
|
+
function formatPlannerFallback(result) {
|
|
1107
|
+
const planner = result.planner;
|
|
1108
|
+
if (!planner?.reuseAttempted || planner.source === "artifact" || !planner.reuseReason) {
|
|
1109
|
+
return null;
|
|
1110
|
+
}
|
|
1111
|
+
return planner.reuseMessage ? `${planner.reuseReason}: ${planner.reuseMessage}` : planner.reuseReason;
|
|
1112
|
+
}
|
|
1082
1113
|
async function maybeNotifyExternal(params) {
|
|
1083
1114
|
if (params.skipNotify) return;
|
|
1084
1115
|
const runaAppUrl = process.env.RUNA_APP_URL?.trim() || "";
|
|
@@ -1119,6 +1150,17 @@ async function maybeAddGithubLabelAndComment(params) {
|
|
|
1119
1150
|
if (result.statementsExecuted > 0) {
|
|
1120
1151
|
changeSummaryLines.push(`- **Statements executed**: ${result.statementsExecuted}`);
|
|
1121
1152
|
}
|
|
1153
|
+
const plannerSource = formatPlannerSource(result);
|
|
1154
|
+
if (plannerSource) {
|
|
1155
|
+
changeSummaryLines.push(`- **Plan source**: ${plannerSource}`);
|
|
1156
|
+
}
|
|
1157
|
+
const plannerFallback = formatPlannerFallback(result);
|
|
1158
|
+
if (plannerFallback) {
|
|
1159
|
+
changeSummaryLines.push(`- **Plan reuse fallback**: ${plannerFallback}`);
|
|
1160
|
+
}
|
|
1161
|
+
if (result.planner?.hasChanges === false) {
|
|
1162
|
+
changeSummaryLines.push("- **Plan result**: No schema changes");
|
|
1163
|
+
}
|
|
1122
1164
|
if (result.hazards.length > 0) {
|
|
1123
1165
|
const hazardList = result.hazards.map((h) => ` - ${h}`).join("\n");
|
|
1124
1166
|
changeSummaryLines.push(`- **Hazards**:
|
|
@@ -1371,8 +1413,8 @@ var CiProdApplyWorkflow = class {
|
|
|
1371
1413
|
id: "schema-diff",
|
|
1372
1414
|
description: "Show schema diff",
|
|
1373
1415
|
run: async (ctx) => {
|
|
1374
|
-
await
|
|
1375
|
-
ctx.hasSchemaChanges =
|
|
1416
|
+
ctx.schemaChangeSummary = await collectSchemaChangeSummary(ctx.repoRoot, ctx.tmpDir);
|
|
1417
|
+
ctx.hasSchemaChanges = ctx.schemaChangeSummary.hasChanges;
|
|
1376
1418
|
if (ctx.hasSchemaChanges === false) {
|
|
1377
1419
|
ctx.logger.info("No schema changes detected \u2014 schema apply steps will be skipped");
|
|
1378
1420
|
} else if (ctx.hasSchemaChanges === null) {
|
|
@@ -1469,6 +1511,17 @@ var CiProdApplyWorkflow = class {
|
|
|
1469
1511
|
timeoutMs: ctx.options.applyTimeoutMs
|
|
1470
1512
|
}
|
|
1471
1513
|
);
|
|
1514
|
+
if (ctx.schemaApplyResult.planner) {
|
|
1515
|
+
ctx.summary.detected.dbPlanSource = ctx.schemaApplyResult.planner.source;
|
|
1516
|
+
ctx.summary.detected.dbPlanArtifactPath = ctx.schemaApplyResult.planner.path;
|
|
1517
|
+
ctx.summary.detected.dbPlanReuseAttempted = ctx.schemaApplyResult.planner.reuseAttempted;
|
|
1518
|
+
ctx.summary.detected.dbPlanReuseReason = ctx.schemaApplyResult.planner.reuseReason;
|
|
1519
|
+
ctx.summary.detected.dbPlanReuseMessage = ctx.schemaApplyResult.planner.reuseMessage;
|
|
1520
|
+
ctx.summary.detected.dbPlanHasChanges = ctx.schemaApplyResult.planner.hasChanges;
|
|
1521
|
+
ctx.summary.detected.dbSourceFingerprint = ctx.schemaApplyResult.planner.sourceFingerprint;
|
|
1522
|
+
ctx.summary.detected.dbTargetFingerprint = ctx.schemaApplyResult.planner.targetFingerprint;
|
|
1523
|
+
ctx.summary.detected.dbTargetFingerprintMatched = ctx.schemaApplyResult.planner.targetFingerprintMatched;
|
|
1524
|
+
}
|
|
1472
1525
|
} catch (error) {
|
|
1473
1526
|
ctx.logger.error("Schema apply failed; attempting rollback from snapshot");
|
|
1474
1527
|
await snapshotRestoreLatest(
|
|
@@ -1560,18 +1613,18 @@ var CiProdApplyWorkflow = class {
|
|
|
1560
1613
|
console.log(`${prefix} done (${duration}ms)`);
|
|
1561
1614
|
} catch (error) {
|
|
1562
1615
|
const message = error instanceof Error ? error.message : String(error);
|
|
1563
|
-
const
|
|
1616
|
+
const classified = classifyCiProdApplyError(error);
|
|
1564
1617
|
phases.push({
|
|
1565
1618
|
id: step.id,
|
|
1566
1619
|
label: step.description,
|
|
1567
|
-
status:
|
|
1620
|
+
status: classified.retryable ? "timeout" : "failed",
|
|
1568
1621
|
startedAt: new Date(startedAt).toISOString(),
|
|
1569
1622
|
endedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1570
1623
|
durationMs: Date.now() - startedAt,
|
|
1571
1624
|
error: {
|
|
1572
|
-
code:
|
|
1625
|
+
code: classified.code === "CI_ERROR" ? "CI_PROD_APPLY_FAILED" : classified.code,
|
|
1573
1626
|
message,
|
|
1574
|
-
retryable:
|
|
1627
|
+
retryable: classified.retryable,
|
|
1575
1628
|
phase: step.id
|
|
1576
1629
|
}
|
|
1577
1630
|
});
|
|
@@ -1710,7 +1763,7 @@ function guardProductionDeployment(dbUrl, commit) {
|
|
|
1710
1763
|
};
|
|
1711
1764
|
}
|
|
1712
1765
|
if (!idempotencyResult.checkSuccessful && idempotencyResult.error) {
|
|
1713
|
-
|
|
1766
|
+
process.stderr.write(
|
|
1714
1767
|
`[WARN] Idempotency check failed: ${idempotencyResult.error}. Proceeding with deployment.`
|
|
1715
1768
|
);
|
|
1716
1769
|
}
|
|
@@ -1861,7 +1914,7 @@ var ciProdApplyCommand = new Command("prod-apply").description("Apply production
|
|
|
1861
1914
|
console.log(`status: ${summary.status}`);
|
|
1862
1915
|
console.log(`summary: ${path4.relative(repoRoot, summaryPath)}`);
|
|
1863
1916
|
logNextActions([
|
|
1864
|
-
"Inspect .runa/tmp
|
|
1917
|
+
"Inspect .runa/tmp artifacts (db-apply-production.json, snapshot-*.log)",
|
|
1865
1918
|
"If rollback succeeded, database should be restored to pre-deploy snapshot"
|
|
1866
1919
|
]);
|
|
1867
1920
|
await appendGithubStepSummary(
|
|
@@ -4002,6 +4055,7 @@ function buildSkipResult(hasUrl) {
|
|
|
4002
4055
|
return {
|
|
4003
4056
|
preview: {
|
|
4004
4057
|
executed: false,
|
|
4058
|
+
profile: null,
|
|
4005
4059
|
planSql: null,
|
|
4006
4060
|
hazards: [],
|
|
4007
4061
|
hasChanges: false,
|
|
@@ -4196,6 +4250,7 @@ var PreviewOutputSchema = z.object({
|
|
|
4196
4250
|
filteredPlanSql: z.string().optional(),
|
|
4197
4251
|
hazards: z.array(z.string()),
|
|
4198
4252
|
checkOnly: z.boolean().optional(),
|
|
4253
|
+
previewProfile: z.enum(["compare-only", "full"]).optional(),
|
|
4199
4254
|
metrics: z.object({
|
|
4200
4255
|
totalMs: z.number(),
|
|
4201
4256
|
idempotentMs: z.number().optional(),
|
|
@@ -4219,6 +4274,7 @@ function tryParseStructuredOutput(stdout) {
|
|
|
4219
4274
|
hazards: data.hazards,
|
|
4220
4275
|
hasChanges,
|
|
4221
4276
|
checkOnly: data.checkOnly ?? false,
|
|
4277
|
+
previewProfile: data.previewProfile ?? null,
|
|
4222
4278
|
metrics: data.metrics
|
|
4223
4279
|
};
|
|
4224
4280
|
} catch {
|
|
@@ -4334,6 +4390,7 @@ function buildErrorResult(error, databaseUrl) {
|
|
|
4334
4390
|
return {
|
|
4335
4391
|
preview: {
|
|
4336
4392
|
executed: true,
|
|
4393
|
+
profile: null,
|
|
4337
4394
|
planSql: null,
|
|
4338
4395
|
hazards: [],
|
|
4339
4396
|
hasChanges: false,
|
|
@@ -4349,6 +4406,7 @@ function buildPreviewFromOutput(stdout, _stderr, fullOutput, repoRoot, productio
|
|
|
4349
4406
|
hazards: structured.hazards,
|
|
4350
4407
|
hazardDetails: void 0,
|
|
4351
4408
|
hasChanges: structured.hasChanges,
|
|
4409
|
+
profile: structured.previewProfile,
|
|
4352
4410
|
error: null
|
|
4353
4411
|
};
|
|
4354
4412
|
}
|
|
@@ -4368,6 +4426,7 @@ function buildPreviewFromOutput(stdout, _stderr, fullOutput, repoRoot, productio
|
|
|
4368
4426
|
hazards,
|
|
4369
4427
|
hazardDetails: hazardDetails.length > 0 ? hazardDetails : void 0,
|
|
4370
4428
|
hasChanges,
|
|
4429
|
+
profile: null,
|
|
4371
4430
|
error: enhanceConnectionError(rawError, productionUrl)
|
|
4372
4431
|
};
|
|
4373
4432
|
}
|
|
@@ -4381,6 +4440,7 @@ function buildSuccessPreviewResult(exitCode, stdout, stderr, fullOutput, repoRoo
|
|
|
4381
4440
|
hazards: preview.hazards,
|
|
4382
4441
|
hazardDetails: preview.hazardDetails,
|
|
4383
4442
|
hasChanges: preview.hasChanges,
|
|
4443
|
+
profile: preview.profile ?? null,
|
|
4384
4444
|
error: isSuccess ? null : preview.error
|
|
4385
4445
|
}
|
|
4386
4446
|
};
|
|
@@ -4394,9 +4454,11 @@ var productionPreviewActor = fromPromise(
|
|
|
4394
4454
|
}
|
|
4395
4455
|
const logFile = path4.join(tmpDir, "ci-production-preview.log");
|
|
4396
4456
|
const timeoutMs = resolveProductionPreviewTimeoutMs();
|
|
4397
|
-
console.log("\u25B6 production preview (dry-run): runa db apply production --check --compare-only");
|
|
4398
4457
|
console.log(
|
|
4399
|
-
"\u25B6 production preview
|
|
4458
|
+
"\u25B6 production preview (dry-run): runa db preview production --profile compare-only"
|
|
4459
|
+
);
|
|
4460
|
+
console.log(
|
|
4461
|
+
"\u25B6 production preview details: db preview compare-only profile (plan + hazard extraction)"
|
|
4400
4462
|
);
|
|
4401
4463
|
console.log(`\u25B6 production preview timeout: ${formatDurationMs(timeoutMs)}`);
|
|
4402
4464
|
const heartbeat = startHeartbeat("production preview");
|
|
@@ -4406,7 +4468,7 @@ var productionPreviewActor = fromPromise(
|
|
|
4406
4468
|
try {
|
|
4407
4469
|
const child = execa(
|
|
4408
4470
|
"pnpm",
|
|
4409
|
-
["exec", "runa", "db", "
|
|
4471
|
+
["exec", "runa", "db", "preview", "production", "--profile", "compare-only"],
|
|
4410
4472
|
{
|
|
4411
4473
|
cwd: repoRoot,
|
|
4412
4474
|
env: {
|
|
@@ -4593,7 +4655,6 @@ function findBaseRef(repoRoot) {
|
|
|
4593
4655
|
stdio: ["pipe", "pipe", "pipe"]
|
|
4594
4656
|
});
|
|
4595
4657
|
if (checkOriginMain.status === 0) {
|
|
4596
|
-
console.log("[DEBUG] getSchemaGitDiff: Using origin/main as base");
|
|
4597
4658
|
return "origin/main";
|
|
4598
4659
|
}
|
|
4599
4660
|
const baseRef = process.env.GITHUB_BASE_REF;
|
|
@@ -4604,9 +4665,6 @@ function findBaseRef(repoRoot) {
|
|
|
4604
4665
|
stdio: ["pipe", "pipe", "pipe"]
|
|
4605
4666
|
});
|
|
4606
4667
|
if (checkBaseRef.status === 0) {
|
|
4607
|
-
console.log(
|
|
4608
|
-
`[DEBUG] getSchemaGitDiff: Using origin/${baseRef} as base (from GITHUB_BASE_REF)`
|
|
4609
|
-
);
|
|
4610
4668
|
return `origin/${baseRef}`;
|
|
4611
4669
|
}
|
|
4612
4670
|
}
|
|
@@ -4616,65 +4674,62 @@ function findBaseRef(repoRoot) {
|
|
|
4616
4674
|
stdio: ["pipe", "pipe", "pipe"]
|
|
4617
4675
|
});
|
|
4618
4676
|
if (mergeBaseResult.status === 0 && mergeBaseResult.stdout?.trim()) {
|
|
4619
|
-
console.log("[DEBUG] getSchemaGitDiff: Using merge-base as base");
|
|
4620
4677
|
return mergeBaseResult.stdout.trim();
|
|
4621
4678
|
}
|
|
4622
|
-
console.log("[DEBUG] getSchemaGitDiff: No valid base reference found");
|
|
4623
4679
|
return null;
|
|
4624
4680
|
}
|
|
4681
|
+
function extractSchemaGitDiffSnapshot(diffOutput) {
|
|
4682
|
+
const filesChanged = [];
|
|
4683
|
+
const seenFiles = /* @__PURE__ */ new Set();
|
|
4684
|
+
for (const match of diffOutput.matchAll(/^diff --git a\/(.+?) b\/(.+)$/gm)) {
|
|
4685
|
+
const filePath = match[2] ?? match[1];
|
|
4686
|
+
if (!filePath || seenFiles.has(filePath)) {
|
|
4687
|
+
continue;
|
|
4688
|
+
}
|
|
4689
|
+
seenFiles.add(filePath);
|
|
4690
|
+
filesChanged.push(filePath);
|
|
4691
|
+
}
|
|
4692
|
+
if (filesChanged.length === 0) {
|
|
4693
|
+
return null;
|
|
4694
|
+
}
|
|
4695
|
+
let linesAdded = 0;
|
|
4696
|
+
let linesDeleted = 0;
|
|
4697
|
+
for (const line of diffOutput.split("\n")) {
|
|
4698
|
+
if (line.startsWith("+") && !line.startsWith("+++")) {
|
|
4699
|
+
linesAdded += 1;
|
|
4700
|
+
continue;
|
|
4701
|
+
}
|
|
4702
|
+
if (line.startsWith("-") && !line.startsWith("---")) {
|
|
4703
|
+
linesDeleted += 1;
|
|
4704
|
+
}
|
|
4705
|
+
}
|
|
4706
|
+
const maxLines = 100;
|
|
4707
|
+
const diffLines = diffOutput.split("\n");
|
|
4708
|
+
const diffSummary = diffLines.length > maxLines ? `${diffLines.slice(0, maxLines).join("\n")}
|
|
4709
|
+
... (${diffLines.length - maxLines} more lines)` : diffOutput;
|
|
4710
|
+
return {
|
|
4711
|
+
filesChanged,
|
|
4712
|
+
linesAdded,
|
|
4713
|
+
linesDeleted,
|
|
4714
|
+
diffSummary
|
|
4715
|
+
};
|
|
4716
|
+
}
|
|
4625
4717
|
function getSchemaGitDiff(repoRoot) {
|
|
4626
4718
|
try {
|
|
4627
4719
|
const baseRef = findBaseRef(repoRoot);
|
|
4628
4720
|
if (!baseRef) {
|
|
4629
|
-
console.log("[DEBUG] getSchemaGitDiff: Cannot determine base reference, skipping diff");
|
|
4630
|
-
return null;
|
|
4631
|
-
}
|
|
4632
|
-
const filesResult = spawnSync(
|
|
4633
|
-
"git",
|
|
4634
|
-
["diff", "--name-only", `${baseRef}...HEAD`, "--", "supabase/schemas/"],
|
|
4635
|
-
{ cwd: repoRoot, encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }
|
|
4636
|
-
);
|
|
4637
|
-
if (filesResult.status !== 0) {
|
|
4638
|
-
console.log(
|
|
4639
|
-
`[DEBUG] getSchemaGitDiff: git diff failed: ${filesResult.stderr || "unknown error"}`
|
|
4640
|
-
);
|
|
4641
|
-
return null;
|
|
4642
|
-
}
|
|
4643
|
-
const filesChanged = (filesResult.stdout || "").split("\n").filter((f) => f.trim().length > 0);
|
|
4644
|
-
console.log(`[DEBUG] getSchemaGitDiff: Found ${filesChanged.length} changed schema file(s)`);
|
|
4645
|
-
if (filesChanged.length === 0) {
|
|
4646
4721
|
return null;
|
|
4647
4722
|
}
|
|
4648
|
-
const statResult = spawnSync(
|
|
4649
|
-
"git",
|
|
4650
|
-
["diff", "--stat", `${baseRef}...HEAD`, "--", "supabase/schemas/"],
|
|
4651
|
-
{ cwd: repoRoot, encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }
|
|
4652
|
-
);
|
|
4653
|
-
const statOutput = statResult.stdout || "";
|
|
4654
|
-
const statMatch = statOutput.match(/(\d+) insertions?\(\+\)/);
|
|
4655
|
-
const delMatch = statOutput.match(/(\d+) deletions?\(-\)/);
|
|
4656
|
-
const linesAdded = statMatch ? Number.parseInt(statMatch[1], 10) : 0;
|
|
4657
|
-
const linesDeleted = delMatch ? Number.parseInt(delMatch[1], 10) : 0;
|
|
4658
4723
|
const diffResult = spawnSync(
|
|
4659
4724
|
"git",
|
|
4660
4725
|
["diff", "--no-color", `${baseRef}...HEAD`, "--", "supabase/schemas/"],
|
|
4661
4726
|
{ cwd: repoRoot, encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }
|
|
4662
4727
|
);
|
|
4663
|
-
|
|
4664
|
-
|
|
4665
|
-
|
|
4666
|
-
|
|
4667
|
-
|
|
4668
|
-
return {
|
|
4669
|
-
filesChanged,
|
|
4670
|
-
linesAdded,
|
|
4671
|
-
linesDeleted,
|
|
4672
|
-
diffSummary
|
|
4673
|
-
};
|
|
4674
|
-
} catch (error) {
|
|
4675
|
-
console.log(
|
|
4676
|
-
`[DEBUG] getSchemaGitDiff: Exception: ${error instanceof Error ? error.message : String(error)}`
|
|
4677
|
-
);
|
|
4728
|
+
if (diffResult.status !== 0) {
|
|
4729
|
+
return null;
|
|
4730
|
+
}
|
|
4731
|
+
return extractSchemaGitDiffSnapshot(diffResult.stdout || "");
|
|
4732
|
+
} catch {
|
|
4678
4733
|
return null;
|
|
4679
4734
|
}
|
|
4680
4735
|
}
|
|
@@ -5249,7 +5304,7 @@ function shouldSkipSchemaPostCheck(context) {
|
|
|
5249
5304
|
return context.mode === "ci-pr-local" && context.executionEnv === "github-actions";
|
|
5250
5305
|
}
|
|
5251
5306
|
function shouldReuseCiReferenceStats(context) {
|
|
5252
|
-
return context.mode !== "ci-local" && context.schemaApplied && context.schemaDrift !== null && context.schemaDrift?.hasDrift === false;
|
|
5307
|
+
return context.mode !== "ci-local" && context.schemaApplied && context.schemaDrift !== null && context.schemaDrift?.checkExecuted === true && context.schemaDrift?.hasDrift === false;
|
|
5253
5308
|
}
|
|
5254
5309
|
function mergeLayerResults(coreResults, e2eResults) {
|
|
5255
5310
|
return { ...coreResults, ...e2eResults };
|
|
@@ -5385,6 +5440,11 @@ init_esm_shims();
|
|
|
5385
5440
|
|
|
5386
5441
|
// src/commands/ci/commands/ci-pr-capabilities.ts
|
|
5387
5442
|
init_esm_shims();
|
|
5443
|
+
|
|
5444
|
+
// src/commands/ci/commands/layer4-discovery.ts
|
|
5445
|
+
init_esm_shims();
|
|
5446
|
+
|
|
5447
|
+
// src/commands/ci/commands/ci-pr-capabilities.ts
|
|
5388
5448
|
async function pathExists(p) {
|
|
5389
5449
|
return promises.access(p).then(() => true).catch(() => false);
|
|
5390
5450
|
}
|
|
@@ -5523,12 +5583,22 @@ function isPlaywrightIgnored(filePath) {
|
|
|
5523
5583
|
async function detectGeneratedE2eCapabilities(params) {
|
|
5524
5584
|
const caps = /* @__PURE__ */ new Set();
|
|
5525
5585
|
const diagnostics = {};
|
|
5526
|
-
|
|
5527
|
-
|
|
5586
|
+
let layer4Dir;
|
|
5587
|
+
let tmpDir;
|
|
5588
|
+
try {
|
|
5589
|
+
const discovery = resolveLayer4DiscoverySync(params.repoRoot);
|
|
5590
|
+
layer4Dir = discovery.currentOutputDir;
|
|
5591
|
+
tmpDir = discovery.tmpDir;
|
|
5592
|
+
} catch (error) {
|
|
5593
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
5594
|
+
diagnostics["e2e.generatedLayer4Present"] = `Layer 4 discovery failed: ${message}`;
|
|
5595
|
+
diagnostics["e2e.generatedLayer4HasUnknown"] = "n/a (Layer 4 discovery failed)";
|
|
5596
|
+
return { capabilities: caps, diagnostics };
|
|
5597
|
+
}
|
|
5528
5598
|
const hasLayer4Dir = await pathExists(layer4Dir);
|
|
5529
5599
|
const hasTmpDir = await pathExists(tmpDir);
|
|
5530
5600
|
if (!hasLayer4Dir && !hasTmpDir) {
|
|
5531
|
-
diagnostics["e2e.generatedLayer4Present"] =
|
|
5601
|
+
diagnostics["e2e.generatedLayer4Present"] = `Missing current Layer 4 artifact dirs (${layer4Dir}, ${tmpDir})`;
|
|
5532
5602
|
diagnostics["e2e.generatedLayer4HasUnknown"] = "n/a (layer4 tests not present)";
|
|
5533
5603
|
return { capabilities: caps, diagnostics };
|
|
5534
5604
|
}
|
|
@@ -5687,37 +5757,89 @@ function assertCapabilities(params) {
|
|
|
5687
5757
|
);
|
|
5688
5758
|
}
|
|
5689
5759
|
|
|
5690
|
-
// src/commands/ci/
|
|
5691
|
-
|
|
5692
|
-
|
|
5693
|
-
|
|
5694
|
-
|
|
5695
|
-
|
|
5696
|
-
|
|
5697
|
-
|
|
5698
|
-
|
|
5699
|
-
|
|
5700
|
-
|
|
5701
|
-
|
|
5702
|
-
|
|
5703
|
-
|
|
5704
|
-
|
|
5705
|
-
|
|
5706
|
-
|
|
5707
|
-
|
|
5708
|
-
|
|
5709
|
-
|
|
5710
|
-
|
|
5711
|
-
|
|
5712
|
-
|
|
5713
|
-
|
|
5714
|
-
|
|
5715
|
-
|
|
5716
|
-
|
|
5717
|
-
|
|
5718
|
-
|
|
5719
|
-
|
|
5720
|
-
|
|
5760
|
+
// src/commands/ci/machine/actors/test/capabilities.ts
|
|
5761
|
+
var capabilitiesActor = fromPromise(
|
|
5762
|
+
async ({ input }) => {
|
|
5763
|
+
const { repoRoot, tmpDir, databaseUrlApp, baseUrl } = input;
|
|
5764
|
+
try {
|
|
5765
|
+
const detected = await detectCiCapabilities({
|
|
5766
|
+
repoRoot,
|
|
5767
|
+
tmpDir,
|
|
5768
|
+
databaseUrlApp,
|
|
5769
|
+
baseUrl
|
|
5770
|
+
});
|
|
5771
|
+
return { detected };
|
|
5772
|
+
} catch (error) {
|
|
5773
|
+
const errorMsg = error instanceof Error ? error.message : String(error);
|
|
5774
|
+
console.warn(`[Capabilities] Detection failed: ${errorMsg}`);
|
|
5775
|
+
return {
|
|
5776
|
+
detected: { capabilities: [], diagnostics: {} },
|
|
5777
|
+
error: errorMsg
|
|
5778
|
+
};
|
|
5779
|
+
}
|
|
5780
|
+
}
|
|
5781
|
+
);
|
|
5782
|
+
fromPromise(
|
|
5783
|
+
async ({ input }) => {
|
|
5784
|
+
const { config, repoKind, layer, detected } = input;
|
|
5785
|
+
try {
|
|
5786
|
+
const required = resolveRequiredCapabilities({ config, repoKind, layer });
|
|
5787
|
+
const missing = required.filter((r) => !detected.capabilities.includes(r));
|
|
5788
|
+
if (missing.length > 0) {
|
|
5789
|
+
assertCapabilities({
|
|
5790
|
+
required,
|
|
5791
|
+
detected,
|
|
5792
|
+
label: `Layer ${layer} capability gate`
|
|
5793
|
+
});
|
|
5794
|
+
}
|
|
5795
|
+
return { passed: true, required, missing: [] };
|
|
5796
|
+
} catch (error) {
|
|
5797
|
+
const required = resolveRequiredCapabilities({ config, repoKind, layer });
|
|
5798
|
+
const missing = required.filter((r) => !detected.capabilities.includes(r));
|
|
5799
|
+
return {
|
|
5800
|
+
passed: false,
|
|
5801
|
+
required,
|
|
5802
|
+
missing,
|
|
5803
|
+
error: error instanceof Error ? error.message : String(error)
|
|
5804
|
+
};
|
|
5805
|
+
}
|
|
5806
|
+
}
|
|
5807
|
+
);
|
|
5808
|
+
|
|
5809
|
+
// src/commands/ci/machine/actors/test/layer-content.ts
|
|
5810
|
+
init_esm_shims();
|
|
5811
|
+
|
|
5812
|
+
// src/commands/ci/commands/ci-layer-content.ts
|
|
5813
|
+
init_esm_shims();
|
|
5814
|
+
var MAX_FILE_SIZE_BYTES = 10 * 1024 * 1024;
|
|
5815
|
+
var SAFE_GLOB_OPTIONS = {
|
|
5816
|
+
follow: false
|
|
5817
|
+
// Don't follow symlinks (security + performance)
|
|
5818
|
+
};
|
|
5819
|
+
function createBaseStatus(layer, contentType, source = "manifest") {
|
|
5820
|
+
return { layer, contentType, source };
|
|
5821
|
+
}
|
|
5822
|
+
function createSuccessStatus(base, count, source) {
|
|
5823
|
+
return {
|
|
5824
|
+
...base,
|
|
5825
|
+
hasContent: true,
|
|
5826
|
+
count,
|
|
5827
|
+
source: source ?? base.source
|
|
5828
|
+
};
|
|
5829
|
+
}
|
|
5830
|
+
function createSkipStatus(base, skipReason) {
|
|
5831
|
+
return {
|
|
5832
|
+
...base,
|
|
5833
|
+
hasContent: false,
|
|
5834
|
+
count: 0,
|
|
5835
|
+
skipReason
|
|
5836
|
+
};
|
|
5837
|
+
}
|
|
5838
|
+
function safeReadFile(filePath) {
|
|
5839
|
+
try {
|
|
5840
|
+
const lstats = lstatSync(filePath);
|
|
5841
|
+
if (lstats.isSymbolicLink()) {
|
|
5842
|
+
return null;
|
|
5721
5843
|
}
|
|
5722
5844
|
const stats = statSync(filePath);
|
|
5723
5845
|
if (stats.size > MAX_FILE_SIZE_BYTES) {
|
|
@@ -6170,71 +6292,36 @@ async function detectLayerContent(repoRoot, selectedLayers, manifestPath) {
|
|
|
6170
6292
|
return { layers, skippedLayers, runnableLayers };
|
|
6171
6293
|
}
|
|
6172
6294
|
|
|
6173
|
-
// src/commands/ci/machine/actors/test/
|
|
6174
|
-
var
|
|
6295
|
+
// src/commands/ci/machine/actors/test/layer-content.ts
|
|
6296
|
+
var layerContentActor = fromPromise(
|
|
6175
6297
|
async ({ input }) => {
|
|
6176
|
-
const { repoRoot,
|
|
6298
|
+
const { repoRoot, selectedLayers } = input;
|
|
6177
6299
|
try {
|
|
6178
|
-
const detected = await detectCiCapabilities({
|
|
6179
|
-
repoRoot,
|
|
6180
|
-
tmpDir,
|
|
6181
|
-
databaseUrlApp,
|
|
6182
|
-
baseUrl
|
|
6183
|
-
});
|
|
6184
6300
|
const layerContent = await detectLayerContent(repoRoot, selectedLayers);
|
|
6185
|
-
return {
|
|
6301
|
+
return { layerContent };
|
|
6186
6302
|
} catch (error) {
|
|
6187
|
-
const
|
|
6188
|
-
console.warn(`[
|
|
6189
|
-
const fallbackLayers = {};
|
|
6190
|
-
for (const layer of selectedLayers) {
|
|
6191
|
-
fallbackLayers[layer] = {
|
|
6192
|
-
layer,
|
|
6193
|
-
hasContent: true,
|
|
6194
|
-
// Conservative: assume content exists
|
|
6195
|
-
contentType: "machines",
|
|
6196
|
-
// Placeholder
|
|
6197
|
-
count: 0,
|
|
6198
|
-
source: "filesystem",
|
|
6199
|
-
skipReason: `Detection failed: ${errorMsg}`
|
|
6200
|
-
// Track why we're using fallback
|
|
6201
|
-
};
|
|
6202
|
-
}
|
|
6303
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
6304
|
+
console.warn(`[Layer Content] Detection failed: ${errorMessage}`);
|
|
6203
6305
|
const fallbackLayerContent = {
|
|
6204
|
-
layers:
|
|
6306
|
+
layers: Object.fromEntries(
|
|
6307
|
+
selectedLayers.map((layer) => [
|
|
6308
|
+
layer,
|
|
6309
|
+
{
|
|
6310
|
+
layer,
|
|
6311
|
+
hasContent: true,
|
|
6312
|
+
contentType: "machines",
|
|
6313
|
+
count: 0,
|
|
6314
|
+
source: "filesystem",
|
|
6315
|
+
skipReason: `Detection failed: ${errorMessage}`
|
|
6316
|
+
}
|
|
6317
|
+
])
|
|
6318
|
+
),
|
|
6205
6319
|
skippedLayers: [],
|
|
6206
6320
|
runnableLayers: selectedLayers
|
|
6207
6321
|
};
|
|
6208
6322
|
return {
|
|
6209
|
-
detected: { capabilities: [], diagnostics: {} },
|
|
6210
6323
|
layerContent: fallbackLayerContent,
|
|
6211
|
-
error:
|
|
6212
|
-
};
|
|
6213
|
-
}
|
|
6214
|
-
}
|
|
6215
|
-
);
|
|
6216
|
-
fromPromise(
|
|
6217
|
-
async ({ input }) => {
|
|
6218
|
-
const { config, repoKind, layer, detected } = input;
|
|
6219
|
-
try {
|
|
6220
|
-
const required = resolveRequiredCapabilities({ config, repoKind, layer });
|
|
6221
|
-
const missing = required.filter((r) => !detected.capabilities.includes(r));
|
|
6222
|
-
if (missing.length > 0) {
|
|
6223
|
-
assertCapabilities({
|
|
6224
|
-
required,
|
|
6225
|
-
detected,
|
|
6226
|
-
label: `Layer ${layer} capability gate`
|
|
6227
|
-
});
|
|
6228
|
-
}
|
|
6229
|
-
return { passed: true, required, missing: [] };
|
|
6230
|
-
} catch (error) {
|
|
6231
|
-
const required = resolveRequiredCapabilities({ config, repoKind, layer });
|
|
6232
|
-
const missing = required.filter((r) => !detected.capabilities.includes(r));
|
|
6233
|
-
return {
|
|
6234
|
-
passed: false,
|
|
6235
|
-
required,
|
|
6236
|
-
missing,
|
|
6237
|
-
error: error instanceof Error ? error.message : String(error)
|
|
6324
|
+
error: errorMessage
|
|
6238
6325
|
};
|
|
6239
6326
|
}
|
|
6240
6327
|
}
|
|
@@ -6502,9 +6589,6 @@ function getEffectiveMode(context) {
|
|
|
6502
6589
|
function isCiLocalMode(context) {
|
|
6503
6590
|
return getEffectiveMode(context) === "ci-local";
|
|
6504
6591
|
}
|
|
6505
|
-
function isCiPrLocalMode(context) {
|
|
6506
|
-
return getEffectiveMode(context) === "ci-pr-local";
|
|
6507
|
-
}
|
|
6508
6592
|
function isCiPrMode(context) {
|
|
6509
6593
|
return getEffectiveMode(context) === "ci-pr-local";
|
|
6510
6594
|
}
|
|
@@ -6526,7 +6610,9 @@ function shouldSkipStaticChecks(context) {
|
|
|
6526
6610
|
return shouldSkipStep(context, "staticChecks");
|
|
6527
6611
|
}
|
|
6528
6612
|
function shouldSkipBuild(context) {
|
|
6529
|
-
|
|
6613
|
+
if (shouldSkipStep(context, "build")) return true;
|
|
6614
|
+
const requiresBuild = context.selectedLayers.some((layer) => layer === 3 || layer === 4);
|
|
6615
|
+
return !requiresBuild;
|
|
6530
6616
|
}
|
|
6531
6617
|
function shouldSkipAppStart(context) {
|
|
6532
6618
|
return shouldSkipStep(context, "appStart");
|
|
@@ -6553,9 +6639,6 @@ function shouldPostGitHubComment(context) {
|
|
|
6553
6639
|
if (context.input.skipGithubComment === true) return false;
|
|
6554
6640
|
return true;
|
|
6555
6641
|
}
|
|
6556
|
-
function isBlockingPhase(context) {
|
|
6557
|
-
return isCiPrMode(context) && context.phase === "blocking";
|
|
6558
|
-
}
|
|
6559
6642
|
function isObservabilityPhase(context) {
|
|
6560
6643
|
return isCiPrMode(context) && context.phase === "observability";
|
|
6561
6644
|
}
|
|
@@ -6568,7 +6651,7 @@ function shouldRunPrExecutionPhase(context) {
|
|
|
6568
6651
|
}
|
|
6569
6652
|
function shouldRunPrObservabilityPhase(context) {
|
|
6570
6653
|
if (!isCiPrMode(context)) return true;
|
|
6571
|
-
return !
|
|
6654
|
+
return !isTestPhase(context);
|
|
6572
6655
|
}
|
|
6573
6656
|
function hasError(context) {
|
|
6574
6657
|
return context.error !== null;
|
|
@@ -7315,9 +7398,10 @@ function truncateSql(sql, maxLength) {
|
|
|
7315
7398
|
}
|
|
7316
7399
|
function generatePreviewDetails(prodPreview) {
|
|
7317
7400
|
const truncatedSql = truncateSql(prodPreview.planSql || "", 2e3);
|
|
7401
|
+
const profileLabel = prodPreview.profile ?? "compare-only";
|
|
7318
7402
|
return [
|
|
7319
7403
|
"<details>",
|
|
7320
|
-
|
|
7404
|
+
`<summary>\u{1F4CB} \u672C\u756A\u30B9\u30AD\u30FC\u30DE\u30D7\u30EC\u30D3\u30E5\u30FC (${profileLabel})</summary>`,
|
|
7321
7405
|
"",
|
|
7322
7406
|
"**\u672C\u756A\u306B\u9069\u7528\u4E88\u5B9A\u306ESQL:**",
|
|
7323
7407
|
"",
|
|
@@ -7396,7 +7480,7 @@ function generatePreviewChangesDetectedSection(prodPreview, previewOnlyChanges)
|
|
|
7396
7480
|
];
|
|
7397
7481
|
if (previewOnlyChanges) {
|
|
7398
7482
|
lines.push(
|
|
7399
|
-
"> \u88DC\u8DB3: `Prod semantic diff` / count diff / index diff \u3067\u306F\u5DEE\u5206\u304C\u306A\u304F\u3001`
|
|
7483
|
+
"> \u88DC\u8DB3: `Prod semantic diff` / count diff / index diff \u3067\u306F\u5DEE\u5206\u304C\u306A\u304F\u3001`db preview --profile compare-only` \u306E\u307F\u304C\u5909\u66F4\u3092\u691C\u51FA\u3057\u307E\u3057\u305F\u3002",
|
|
7400
7484
|
"> \u3053\u308C\u306F ownership / session config / planner-only rewrite \u306A\u3069\u3001canonical diff \u3067\u306F\u8FFD\u308F\u306A\u3044\u5DEE\u5206\u3067\u3042\u308B\u53EF\u80FD\u6027\u304C\u3042\u308A\u307E\u3059\u3002",
|
|
7401
7485
|
""
|
|
7402
7486
|
);
|
|
@@ -7439,7 +7523,7 @@ function generateComparisonMismatchSection(schemaStats, expectedDrift) {
|
|
|
7439
7523
|
const lines = [
|
|
7440
7524
|
"> \u{1F536} **\u672C\u756ADB\u3068\u306E\u5DEE\u5206\u691C\u51FA - \u672C\u756A\u30C7\u30D7\u30ED\u30A4\u304C\u5FC5\u8981\u3067\u3059**",
|
|
7441
7525
|
">",
|
|
7442
|
-
"> `runa db
|
|
7526
|
+
"> `runa db preview production --profile compare-only` \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",
|
|
7443
7527
|
"> \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",
|
|
7444
7528
|
""
|
|
7445
7529
|
];
|
|
@@ -7498,13 +7582,6 @@ function generateProductionPreviewSection(phase, prodPreview, schemaDrift, schem
|
|
|
7498
7582
|
function getDeferredProductionPreviewSection(phase, prodPreview) {
|
|
7499
7583
|
if (prodPreview?.executed) return [];
|
|
7500
7584
|
switch (phase) {
|
|
7501
|
-
case "blocking":
|
|
7502
|
-
return [
|
|
7503
|
-
"**\u672C\u756A\u30D7\u30EC\u30D3\u30E5\u30FC**: \u23ED\uFE0F blocking phase \u3067\u306F\u5B9F\u884C\u3057\u307E\u305B\u3093",
|
|
7504
|
-
"",
|
|
7505
|
-
"> \u672C\u756A\u6BD4\u8F03\u3068 schema stats \u306F blocking CI \u306E\u30AF\u30EA\u30C6\u30A3\u30AB\u30EB\u30D1\u30B9\u304B\u3089\u5207\u308A\u96E2\u3055\u308C\u3066\u3044\u307E\u3059\u3002\u672C\u756A\u53CD\u6620\u524D\u306B\u306F `deploy-db.yml` \u304C compare-only dry-run \u3067\u518D\u691C\u8A3C\u3057\u307E\u3059\u3002",
|
|
7506
|
-
""
|
|
7507
|
-
];
|
|
7508
7585
|
case "test":
|
|
7509
7586
|
return [
|
|
7510
7587
|
"**\u672C\u756A\u30D7\u30EC\u30D3\u30E5\u30FC**: \u23ED\uFE0F phase=test \u306E\u305F\u3081\u30B9\u30AD\u30C3\u30D7",
|
|
@@ -7516,20 +7593,20 @@ function getDeferredProductionPreviewSection(phase, prodPreview) {
|
|
|
7516
7593
|
return [];
|
|
7517
7594
|
}
|
|
7518
7595
|
}
|
|
7519
|
-
function shouldShowDeployButton(productionPreview, schemaDrift,
|
|
7520
|
-
if (
|
|
7596
|
+
function shouldShowDeployButton(productionPreview, schemaDrift, hasSchemaChanges) {
|
|
7597
|
+
if (hasSchemaChanges) return true;
|
|
7521
7598
|
if (productionPreview?.error) return true;
|
|
7522
7599
|
if (!productionPreview?.executed && schemaDrift?.gitDiff?.filesChanged?.length) return true;
|
|
7523
7600
|
return false;
|
|
7524
7601
|
}
|
|
7525
7602
|
function generateDeploySection(exitCode, layerResults, phase, gitBranchName, productionPreview, schemaDrift, schemaStats, expectedDrift, env) {
|
|
7526
7603
|
if (!checkIfDeployable(exitCode, layerResults)) return [];
|
|
7527
|
-
if (phase === "
|
|
7604
|
+
if (phase === "test") return [];
|
|
7528
7605
|
const deployWorkflowUrl = env.repository ? `${env.serverUrl}/${env.repository}/actions/workflows/deploy-db.yml` : null;
|
|
7529
7606
|
const signals = getProductionSchemaSignals(productionPreview, schemaStats, expectedDrift);
|
|
7530
|
-
const
|
|
7531
|
-
const headerEmoji =
|
|
7532
|
-
const headerText =
|
|
7607
|
+
const hasSchemaChanges = signals.requiresDeploy;
|
|
7608
|
+
const headerEmoji = hasSchemaChanges ? "\u{1F6A8}" : "\u{1F680}";
|
|
7609
|
+
const headerText = hasSchemaChanges ? "\u672C\u756A\u30C7\u30D7\u30ED\u30A4 (\u30B9\u30AD\u30FC\u30DE\u5909\u66F4\u3042\u308A!)" : "\u672C\u756A\u30C7\u30D7\u30ED\u30A4";
|
|
7533
7610
|
const lines = ["---", "", `### ${headerEmoji} ${headerText}`, ""];
|
|
7534
7611
|
if (exitCode !== 0)
|
|
7535
7612
|
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", "");
|
|
@@ -7542,9 +7619,9 @@ function generateDeploySection(exitCode, layerResults, phase, gitBranchName, pro
|
|
|
7542
7619
|
expectedDrift
|
|
7543
7620
|
)
|
|
7544
7621
|
);
|
|
7545
|
-
const showButton = shouldShowDeployButton(productionPreview, schemaDrift,
|
|
7622
|
+
const showButton = shouldShowDeployButton(productionPreview, schemaDrift, hasSchemaChanges);
|
|
7546
7623
|
if (deployWorkflowUrl && showButton) {
|
|
7547
|
-
const buttonText =
|
|
7624
|
+
const buttonText = hasSchemaChanges ? "\u26A1 \u30B9\u30AD\u30FC\u30DE\u5909\u66F4\u3092\u672C\u756A\u306B\u30C7\u30D7\u30ED\u30A4" : "\u25B6\uFE0F \u672C\u756A\u306B\u30C7\u30D7\u30ED\u30A4";
|
|
7548
7625
|
lines.push(
|
|
7549
7626
|
`**\u30C7\u30D7\u30ED\u30A4\u30D6\u30E9\u30F3\u30C1**: \`${gitBranchName}\``,
|
|
7550
7627
|
"",
|
|
@@ -7559,7 +7636,18 @@ function generateDeploySection(exitCode, layerResults, phase, gitBranchName, pro
|
|
|
7559
7636
|
function generateObservabilitySectionBody(input) {
|
|
7560
7637
|
const env = getGitHubEnv();
|
|
7561
7638
|
const gitBranchName = input.prContext?.headBranch ?? input.branchName ?? "unknown";
|
|
7562
|
-
const
|
|
7639
|
+
const deploySection = generateDeploySection(
|
|
7640
|
+
input.exitCode,
|
|
7641
|
+
input.layerResults,
|
|
7642
|
+
input.phase,
|
|
7643
|
+
gitBranchName,
|
|
7644
|
+
input.productionPreview,
|
|
7645
|
+
input.schemaDrift,
|
|
7646
|
+
input.schemaStats ?? null,
|
|
7647
|
+
input.expectedDrift ?? [],
|
|
7648
|
+
env
|
|
7649
|
+
);
|
|
7650
|
+
const standalonePreviewLines = deploySection.length === 0 ? generateProductionPreviewSection(
|
|
7563
7651
|
input.phase,
|
|
7564
7652
|
input.productionPreview,
|
|
7565
7653
|
input.schemaDrift,
|
|
@@ -7572,18 +7660,8 @@ function generateObservabilitySectionBody(input) {
|
|
|
7572
7660
|
input.schemaDrift?.gitDiff ?? null,
|
|
7573
7661
|
input.expectedDrift ?? []
|
|
7574
7662
|
),
|
|
7575
|
-
...
|
|
7576
|
-
...
|
|
7577
|
-
input.exitCode,
|
|
7578
|
-
input.layerResults,
|
|
7579
|
-
input.phase,
|
|
7580
|
-
gitBranchName,
|
|
7581
|
-
input.productionPreview,
|
|
7582
|
-
input.schemaDrift,
|
|
7583
|
-
input.schemaStats ?? null,
|
|
7584
|
-
input.expectedDrift ?? [],
|
|
7585
|
-
env
|
|
7586
|
-
)
|
|
7663
|
+
...standalonePreviewLines,
|
|
7664
|
+
...deploySection
|
|
7587
7665
|
];
|
|
7588
7666
|
return lines.join("\n").trim();
|
|
7589
7667
|
}
|
|
@@ -7652,9 +7730,9 @@ function generateCommentBody(input) {
|
|
|
7652
7730
|
|
|
7653
7731
|
// src/commands/ci/machine/formatters/sections/progress-comment.ts
|
|
7654
7732
|
init_esm_shims();
|
|
7655
|
-
function calculateTotalElapsed(
|
|
7656
|
-
if (!
|
|
7657
|
-
return Object.values(
|
|
7733
|
+
function calculateTotalElapsed(stepTimings) {
|
|
7734
|
+
if (!stepTimings) return 0;
|
|
7735
|
+
return Object.values(stepTimings).reduce((sum, t) => sum + (t ?? 0), 0);
|
|
7658
7736
|
}
|
|
7659
7737
|
function formatProgressBar(_currentStep, completedSteps, failedStep, skippedSteps) {
|
|
7660
7738
|
const total = CI_STEPS.length;
|
|
@@ -7807,12 +7885,12 @@ function generateInfoLine(branchName, supabaseUrl, runUrl) {
|
|
|
7807
7885
|
if (runUrl) items.push(`[\u{1F517} \u30EF\u30FC\u30AF\u30D5\u30ED\u30FC](${runUrl})`);
|
|
7808
7886
|
return [items.join(" \xB7 "), ""];
|
|
7809
7887
|
}
|
|
7810
|
-
function generateProgressSteps(currentStep, completedSteps, failedStep, skippedSteps,
|
|
7888
|
+
function generateProgressSteps(currentStep, completedSteps, failedStep, skippedSteps, stepTimings, schemaDrift, layerResults, productionPreview) {
|
|
7811
7889
|
const lines = ["### \u9032\u6357", ""];
|
|
7812
7890
|
for (const { step, label, detail } of CI_STEPS) {
|
|
7813
7891
|
const status = getStepStatus(step, currentStep, completedSteps, failedStep, skippedSteps);
|
|
7814
7892
|
const icon = getStepIcon(status);
|
|
7815
|
-
const timing =
|
|
7893
|
+
const timing = stepTimings?.[step];
|
|
7816
7894
|
const timingStr = timing !== void 0 ? ` \`${formatDuration3(timing)}\`` : "";
|
|
7817
7895
|
const stepDetail = getStepDetail(step, status, schemaDrift, layerResults);
|
|
7818
7896
|
const runningDetail = getRunningStepDetail(step, detail, productionPreview);
|
|
@@ -7847,7 +7925,7 @@ function getFixSuggestions(failedStep) {
|
|
|
7847
7925
|
],
|
|
7848
7926
|
observability: [
|
|
7849
7927
|
"production preview / schema stats \u306E\u30ED\u30B0\u3092\u78BA\u8A8D",
|
|
7850
|
-
"\u672C\u756A DB \u63A5\u7D9A\u60C5\u5831\u3068 compare-only
|
|
7928
|
+
"\u672C\u756A DB \u63A5\u7D9A\u60C5\u5831\u3068 db preview compare-only \u306E\u51FA\u529B\u3092\u78BA\u8A8D",
|
|
7851
7929
|
"schema stats \u304C\u5FC5\u8981\u306A\u30B1\u30FC\u30B9\u304B expected drift \u8A2D\u5B9A\u3092\u78BA\u8A8D"
|
|
7852
7930
|
],
|
|
7853
7931
|
staticChecks: [
|
|
@@ -7946,8 +8024,6 @@ function generateProductionSchemaSection(productionPreview, phase, deployStatus,
|
|
|
7946
8024
|
function getDeferredProductionPreviewMessage(phase, productionPreview) {
|
|
7947
8025
|
if (productionPreview?.executed) return null;
|
|
7948
8026
|
switch (phase) {
|
|
7949
|
-
case "blocking":
|
|
7950
|
-
return "\u23ED\uFE0F _blocking phase \u306E\u305F\u3081\u3001\u672C\u756A\u6BD4\u8F03\u3068 schema stats \u306F\u3053\u306E job \u3067\u306F\u5B9F\u884C\u3057\u307E\u305B\u3093_";
|
|
7951
8027
|
case "test":
|
|
7952
8028
|
return "\u23ED\uFE0F _phase=test \u306E\u305F\u3081\u3001\u672C\u756A\u6BD4\u8F03\u306F\u30B9\u30AD\u30C3\u30D7\u3055\u308C\u3066\u3044\u307E\u3059_";
|
|
7953
8029
|
default:
|
|
@@ -7996,14 +8072,15 @@ function appendChangesPendingSection(lines, productionPreview, env) {
|
|
|
7996
8072
|
if (!productionPreview) return;
|
|
7997
8073
|
const hazardCount = productionPreview.hazards.length;
|
|
7998
8074
|
const hazardText = hazardCount > 0 ? ` (\u26A0\uFE0F ${hazardCount}\u4EF6\u306E\u30CF\u30B6\u30FC\u30C9)` : "";
|
|
7999
|
-
|
|
8075
|
+
const profileText = productionPreview.profile ? ` [${productionPreview.profile}]` : "";
|
|
8076
|
+
lines.push(`\u26A0\uFE0F **\u5909\u66F4\u5F85\u3061**${profileText}${hazardText}`, "");
|
|
8000
8077
|
if (productionPreview.planSql) {
|
|
8001
8078
|
appendPlanSqlPreview(lines, productionPreview.planSql);
|
|
8002
8079
|
}
|
|
8003
8080
|
appendDeployLink(lines, env);
|
|
8004
8081
|
}
|
|
8005
8082
|
function generateDbDeploySection(phase, layerResults, gitBranchName, productionPreview, env) {
|
|
8006
|
-
if (phase === "
|
|
8083
|
+
if (phase === "test") return [];
|
|
8007
8084
|
if (!checkBlockingLayersPassed(layerResults)) return [];
|
|
8008
8085
|
if (productionPreview?.hasChanges) return [];
|
|
8009
8086
|
const deployWorkflowUrl = env.repository ? `${env.serverUrl}/${env.repository}/actions/workflows/deploy-db.yml` : null;
|
|
@@ -8036,13 +8113,13 @@ function generateProgressCommentBody(input) {
|
|
|
8036
8113
|
productionPreview,
|
|
8037
8114
|
deployStatus,
|
|
8038
8115
|
error,
|
|
8039
|
-
stepTimings
|
|
8116
|
+
stepTimings
|
|
8040
8117
|
} = input;
|
|
8041
8118
|
const env = getGitHubEnv();
|
|
8042
8119
|
const runUrl = env.repository && env.runId ? `${env.serverUrl}/${env.repository}/actions/runs/${env.runId}` : null;
|
|
8043
8120
|
const effectiveBranchName = branchName ?? input.prContext?.headBranch ?? "unknown";
|
|
8044
8121
|
const supabaseUrl = supabase?.supabaseUrl ?? "";
|
|
8045
|
-
const totalElapsed = calculateTotalElapsed(
|
|
8122
|
+
const totalElapsed = calculateTotalElapsed(stepTimings);
|
|
8046
8123
|
const gitBranchName = input.prContext?.headBranch ?? branchName ?? "unknown";
|
|
8047
8124
|
const lines = [
|
|
8048
8125
|
...generateProgressHeader(
|
|
@@ -8058,7 +8135,7 @@ function generateProgressCommentBody(input) {
|
|
|
8058
8135
|
completedSteps,
|
|
8059
8136
|
failedStep,
|
|
8060
8137
|
input.skippedSteps ?? [],
|
|
8061
|
-
|
|
8138
|
+
stepTimings,
|
|
8062
8139
|
schemaDrift,
|
|
8063
8140
|
input.layerResults,
|
|
8064
8141
|
productionPreview
|
|
@@ -8115,7 +8192,7 @@ function createCommentInput(context, options) {
|
|
|
8115
8192
|
originalSelectedLayers
|
|
8116
8193
|
};
|
|
8117
8194
|
}
|
|
8118
|
-
function createProgressCommentInput(context, currentStep, completedSteps, failedStep = null,
|
|
8195
|
+
function createProgressCommentInput(context, currentStep, completedSteps, failedStep = null, stepTimings) {
|
|
8119
8196
|
const phase = context.phase;
|
|
8120
8197
|
const skippedSteps = getSkippedStepsForPhase(phase);
|
|
8121
8198
|
return {
|
|
@@ -8131,7 +8208,7 @@ function createProgressCommentInput(context, currentStep, completedSteps, failed
|
|
|
8131
8208
|
layerResults: context.layerResults,
|
|
8132
8209
|
productionPreview: context.productionPreview,
|
|
8133
8210
|
error: context.error,
|
|
8134
|
-
stepTimings
|
|
8211
|
+
stepTimings
|
|
8135
8212
|
};
|
|
8136
8213
|
}
|
|
8137
8214
|
|
|
@@ -8321,7 +8398,12 @@ function createSummaryInput(context) {
|
|
|
8321
8398
|
endedAt: now,
|
|
8322
8399
|
durationMs: Date.now() - context.startTime,
|
|
8323
8400
|
repoKind: context.repoKind,
|
|
8324
|
-
detected: {
|
|
8401
|
+
detected: {
|
|
8402
|
+
ciProfile: context.input.internalProfile ?? "default",
|
|
8403
|
+
requestedLayers: context.input.layers ?? [1, 2, 3, 4],
|
|
8404
|
+
effectiveLayers: context.selectedLayers,
|
|
8405
|
+
phase: context.phase
|
|
8406
|
+
},
|
|
8325
8407
|
diagnostics: context.diagnostics,
|
|
8326
8408
|
steps: {},
|
|
8327
8409
|
layers: formatLayerSummary(context.layerResults),
|
|
@@ -8458,6 +8540,12 @@ function createStaticChecksInput(context) {
|
|
|
8458
8540
|
tmpDir: assertTmpDir(context)
|
|
8459
8541
|
};
|
|
8460
8542
|
}
|
|
8543
|
+
function createLayerContentInput(context) {
|
|
8544
|
+
return {
|
|
8545
|
+
repoRoot: assertRepoRoot(context),
|
|
8546
|
+
selectedLayers: context.selectedLayers
|
|
8547
|
+
};
|
|
8548
|
+
}
|
|
8461
8549
|
function createBuildAndPlaywrightInput(context) {
|
|
8462
8550
|
return {
|
|
8463
8551
|
repoRoot: assertRepoRoot(context),
|
|
@@ -8486,8 +8574,7 @@ function createCapabilitiesInput(context) {
|
|
|
8486
8574
|
repoRoot: assertRepoRoot(context),
|
|
8487
8575
|
tmpDir: assertTmpDir(context),
|
|
8488
8576
|
databaseUrlApp: getDatabaseUrlForRuntime(context),
|
|
8489
|
-
baseUrl: context.baseUrl ?? `http://localhost:${context.app?.port ?? 3e3}
|
|
8490
|
-
selectedLayers: context.selectedLayers
|
|
8577
|
+
baseUrl: context.appStarted ? context.baseUrl ?? `http://localhost:${context.app?.port ?? 3e3}` : null
|
|
8491
8578
|
};
|
|
8492
8579
|
}
|
|
8493
8580
|
function createRunCoreTestsInput(context) {
|
|
@@ -8511,7 +8598,94 @@ function deriveCoreLayerResults(output) {
|
|
|
8511
8598
|
return convertLayerResults(results);
|
|
8512
8599
|
}
|
|
8513
8600
|
|
|
8601
|
+
// src/commands/ci/machine/selectors.ts
|
|
8602
|
+
init_esm_shims();
|
|
8603
|
+
function getStateName(snapshot) {
|
|
8604
|
+
return getSnapshotStateName(snapshot);
|
|
8605
|
+
}
|
|
8606
|
+
function isComplete(snapshot) {
|
|
8607
|
+
return isSnapshotComplete(snapshot);
|
|
8608
|
+
}
|
|
8609
|
+
|
|
8514
8610
|
// src/commands/ci/machine/machine.ts
|
|
8611
|
+
var FLAT_EXECUTION_META = {
|
|
8612
|
+
setupRoles: {
|
|
8613
|
+
e2e: {
|
|
8614
|
+
observable: "log",
|
|
8615
|
+
assertions: [{ type: "log", contains: "Setting up database roles" }]
|
|
8616
|
+
}
|
|
8617
|
+
},
|
|
8618
|
+
staticChecks: {
|
|
8619
|
+
e2e: {
|
|
8620
|
+
observable: "log",
|
|
8621
|
+
assertions: [{ type: "log", contains: "Running static checks" }]
|
|
8622
|
+
}
|
|
8623
|
+
},
|
|
8624
|
+
layerContent: {
|
|
8625
|
+
e2e: {
|
|
8626
|
+
observable: "log",
|
|
8627
|
+
assertions: [{ type: "log", contains: "Detecting runnable layers" }]
|
|
8628
|
+
}
|
|
8629
|
+
},
|
|
8630
|
+
buildAndPlaywright: {
|
|
8631
|
+
e2e: {
|
|
8632
|
+
observable: "log",
|
|
8633
|
+
assertions: [
|
|
8634
|
+
{ type: "log", contains: "Building app" },
|
|
8635
|
+
{ type: "log", contains: "Installing Playwright" }
|
|
8636
|
+
]
|
|
8637
|
+
}
|
|
8638
|
+
},
|
|
8639
|
+
appStart: {
|
|
8640
|
+
e2e: {
|
|
8641
|
+
observable: "log",
|
|
8642
|
+
assertions: [{ type: "log", contains: "Starting app" }]
|
|
8643
|
+
}
|
|
8644
|
+
},
|
|
8645
|
+
capabilities: {
|
|
8646
|
+
e2e: {
|
|
8647
|
+
observable: "log",
|
|
8648
|
+
assertions: [{ type: "log", contains: "Detecting capabilities" }]
|
|
8649
|
+
}
|
|
8650
|
+
},
|
|
8651
|
+
runCoreTests: {
|
|
8652
|
+
e2e: {
|
|
8653
|
+
observable: "log",
|
|
8654
|
+
assertions: [{ type: "log", contains: "Running core tests" }]
|
|
8655
|
+
}
|
|
8656
|
+
}
|
|
8657
|
+
};
|
|
8658
|
+
function getSetupRolesApplied(output) {
|
|
8659
|
+
return output.skipped !== true;
|
|
8660
|
+
}
|
|
8661
|
+
function mergeSetupRolesStepOverrides(context, output, stepId) {
|
|
8662
|
+
if (output.skipped !== true) {
|
|
8663
|
+
return context.stepOverrides;
|
|
8664
|
+
}
|
|
8665
|
+
return {
|
|
8666
|
+
...context.stepOverrides,
|
|
8667
|
+
[stepId]: {
|
|
8668
|
+
status: "skipped",
|
|
8669
|
+
reason: output.skipReason ?? "Skipped because db:setup-roles is unavailable"
|
|
8670
|
+
}
|
|
8671
|
+
};
|
|
8672
|
+
}
|
|
8673
|
+
function getRunnableLayers(context, layerContent) {
|
|
8674
|
+
return filterRunnableLayers(context.selectedLayers, layerContent);
|
|
8675
|
+
}
|
|
8676
|
+
function getMergedLayerSkipReasons(context, layerContent) {
|
|
8677
|
+
return {
|
|
8678
|
+
...context.layerSkipReasons,
|
|
8679
|
+
...extractLayerSkipReasons(layerContent)
|
|
8680
|
+
};
|
|
8681
|
+
}
|
|
8682
|
+
function getDetectedCapabilities(output) {
|
|
8683
|
+
return output.detected?.capabilities ?? [];
|
|
8684
|
+
}
|
|
8685
|
+
function getCoreSuccessLayerResults(context, output, strategy) {
|
|
8686
|
+
const nextResults = convertLayerResults(output.results ?? []);
|
|
8687
|
+
return strategy === "merge" ? mergeLayerResults(context.layerResults, nextResults) : nextResults;
|
|
8688
|
+
}
|
|
8515
8689
|
var ciMachine = setup({
|
|
8516
8690
|
types: {},
|
|
8517
8691
|
actors: {
|
|
@@ -8534,6 +8708,7 @@ var ciMachine = setup({
|
|
|
8534
8708
|
playwrightInstall: playwrightInstallActor,
|
|
8535
8709
|
buildAndPlaywright: buildAndPlaywrightActor,
|
|
8536
8710
|
// Test
|
|
8711
|
+
layerContent: layerContentActor,
|
|
8537
8712
|
capabilities: capabilitiesActor,
|
|
8538
8713
|
runLayers: runLayersActor,
|
|
8539
8714
|
// Finalize
|
|
@@ -8541,7 +8716,7 @@ var ciMachine = setup({
|
|
|
8541
8716
|
},
|
|
8542
8717
|
guards: {
|
|
8543
8718
|
isCiLocalMode: ({ context }) => isCiLocalMode(context),
|
|
8544
|
-
isCiPrLocalMode: ({ context }) =>
|
|
8719
|
+
isCiPrLocalMode: ({ context }) => isCiPrMode(context),
|
|
8545
8720
|
isCiPrMode: ({ context }) => isCiPrMode(context),
|
|
8546
8721
|
shouldResetDb: ({ context }) => shouldResetDb(context),
|
|
8547
8722
|
shouldPullProduction: ({ context }) => shouldPullProduction(context),
|
|
@@ -8903,16 +9078,12 @@ var ciMachine = setup({
|
|
|
8903
9078
|
onDone: {
|
|
8904
9079
|
target: "staticChecks",
|
|
8905
9080
|
actions: assign({
|
|
8906
|
-
rolesSetup: ({ event }) => event.output
|
|
8907
|
-
stepOverrides: ({ context, event }) => (
|
|
8908
|
-
|
|
8909
|
-
|
|
8910
|
-
|
|
8911
|
-
|
|
8912
|
-
reason: event.output.skipReason ?? "Skipped because db:setup-roles is unavailable"
|
|
8913
|
-
}
|
|
8914
|
-
} : {}
|
|
8915
|
-
}),
|
|
9081
|
+
rolesSetup: ({ event }) => getSetupRolesApplied(event.output),
|
|
9082
|
+
stepOverrides: ({ context, event }) => mergeSetupRolesStepOverrides(
|
|
9083
|
+
context,
|
|
9084
|
+
event.output,
|
|
9085
|
+
"postSeedPr.execution.setupRoles"
|
|
9086
|
+
),
|
|
8916
9087
|
supabase: ({ context, event }) => mergeSetupRolesSupabase(context, event.output.appDatabaseUrl)
|
|
8917
9088
|
})
|
|
8918
9089
|
},
|
|
@@ -8923,7 +9094,7 @@ var ciMachine = setup({
|
|
|
8923
9094
|
}
|
|
8924
9095
|
},
|
|
8925
9096
|
staticChecks: {
|
|
8926
|
-
always: [{ guard: "shouldSkipStaticChecks", target: "
|
|
9097
|
+
always: [{ guard: "shouldSkipStaticChecks", target: "layerContent" }],
|
|
8927
9098
|
invoke: {
|
|
8928
9099
|
src: "staticChecks",
|
|
8929
9100
|
input: ({ context }) => createStaticChecksInput(context),
|
|
@@ -8937,7 +9108,7 @@ var ciMachine = setup({
|
|
|
8937
9108
|
})
|
|
8938
9109
|
},
|
|
8939
9110
|
{
|
|
8940
|
-
target: "
|
|
9111
|
+
target: "layerContent",
|
|
8941
9112
|
actions: assign({ staticChecksPassed: true })
|
|
8942
9113
|
}
|
|
8943
9114
|
],
|
|
@@ -8950,6 +9121,22 @@ var ciMachine = setup({
|
|
|
8950
9121
|
}
|
|
8951
9122
|
}
|
|
8952
9123
|
},
|
|
9124
|
+
layerContent: {
|
|
9125
|
+
invoke: {
|
|
9126
|
+
src: "layerContent",
|
|
9127
|
+
input: ({ context }) => createLayerContentInput(context),
|
|
9128
|
+
onDone: {
|
|
9129
|
+
target: "buildAndPlaywright",
|
|
9130
|
+
actions: assign({
|
|
9131
|
+
selectedLayers: ({ context, event }) => getRunnableLayers(context, event.output.layerContent),
|
|
9132
|
+
layerSkipReasons: ({ context, event }) => getMergedLayerSkipReasons(context, event.output.layerContent)
|
|
9133
|
+
})
|
|
9134
|
+
},
|
|
9135
|
+
onError: {
|
|
9136
|
+
target: "buildAndPlaywright"
|
|
9137
|
+
}
|
|
9138
|
+
}
|
|
9139
|
+
},
|
|
8953
9140
|
buildAndPlaywright: {
|
|
8954
9141
|
always: [{ guard: "shouldSkipBuild", target: "appStart" }],
|
|
8955
9142
|
invoke: {
|
|
@@ -9026,9 +9213,7 @@ var ciMachine = setup({
|
|
|
9026
9213
|
onDone: {
|
|
9027
9214
|
target: "runCoreTests",
|
|
9028
9215
|
actions: assign({
|
|
9029
|
-
capabilities: ({ event }) => event.output
|
|
9030
|
-
selectedLayers: ({ context, event }) => filterRunnableLayers(context.selectedLayers, event.output.layerContent),
|
|
9031
|
-
layerSkipReasons: ({ event }) => extractLayerSkipReasons(event.output.layerContent)
|
|
9216
|
+
capabilities: ({ event }) => getDetectedCapabilities(event.output)
|
|
9032
9217
|
})
|
|
9033
9218
|
},
|
|
9034
9219
|
onError: {
|
|
@@ -9056,10 +9241,7 @@ var ciMachine = setup({
|
|
|
9056
9241
|
target: "coreTestsComplete",
|
|
9057
9242
|
actions: assign({
|
|
9058
9243
|
testsRun: true,
|
|
9059
|
-
layerResults: ({ context, event }) =>
|
|
9060
|
-
const nextResults = convertLayerResults(event.output.results);
|
|
9061
|
-
return mergeLayerResults(context.layerResults, nextResults);
|
|
9062
|
-
}
|
|
9244
|
+
layerResults: ({ context, event }) => getCoreSuccessLayerResults(context, event.output, "merge")
|
|
9063
9245
|
})
|
|
9064
9246
|
}
|
|
9065
9247
|
],
|
|
@@ -9396,19 +9578,14 @@ var ciMachine = setup({
|
|
|
9396
9578
|
// Setup Roles (ci-pr modes only)
|
|
9397
9579
|
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
9398
9580
|
setupRoles: {
|
|
9399
|
-
meta:
|
|
9400
|
-
e2e: {
|
|
9401
|
-
observable: "log",
|
|
9402
|
-
assertions: [{ type: "log", contains: "Setting up database roles" }]
|
|
9403
|
-
}
|
|
9404
|
-
},
|
|
9581
|
+
meta: FLAT_EXECUTION_META.setupRoles,
|
|
9405
9582
|
invoke: {
|
|
9406
9583
|
src: "setupRoles",
|
|
9407
9584
|
input: ({ context }) => createSetupRolesInput(context),
|
|
9408
9585
|
onDone: {
|
|
9409
9586
|
target: "staticChecks",
|
|
9410
9587
|
actions: assign({
|
|
9411
|
-
rolesSetup:
|
|
9588
|
+
rolesSetup: ({ event }) => getSetupRolesApplied(event.output),
|
|
9412
9589
|
supabase: ({ context, event }) => mergeSetupRolesSupabase(context, event.output.appDatabaseUrl)
|
|
9413
9590
|
})
|
|
9414
9591
|
},
|
|
@@ -9423,13 +9600,8 @@ var ciMachine = setup({
|
|
|
9423
9600
|
// Static Checks (ci-pr modes only)
|
|
9424
9601
|
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
9425
9602
|
staticChecks: {
|
|
9426
|
-
meta:
|
|
9427
|
-
|
|
9428
|
-
observable: "log",
|
|
9429
|
-
assertions: [{ type: "log", contains: "Running static checks" }]
|
|
9430
|
-
}
|
|
9431
|
-
},
|
|
9432
|
-
always: [{ guard: "shouldSkipStaticChecks", target: "buildAndPlaywright" }],
|
|
9603
|
+
meta: FLAT_EXECUTION_META.staticChecks,
|
|
9604
|
+
always: [{ guard: "shouldSkipStaticChecks", target: "layerContent" }],
|
|
9433
9605
|
invoke: {
|
|
9434
9606
|
src: "staticChecks",
|
|
9435
9607
|
input: ({ context }) => createStaticChecksInput(context),
|
|
@@ -9443,7 +9615,7 @@ var ciMachine = setup({
|
|
|
9443
9615
|
})
|
|
9444
9616
|
},
|
|
9445
9617
|
{
|
|
9446
|
-
target: "
|
|
9618
|
+
target: "layerContent",
|
|
9447
9619
|
actions: assign({ staticChecksPassed: true })
|
|
9448
9620
|
}
|
|
9449
9621
|
],
|
|
@@ -9456,20 +9628,29 @@ var ciMachine = setup({
|
|
|
9456
9628
|
}
|
|
9457
9629
|
}
|
|
9458
9630
|
},
|
|
9631
|
+
layerContent: {
|
|
9632
|
+
meta: FLAT_EXECUTION_META.layerContent,
|
|
9633
|
+
invoke: {
|
|
9634
|
+
src: "layerContent",
|
|
9635
|
+
input: ({ context }) => createLayerContentInput(context),
|
|
9636
|
+
onDone: {
|
|
9637
|
+
target: "buildAndPlaywright",
|
|
9638
|
+
actions: assign({
|
|
9639
|
+
selectedLayers: ({ context, event }) => getRunnableLayers(context, event.output.layerContent),
|
|
9640
|
+
layerSkipReasons: ({ context, event }) => getMergedLayerSkipReasons(context, event.output.layerContent)
|
|
9641
|
+
})
|
|
9642
|
+
},
|
|
9643
|
+
onError: {
|
|
9644
|
+
target: "buildAndPlaywright"
|
|
9645
|
+
}
|
|
9646
|
+
}
|
|
9647
|
+
},
|
|
9459
9648
|
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
9460
9649
|
// Build and Playwright Install (ci-pr modes only, parallel)
|
|
9461
9650
|
// Optimization: Running build and playwright install concurrently saves ~30-60s
|
|
9462
9651
|
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
9463
9652
|
buildAndPlaywright: {
|
|
9464
|
-
meta:
|
|
9465
|
-
e2e: {
|
|
9466
|
-
observable: "log",
|
|
9467
|
-
assertions: [
|
|
9468
|
-
{ type: "log", contains: "Building app" },
|
|
9469
|
-
{ type: "log", contains: "Installing Playwright" }
|
|
9470
|
-
]
|
|
9471
|
-
}
|
|
9472
|
-
},
|
|
9653
|
+
meta: FLAT_EXECUTION_META.buildAndPlaywright,
|
|
9473
9654
|
always: [{ guard: "shouldSkipBuild", target: "appStart" }],
|
|
9474
9655
|
invoke: {
|
|
9475
9656
|
src: "buildAndPlaywright",
|
|
@@ -9510,12 +9691,7 @@ var ciMachine = setup({
|
|
|
9510
9691
|
// App Start (ci-pr modes only)
|
|
9511
9692
|
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
9512
9693
|
appStart: {
|
|
9513
|
-
meta:
|
|
9514
|
-
e2e: {
|
|
9515
|
-
observable: "log",
|
|
9516
|
-
assertions: [{ type: "log", contains: "Starting app" }]
|
|
9517
|
-
}
|
|
9518
|
-
},
|
|
9694
|
+
meta: FLAT_EXECUTION_META.appStart,
|
|
9519
9695
|
always: [{ guard: "shouldSkipAppStart", target: "capabilities" }],
|
|
9520
9696
|
invoke: {
|
|
9521
9697
|
src: "appStart",
|
|
@@ -9550,26 +9726,16 @@ var ciMachine = setup({
|
|
|
9550
9726
|
},
|
|
9551
9727
|
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
9552
9728
|
// Capability Detection (ci-pr modes only)
|
|
9553
|
-
// Also detects layer content to filter layers without testable content
|
|
9554
9729
|
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
9555
9730
|
capabilities: {
|
|
9556
|
-
meta:
|
|
9557
|
-
e2e: {
|
|
9558
|
-
observable: "log",
|
|
9559
|
-
assertions: [{ type: "log", contains: "Detecting capabilities" }]
|
|
9560
|
-
}
|
|
9561
|
-
},
|
|
9731
|
+
meta: FLAT_EXECUTION_META.capabilities,
|
|
9562
9732
|
invoke: {
|
|
9563
9733
|
src: "capabilities",
|
|
9564
9734
|
input: ({ context }) => createCapabilitiesInput(context),
|
|
9565
9735
|
onDone: {
|
|
9566
9736
|
target: "runCoreTests",
|
|
9567
9737
|
actions: assign({
|
|
9568
|
-
capabilities: ({ event }) => event.output
|
|
9569
|
-
// Filter to runnable layers only (those with content)
|
|
9570
|
-
selectedLayers: ({ context, event }) => filterRunnableLayers(context.selectedLayers, event.output.layerContent),
|
|
9571
|
-
// Store skip reasons for PR comment
|
|
9572
|
-
layerSkipReasons: ({ event }) => extractLayerSkipReasons(event.output.layerContent)
|
|
9738
|
+
capabilities: ({ event }) => getDetectedCapabilities(event.output)
|
|
9573
9739
|
})
|
|
9574
9740
|
},
|
|
9575
9741
|
onError: {
|
|
@@ -9585,12 +9751,7 @@ var ciMachine = setup({
|
|
|
9585
9751
|
// ci-pr: Only core layers (1,2,3) - E2E runs in e2ePhase
|
|
9586
9752
|
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
9587
9753
|
runCoreTests: {
|
|
9588
|
-
meta:
|
|
9589
|
-
e2e: {
|
|
9590
|
-
observable: "log",
|
|
9591
|
-
assertions: [{ type: "log", contains: "Running core tests" }]
|
|
9592
|
-
}
|
|
9593
|
-
},
|
|
9754
|
+
meta: FLAT_EXECUTION_META.runCoreTests,
|
|
9594
9755
|
invoke: {
|
|
9595
9756
|
src: "runLayers",
|
|
9596
9757
|
input: ({ context }) => createRunCoreTestsInput(context),
|
|
@@ -9612,7 +9773,7 @@ var ciMachine = setup({
|
|
|
9612
9773
|
target: "coreTestsComplete",
|
|
9613
9774
|
actions: assign({
|
|
9614
9775
|
testsRun: true,
|
|
9615
|
-
layerResults: ({ event }) =>
|
|
9776
|
+
layerResults: ({ context, event }) => getCoreSuccessLayerResults(context, event.output, "replace")
|
|
9616
9777
|
})
|
|
9617
9778
|
}
|
|
9618
9779
|
],
|
|
@@ -9835,18 +9996,94 @@ var ciMachine = setup({
|
|
|
9835
9996
|
},
|
|
9836
9997
|
output: ({ context }) => createOutput(context)
|
|
9837
9998
|
});
|
|
9838
|
-
function getStateName(snapshot) {
|
|
9839
|
-
return getSnapshotStateName(snapshot);
|
|
9840
|
-
}
|
|
9841
|
-
function isComplete(snapshot) {
|
|
9842
|
-
return isSnapshotComplete(snapshot);
|
|
9843
|
-
}
|
|
9844
9999
|
|
|
9845
10000
|
// src/commands/ci/machine/commands/machine-runner.ts
|
|
9846
10001
|
init_esm_shims();
|
|
9847
10002
|
|
|
9848
|
-
// src/commands/ci/machine/commands/step-
|
|
10003
|
+
// src/commands/ci/machine/commands/ci-step-registry.ts
|
|
9849
10004
|
init_esm_shims();
|
|
10005
|
+
var PROGRESS_STATE_TO_STEP = {
|
|
10006
|
+
setup: "setup",
|
|
10007
|
+
"setup.resolving": "setup",
|
|
10008
|
+
"setup.local": "setup",
|
|
10009
|
+
"setup.prLocal": "setup",
|
|
10010
|
+
"setup.complete": "setup",
|
|
10011
|
+
dbReset: "syncSchema",
|
|
10012
|
+
pullProduction: "syncSchema",
|
|
10013
|
+
syncSchema: "syncSchema",
|
|
10014
|
+
applySeeds: "applySeeds",
|
|
10015
|
+
productionPreview: "observability",
|
|
10016
|
+
collectSchemaStats: "observability",
|
|
10017
|
+
postSeedPr: "postSeedChecks",
|
|
10018
|
+
"postSeedPr.execution": "postSeedChecks",
|
|
10019
|
+
"postSeedPr.observability": "observability",
|
|
10020
|
+
"postSeedPr.execution.setupRoles": "postSeedChecks",
|
|
10021
|
+
"postSeedPr.observability.productionPreview": "observability",
|
|
10022
|
+
"postSeedPr.observability.collectSchemaStats": "observability",
|
|
10023
|
+
"postSeedPr.execution.staticChecks": "staticChecks",
|
|
10024
|
+
"postSeedPr.execution.layerContent": "build",
|
|
10025
|
+
"postSeedPr.execution.buildAndPlaywright": "build",
|
|
10026
|
+
"postSeedPr.execution.appStart": "build",
|
|
10027
|
+
"postSeedPr.execution.capabilities": "build",
|
|
10028
|
+
"postSeedPr.execution.runCoreTests": "runTests",
|
|
10029
|
+
"postSeedPr.execution.coreTestsComplete": "runTests",
|
|
10030
|
+
"postSeedPr.execution.coreTestsFailed": "runTests",
|
|
10031
|
+
"postSeedPr.execution.e2ePhase": "runTests",
|
|
10032
|
+
"postSeedPr.execution.done": "runTests",
|
|
10033
|
+
decidePath: "postSeedChecks",
|
|
10034
|
+
installPgTap: "applySeeds",
|
|
10035
|
+
setupRoles: "postSeedChecks",
|
|
10036
|
+
staticChecks: "staticChecks",
|
|
10037
|
+
layerContent: "build",
|
|
10038
|
+
buildAndPlaywright: "build",
|
|
10039
|
+
appStart: "build",
|
|
10040
|
+
capabilities: "build",
|
|
10041
|
+
runTests: "runTests",
|
|
10042
|
+
testsComplete: "runTests",
|
|
10043
|
+
testsFailed: "runTests",
|
|
10044
|
+
runCoreTests: "runTests",
|
|
10045
|
+
coreTestsComplete: "runTests",
|
|
10046
|
+
coreTestsFailed: "runTests",
|
|
10047
|
+
e2ePhase: "runTests",
|
|
10048
|
+
"e2ePhase.intermediateComment": "runTests",
|
|
10049
|
+
"e2ePhase.e2eTests": "runTests",
|
|
10050
|
+
finalize: "finalize",
|
|
10051
|
+
"finalize.writeSummary": "finalize",
|
|
10052
|
+
"finalize.postComment": "finalize",
|
|
10053
|
+
"finalize.complete": "finalize",
|
|
10054
|
+
done: "finalize",
|
|
10055
|
+
failed: "finalize"
|
|
10056
|
+
};
|
|
10057
|
+
var PROGRESS_STEP_ORDER = [
|
|
10058
|
+
"setup",
|
|
10059
|
+
"syncSchema",
|
|
10060
|
+
"applySeeds",
|
|
10061
|
+
"postSeedChecks",
|
|
10062
|
+
"observability",
|
|
10063
|
+
"staticChecks",
|
|
10064
|
+
"build",
|
|
10065
|
+
"runTests",
|
|
10066
|
+
"finalize"
|
|
10067
|
+
];
|
|
10068
|
+
var OBSERVABILITY_STEP_IDS = [
|
|
10069
|
+
"postSeedPr.observability.productionPreview",
|
|
10070
|
+
"postSeedPr.observability.collectSchemaStats"
|
|
10071
|
+
];
|
|
10072
|
+
var EXECUTION_STEP_IDS = [
|
|
10073
|
+
"postSeedPr.execution.setupRoles",
|
|
10074
|
+
"postSeedPr.execution.staticChecks",
|
|
10075
|
+
"postSeedPr.execution.layerContent",
|
|
10076
|
+
"postSeedPr.execution.buildAndPlaywright",
|
|
10077
|
+
"postSeedPr.execution.appStart",
|
|
10078
|
+
"postSeedPr.execution.capabilities",
|
|
10079
|
+
"postSeedPr.execution.runCoreTests",
|
|
10080
|
+
"postSeedPr.execution.e2ePhase"
|
|
10081
|
+
];
|
|
10082
|
+
var BUILD_PREPARATION_SUBSTEP_IDS = {
|
|
10083
|
+
build: "postSeedPr.execution.buildAndPlaywright.build",
|
|
10084
|
+
manifestGenerate: "postSeedPr.execution.buildAndPlaywright.manifestGenerate",
|
|
10085
|
+
playwrightInstall: "postSeedPr.execution.buildAndPlaywright.playwrightInstall"
|
|
10086
|
+
};
|
|
9850
10087
|
var STEP_METADATA = {
|
|
9851
10088
|
"setup.local": {
|
|
9852
10089
|
order: 10,
|
|
@@ -9888,74 +10125,80 @@ var STEP_METADATA = {
|
|
|
9888
10125
|
optional: true,
|
|
9889
10126
|
parentStep: "applySeeds"
|
|
9890
10127
|
},
|
|
9891
|
-
|
|
10128
|
+
[OBSERVABILITY_STEP_IDS[0]]: {
|
|
9892
10129
|
order: 50,
|
|
9893
10130
|
title: "Run production preview",
|
|
9894
10131
|
phase: "observability",
|
|
9895
10132
|
optional: true,
|
|
9896
10133
|
parentStep: "applySeeds"
|
|
9897
10134
|
},
|
|
9898
|
-
|
|
10135
|
+
[OBSERVABILITY_STEP_IDS[1]]: {
|
|
9899
10136
|
order: 60,
|
|
9900
10137
|
title: "Collect schema statistics",
|
|
9901
10138
|
phase: "observability",
|
|
9902
10139
|
optional: true,
|
|
9903
10140
|
parentStep: "applySeeds"
|
|
9904
10141
|
},
|
|
9905
|
-
|
|
10142
|
+
[EXECUTION_STEP_IDS[0]]: {
|
|
9906
10143
|
order: 70,
|
|
9907
10144
|
title: "Setup database roles",
|
|
9908
10145
|
phase: "db",
|
|
9909
10146
|
optional: true,
|
|
9910
10147
|
parentStep: "applySeeds"
|
|
9911
10148
|
},
|
|
9912
|
-
|
|
10149
|
+
[EXECUTION_STEP_IDS[1]]: {
|
|
9913
10150
|
order: 80,
|
|
9914
10151
|
title: "Run static checks",
|
|
9915
10152
|
phase: "build"
|
|
9916
10153
|
},
|
|
9917
|
-
|
|
10154
|
+
[EXECUTION_STEP_IDS[2]]: {
|
|
10155
|
+
order: 85,
|
|
10156
|
+
title: "Detect runnable test layers",
|
|
10157
|
+
phase: "build",
|
|
10158
|
+
optional: true
|
|
10159
|
+
},
|
|
10160
|
+
[EXECUTION_STEP_IDS[3]]: {
|
|
9918
10161
|
order: 90,
|
|
9919
10162
|
title: "Build app and prepare Playwright",
|
|
9920
10163
|
phase: "build"
|
|
9921
10164
|
},
|
|
9922
|
-
|
|
10165
|
+
[BUILD_PREPARATION_SUBSTEP_IDS.build]: {
|
|
9923
10166
|
order: 91,
|
|
9924
10167
|
title: "Build application",
|
|
9925
10168
|
phase: "build",
|
|
9926
|
-
parentStep:
|
|
10169
|
+
parentStep: EXECUTION_STEP_IDS[3]
|
|
9927
10170
|
},
|
|
9928
|
-
|
|
10171
|
+
[BUILD_PREPARATION_SUBSTEP_IDS.manifestGenerate]: {
|
|
9929
10172
|
order: 92,
|
|
9930
10173
|
title: "Generate manifest",
|
|
9931
10174
|
phase: "build",
|
|
9932
10175
|
optional: true,
|
|
9933
|
-
parentStep:
|
|
10176
|
+
parentStep: EXECUTION_STEP_IDS[3]
|
|
9934
10177
|
},
|
|
9935
|
-
|
|
10178
|
+
[BUILD_PREPARATION_SUBSTEP_IDS.playwrightInstall]: {
|
|
9936
10179
|
order: 93,
|
|
9937
10180
|
title: "Install Playwright browsers",
|
|
9938
10181
|
phase: "build",
|
|
9939
10182
|
optional: true,
|
|
9940
|
-
parentStep:
|
|
10183
|
+
parentStep: EXECUTION_STEP_IDS[3]
|
|
9941
10184
|
},
|
|
9942
|
-
|
|
10185
|
+
[EXECUTION_STEP_IDS[4]]: {
|
|
9943
10186
|
order: 100,
|
|
9944
10187
|
title: "Start application",
|
|
9945
10188
|
phase: "build"
|
|
9946
10189
|
},
|
|
9947
|
-
|
|
10190
|
+
[EXECUTION_STEP_IDS[5]]: {
|
|
9948
10191
|
order: 110,
|
|
9949
10192
|
title: "Detect test capabilities",
|
|
9950
10193
|
phase: "test",
|
|
9951
10194
|
optional: true
|
|
9952
10195
|
},
|
|
9953
|
-
|
|
10196
|
+
[EXECUTION_STEP_IDS[6]]: {
|
|
9954
10197
|
order: 120,
|
|
9955
10198
|
title: "Run core test layers",
|
|
9956
10199
|
phase: "test"
|
|
9957
10200
|
},
|
|
9958
|
-
|
|
10201
|
+
[EXECUTION_STEP_IDS[7]]: {
|
|
9959
10202
|
order: 130,
|
|
9960
10203
|
title: "Run E2E phase",
|
|
9961
10204
|
phase: "test",
|
|
@@ -9974,10 +10217,7 @@ var STEP_METADATA = {
|
|
|
9974
10217
|
}
|
|
9975
10218
|
};
|
|
9976
10219
|
var CANONICAL_STEP_IDS = Object.keys(STEP_METADATA).sort((a, b) => b.length - a.length);
|
|
9977
|
-
function
|
|
9978
|
-
return new Date(timestampMs).toISOString();
|
|
9979
|
-
}
|
|
9980
|
-
function getMetadata(stepId) {
|
|
10220
|
+
function getStepMetadata(stepId) {
|
|
9981
10221
|
return STEP_METADATA[stepId];
|
|
9982
10222
|
}
|
|
9983
10223
|
function getCanonicalStepId(statePath) {
|
|
@@ -9993,6 +10233,12 @@ function getStepOrderFromStatePath(statePath) {
|
|
|
9993
10233
|
if (!canonical) return -1;
|
|
9994
10234
|
return STEP_METADATA[canonical]?.order ?? -1;
|
|
9995
10235
|
}
|
|
10236
|
+
|
|
10237
|
+
// src/commands/ci/machine/commands/step-telemetry.ts
|
|
10238
|
+
init_esm_shims();
|
|
10239
|
+
function toIsoString(timestampMs) {
|
|
10240
|
+
return new Date(timestampMs).toISOString();
|
|
10241
|
+
}
|
|
9996
10242
|
function pickPrimaryStatePath(statePaths, previousStatePath) {
|
|
9997
10243
|
if (statePaths.length === 0) return previousStatePath;
|
|
9998
10244
|
if (statePaths.length === 1 && (statePaths[0] === "done" || statePaths[0] === "failed")) {
|
|
@@ -10009,7 +10255,7 @@ function createSkippedSummary(stepId, reason) {
|
|
|
10009
10255
|
return createSyntheticSummary(stepId, "skipped", { reason });
|
|
10010
10256
|
}
|
|
10011
10257
|
function createSyntheticSummary(stepId, status, params = {}) {
|
|
10012
|
-
const metadata =
|
|
10258
|
+
const metadata = getStepMetadata(stepId);
|
|
10013
10259
|
if (!metadata) return null;
|
|
10014
10260
|
const durationMs = params.startedAtMs !== void 0 && params.endedAtMs !== void 0 ? Math.max(0, params.endedAtMs - params.startedAtMs) : void 0;
|
|
10015
10261
|
return [
|
|
@@ -10031,7 +10277,7 @@ function getBuildSkipReason(context) {
|
|
|
10031
10277
|
if (!shouldRunPrExecutionPhase(context)) {
|
|
10032
10278
|
return getExecutionSkipReason(context);
|
|
10033
10279
|
}
|
|
10034
|
-
return isCiLocalMode(context) ? "Skipped in ci-local mode" : context.input.skipBuild === true ? "Skipped by --skip-build" : "Skipped by step guard";
|
|
10280
|
+
return isCiLocalMode(context) ? "Skipped in ci-local mode" : context.input.skipBuild === true ? "Skipped by --skip-build" : !context.selectedLayers.some((layer) => layer === 3 || layer === 4) ? "Skipped because no build-dependent layers are selected" : "Skipped by step guard";
|
|
10035
10281
|
}
|
|
10036
10282
|
function getPlaywrightSkipReason(context) {
|
|
10037
10283
|
if (!shouldRunPrExecutionPhase(context)) {
|
|
@@ -10051,9 +10297,6 @@ function getObservabilitySkipReason(context) {
|
|
|
10051
10297
|
if (isTestPhase(context)) {
|
|
10052
10298
|
return "Skipped in phase=test; observability DB checks are disabled";
|
|
10053
10299
|
}
|
|
10054
|
-
if (isBlockingPhase(context)) {
|
|
10055
|
-
return "Skipped in phase=blocking; observability runs outside the blocking path";
|
|
10056
|
-
}
|
|
10057
10300
|
return "Skipped by phase selection";
|
|
10058
10301
|
}
|
|
10059
10302
|
function getExecutionSkipReason(context) {
|
|
@@ -10069,7 +10312,13 @@ function getSyntheticBuildEntries(context, trackedStepIds, _stepStates, _nowMs)
|
|
|
10069
10312
|
if (trackedStepIds.has(entry[0])) return;
|
|
10070
10313
|
entries.push(entry);
|
|
10071
10314
|
};
|
|
10072
|
-
|
|
10315
|
+
if (!shouldRunPrExecutionPhase(context)) {
|
|
10316
|
+
const reason = getExecutionSkipReason(context);
|
|
10317
|
+
pushEntry(createSkippedSummary(BUILD_PREPARATION_SUBSTEP_IDS.build, reason));
|
|
10318
|
+
pushEntry(createSkippedSummary(BUILD_PREPARATION_SUBSTEP_IDS.manifestGenerate, reason));
|
|
10319
|
+
pushEntry(createSkippedSummary(BUILD_PREPARATION_SUBSTEP_IDS.playwrightInstall, reason));
|
|
10320
|
+
return entries;
|
|
10321
|
+
}
|
|
10073
10322
|
if (!shouldRunPrExecutionPhase(context)) {
|
|
10074
10323
|
const reason = getExecutionSkipReason(context);
|
|
10075
10324
|
pushEntry(createSkippedSummary("postSeedPr.execution.buildAndPlaywright.build", reason));
|
|
@@ -10083,115 +10332,62 @@ function getSyntheticBuildEntries(context, trackedStepIds, _stepStates, _nowMs)
|
|
|
10083
10332
|
}
|
|
10084
10333
|
if (shouldSkipBuild(context)) {
|
|
10085
10334
|
const reason = getBuildSkipReason(context);
|
|
10086
|
-
pushEntry(createSkippedSummary(
|
|
10087
|
-
pushEntry(
|
|
10088
|
-
|
|
10089
|
-
);
|
|
10090
|
-
pushEntry(
|
|
10091
|
-
createSkippedSummary("postSeedPr.execution.buildAndPlaywright.playwrightInstall", reason)
|
|
10092
|
-
);
|
|
10335
|
+
pushEntry(createSkippedSummary(BUILD_PREPARATION_SUBSTEP_IDS.build, reason));
|
|
10336
|
+
pushEntry(createSkippedSummary(BUILD_PREPARATION_SUBSTEP_IDS.manifestGenerate, reason));
|
|
10337
|
+
pushEntry(createSkippedSummary(BUILD_PREPARATION_SUBSTEP_IDS.playwrightInstall, reason));
|
|
10093
10338
|
return entries;
|
|
10094
10339
|
}
|
|
10095
10340
|
if (context.appBuildPassed === true) {
|
|
10096
|
-
pushEntry(
|
|
10097
|
-
createSyntheticSummary(
|
|
10098
|
-
"postSeedPr.execution.buildAndPlaywright.build",
|
|
10099
|
-
"passed",
|
|
10100
|
-
syntheticTimingParams
|
|
10101
|
-
)
|
|
10102
|
-
);
|
|
10341
|
+
pushEntry(createSyntheticSummary(BUILD_PREPARATION_SUBSTEP_IDS.build, "passed"));
|
|
10103
10342
|
} else if (context.appBuildPassed === false) {
|
|
10104
10343
|
pushEntry(
|
|
10105
|
-
createSyntheticSummary(
|
|
10106
|
-
...syntheticTimingParams,
|
|
10344
|
+
createSyntheticSummary(BUILD_PREPARATION_SUBSTEP_IDS.build, "failed", {
|
|
10107
10345
|
reason: context.error ?? "App build failed"
|
|
10108
10346
|
})
|
|
10109
10347
|
);
|
|
10110
10348
|
}
|
|
10111
10349
|
if (context.appBuildPassed === true) {
|
|
10112
10350
|
if (context.manifestGenerated === true) {
|
|
10113
|
-
pushEntry(
|
|
10114
|
-
createSyntheticSummary(
|
|
10115
|
-
"postSeedPr.execution.buildAndPlaywright.manifestGenerate",
|
|
10116
|
-
"passed",
|
|
10117
|
-
syntheticTimingParams
|
|
10118
|
-
)
|
|
10119
|
-
);
|
|
10351
|
+
pushEntry(createSyntheticSummary(BUILD_PREPARATION_SUBSTEP_IDS.manifestGenerate, "passed"));
|
|
10120
10352
|
} else if (context.manifestGenerated === null) {
|
|
10121
10353
|
pushEntry(
|
|
10122
|
-
createSyntheticSummary(
|
|
10123
|
-
"
|
|
10124
|
-
|
|
10125
|
-
{
|
|
10126
|
-
...syntheticTimingParams,
|
|
10127
|
-
reason: "Skipped because manifest generation is optional for this repository"
|
|
10128
|
-
}
|
|
10129
|
-
)
|
|
10354
|
+
createSyntheticSummary(BUILD_PREPARATION_SUBSTEP_IDS.manifestGenerate, "skipped", {
|
|
10355
|
+
reason: "Skipped because manifest generation is optional for this repository"
|
|
10356
|
+
})
|
|
10130
10357
|
);
|
|
10131
10358
|
} else if (context.manifestGenerated === false) {
|
|
10132
10359
|
pushEntry(
|
|
10133
|
-
createSyntheticSummary(
|
|
10134
|
-
"
|
|
10135
|
-
|
|
10136
|
-
{
|
|
10137
|
-
...syntheticTimingParams,
|
|
10138
|
-
reason: "Manifest generation failed but CI continued because it is optional"
|
|
10139
|
-
}
|
|
10140
|
-
)
|
|
10360
|
+
createSyntheticSummary(BUILD_PREPARATION_SUBSTEP_IDS.manifestGenerate, "failed", {
|
|
10361
|
+
reason: "Manifest generation failed but CI continued because it is optional"
|
|
10362
|
+
})
|
|
10141
10363
|
);
|
|
10142
10364
|
}
|
|
10143
10365
|
} else if (context.appBuildPassed === false) {
|
|
10144
10366
|
pushEntry(
|
|
10145
|
-
createSyntheticSummary(
|
|
10146
|
-
"
|
|
10147
|
-
|
|
10148
|
-
{
|
|
10149
|
-
...syntheticTimingParams,
|
|
10150
|
-
reason: "Skipped because app build failed before manifest generation"
|
|
10151
|
-
}
|
|
10152
|
-
)
|
|
10367
|
+
createSyntheticSummary(BUILD_PREPARATION_SUBSTEP_IDS.manifestGenerate, "skipped", {
|
|
10368
|
+
reason: "Skipped because app build failed before manifest generation"
|
|
10369
|
+
})
|
|
10153
10370
|
);
|
|
10154
10371
|
}
|
|
10155
10372
|
if (shouldReusePreparedPlaywright(context)) {
|
|
10156
10373
|
pushEntry(
|
|
10157
|
-
createSyntheticSummary(
|
|
10158
|
-
|
|
10159
|
-
|
|
10160
|
-
{
|
|
10161
|
-
...syntheticTimingParams,
|
|
10162
|
-
reason: getPlaywrightSkipReason(context)
|
|
10163
|
-
}
|
|
10164
|
-
)
|
|
10374
|
+
createSyntheticSummary(BUILD_PREPARATION_SUBSTEP_IDS.playwrightInstall, "skipped", {
|
|
10375
|
+
reason: getPlaywrightSkipReason(context)
|
|
10376
|
+
})
|
|
10165
10377
|
);
|
|
10166
10378
|
} else if (shouldSkipPlaywrightInstall(context)) {
|
|
10167
10379
|
pushEntry(
|
|
10168
|
-
createSyntheticSummary(
|
|
10169
|
-
|
|
10170
|
-
|
|
10171
|
-
{
|
|
10172
|
-
...syntheticTimingParams,
|
|
10173
|
-
reason: getPlaywrightSkipReason(context)
|
|
10174
|
-
}
|
|
10175
|
-
)
|
|
10380
|
+
createSyntheticSummary(BUILD_PREPARATION_SUBSTEP_IDS.playwrightInstall, "skipped", {
|
|
10381
|
+
reason: getPlaywrightSkipReason(context)
|
|
10382
|
+
})
|
|
10176
10383
|
);
|
|
10177
10384
|
} else if (context.playwrightInstalled === true) {
|
|
10178
|
-
pushEntry(
|
|
10179
|
-
createSyntheticSummary(
|
|
10180
|
-
"postSeedPr.execution.buildAndPlaywright.playwrightInstall",
|
|
10181
|
-
"passed",
|
|
10182
|
-
syntheticTimingParams
|
|
10183
|
-
)
|
|
10184
|
-
);
|
|
10385
|
+
pushEntry(createSyntheticSummary(BUILD_PREPARATION_SUBSTEP_IDS.playwrightInstall, "passed"));
|
|
10185
10386
|
} else if (context.playwrightInstalled === false) {
|
|
10186
10387
|
pushEntry(
|
|
10187
|
-
createSyntheticSummary(
|
|
10188
|
-
"
|
|
10189
|
-
|
|
10190
|
-
{
|
|
10191
|
-
...syntheticTimingParams,
|
|
10192
|
-
reason: "Playwright browser install failed"
|
|
10193
|
-
}
|
|
10194
|
-
)
|
|
10388
|
+
createSyntheticSummary(BUILD_PREPARATION_SUBSTEP_IDS.playwrightInstall, "failed", {
|
|
10389
|
+
reason: "Playwright browser install failed"
|
|
10390
|
+
})
|
|
10195
10391
|
);
|
|
10196
10392
|
}
|
|
10197
10393
|
return entries;
|
|
@@ -10205,8 +10401,9 @@ function getSkippedStepEntries(context, trackedStepIds) {
|
|
|
10205
10401
|
};
|
|
10206
10402
|
if (isCiPrMode(context) && !shouldRunPrObservabilityPhase(context)) {
|
|
10207
10403
|
const reason = getObservabilitySkipReason(context);
|
|
10208
|
-
|
|
10209
|
-
|
|
10404
|
+
for (const stepId of OBSERVABILITY_STEP_IDS) {
|
|
10405
|
+
pushSkipped(stepId, reason);
|
|
10406
|
+
}
|
|
10210
10407
|
}
|
|
10211
10408
|
if (isTestPhase(context)) {
|
|
10212
10409
|
pushSkipped("syncSchema", "Skipped in phase=test; reusing prepared database");
|
|
@@ -10219,29 +10416,25 @@ function getSkippedStepEntries(context, trackedStepIds) {
|
|
|
10219
10416
|
}
|
|
10220
10417
|
if (isCiPrMode(context) && !shouldRunPrExecutionPhase(context)) {
|
|
10221
10418
|
const reason = getExecutionSkipReason(context);
|
|
10222
|
-
|
|
10223
|
-
|
|
10224
|
-
|
|
10225
|
-
pushSkipped("postSeedPr.execution.appStart", reason);
|
|
10226
|
-
pushSkipped("postSeedPr.execution.capabilities", reason);
|
|
10227
|
-
pushSkipped("postSeedPr.execution.runCoreTests", reason);
|
|
10228
|
-
pushSkipped("postSeedPr.execution.e2ePhase", reason);
|
|
10419
|
+
for (const stepId of EXECUTION_STEP_IDS) {
|
|
10420
|
+
pushSkipped(stepId, reason);
|
|
10421
|
+
}
|
|
10229
10422
|
} else {
|
|
10230
10423
|
if (shouldSkipStaticChecks(context)) {
|
|
10231
10424
|
const reason = isCiLocalMode(context) ? "Skipped in ci-local mode" : context.input.skipStaticChecks === true ? "Skipped by --skip-static-checks" : "Skipped by step guard";
|
|
10232
|
-
pushSkipped(
|
|
10425
|
+
pushSkipped(EXECUTION_STEP_IDS[1], reason);
|
|
10233
10426
|
}
|
|
10234
10427
|
if (shouldSkipBuild(context)) {
|
|
10235
|
-
pushSkipped(
|
|
10428
|
+
pushSkipped(EXECUTION_STEP_IDS[3], getBuildSkipReason(context));
|
|
10236
10429
|
}
|
|
10237
10430
|
if (shouldSkipAppStart(context)) {
|
|
10238
10431
|
const reason = isCiLocalMode(context) ? "Skipped in ci-local mode" : !context.selectedLayers.includes(4) ? "Skipped because Layer 4 is not selected" : "Skipped by step guard";
|
|
10239
|
-
pushSkipped(
|
|
10432
|
+
pushSkipped(EXECUTION_STEP_IDS[4], reason);
|
|
10240
10433
|
}
|
|
10241
10434
|
if (!context.selectedLayers.includes(4)) {
|
|
10242
|
-
pushSkipped(
|
|
10435
|
+
pushSkipped(EXECUTION_STEP_IDS[7], "Skipped because Layer 4 is not selected");
|
|
10243
10436
|
} else if (Object.values(context.layerResults).some((result) => result.status === "failed")) {
|
|
10244
|
-
pushSkipped(
|
|
10437
|
+
pushSkipped(EXECUTION_STEP_IDS[7], "Skipped because blocking core tests failed");
|
|
10245
10438
|
}
|
|
10246
10439
|
}
|
|
10247
10440
|
return skipped;
|
|
@@ -10250,7 +10443,7 @@ function createStepTelemetryTracker() {
|
|
|
10250
10443
|
const stepStates = /* @__PURE__ */ new Map();
|
|
10251
10444
|
let activeStepIds = /* @__PURE__ */ new Set();
|
|
10252
10445
|
const startStep = (stepId, nowMs) => {
|
|
10253
|
-
const metadata =
|
|
10446
|
+
const metadata = getStepMetadata(stepId);
|
|
10254
10447
|
if (!metadata) return;
|
|
10255
10448
|
if (stepStates.has(stepId)) return;
|
|
10256
10449
|
stepStates.set(stepId, {
|
|
@@ -10289,8 +10482,7 @@ function createStepTelemetryTracker() {
|
|
|
10289
10482
|
finishStep(stepId, "failed", nowMs);
|
|
10290
10483
|
continue;
|
|
10291
10484
|
}
|
|
10292
|
-
|
|
10293
|
-
finishStep(stepId, succeeded ? "passed" : metadata?.optional ? "killed" : "killed", nowMs);
|
|
10485
|
+
finishStep(stepId, succeeded ? "passed" : "killed", nowMs);
|
|
10294
10486
|
}
|
|
10295
10487
|
activeStepIds = /* @__PURE__ */ new Set();
|
|
10296
10488
|
const trackedEntries = [...stepStates.entries()].sort((a, b) => a[1].metadata.order - b[1].metadata.order).map(([stepId, state]) => {
|
|
@@ -10323,7 +10515,7 @@ function createStepTelemetryTracker() {
|
|
|
10323
10515
|
new Set([...trackedEntries, ...skippedEntries].map(([stepId]) => stepId)));
|
|
10324
10516
|
return Object.fromEntries(
|
|
10325
10517
|
[...trackedEntries, ...skippedEntries, ...syntheticBuildEntries].sort(
|
|
10326
|
-
([leftStepId], [rightStepId]) => (
|
|
10518
|
+
([leftStepId], [rightStepId]) => (getStepMetadata(leftStepId)?.order ?? Number.MAX_SAFE_INTEGER) - (getStepMetadata(rightStepId)?.order ?? Number.MAX_SAFE_INTEGER)
|
|
10327
10519
|
)
|
|
10328
10520
|
);
|
|
10329
10521
|
}
|
|
@@ -10332,17 +10524,22 @@ function createStepTelemetryTracker() {
|
|
|
10332
10524
|
|
|
10333
10525
|
// src/commands/ci/machine/commands/machine-runner.ts
|
|
10334
10526
|
var FLUSH_DELAY_MS = 100;
|
|
10335
|
-
|
|
10336
|
-
|
|
10337
|
-
|
|
10338
|
-
|
|
10339
|
-
|
|
10340
|
-
|
|
10341
|
-
|
|
10342
|
-
|
|
10343
|
-
|
|
10344
|
-
|
|
10345
|
-
|
|
10527
|
+
function createProgressState() {
|
|
10528
|
+
return {
|
|
10529
|
+
updateSequence: 0,
|
|
10530
|
+
pendingUpdate: null,
|
|
10531
|
+
heartbeatTimer: null,
|
|
10532
|
+
latestSnapshot: null,
|
|
10533
|
+
heartbeatStep: null,
|
|
10534
|
+
heartbeatFailedStep: null,
|
|
10535
|
+
updateCount: 0,
|
|
10536
|
+
stepStartTime: Date.now(),
|
|
10537
|
+
currentTrackedStep: null,
|
|
10538
|
+
stepTimings: {},
|
|
10539
|
+
previousActiveSteps: /* @__PURE__ */ new Set()
|
|
10540
|
+
};
|
|
10541
|
+
}
|
|
10542
|
+
var progress = createProgressState();
|
|
10346
10543
|
var DEFAULT_PROGRESS_HEARTBEAT_MS = 3e4;
|
|
10347
10544
|
var HEARTBEAT_ELIGIBLE_STEPS = /* @__PURE__ */ new Set([
|
|
10348
10545
|
"syncSchema",
|
|
@@ -10353,95 +10550,15 @@ var HEARTBEAT_ELIGIBLE_STEPS = /* @__PURE__ */ new Set([
|
|
|
10353
10550
|
"runTests"
|
|
10354
10551
|
]);
|
|
10355
10552
|
function resetProgressTracking() {
|
|
10356
|
-
|
|
10357
|
-
|
|
10358
|
-
|
|
10359
|
-
|
|
10360
|
-
|
|
10361
|
-
}
|
|
10362
|
-
latestSnapshot = null;
|
|
10363
|
-
heartbeatStep = null;
|
|
10364
|
-
heartbeatFailedStep = null;
|
|
10365
|
-
_progressUpdateSuccessCount = 0;
|
|
10366
|
-
previousActiveSteps = /* @__PURE__ */ new Set();
|
|
10367
|
-
stepStartTime = Date.now();
|
|
10368
|
-
currentTrackedStep = null;
|
|
10369
|
-
for (const key of Object.keys(stepTimings)) {
|
|
10370
|
-
delete stepTimings[key];
|
|
10371
|
-
}
|
|
10372
|
-
}
|
|
10373
|
-
var STATE_TO_STEP = {
|
|
10374
|
-
// Setup phase
|
|
10375
|
-
setup: "setup",
|
|
10376
|
-
"setup.resolving": "setup",
|
|
10377
|
-
"setup.local": "setup",
|
|
10378
|
-
"setup.prLocal": "setup",
|
|
10379
|
-
"setup.complete": "setup",
|
|
10380
|
-
// DB phase
|
|
10381
|
-
dbReset: "syncSchema",
|
|
10382
|
-
pullProduction: "syncSchema",
|
|
10383
|
-
syncSchema: "syncSchema",
|
|
10384
|
-
applySeeds: "applySeeds",
|
|
10385
|
-
productionPreview: "observability",
|
|
10386
|
-
collectSchemaStats: "observability",
|
|
10387
|
-
postSeedPr: "postSeedChecks",
|
|
10388
|
-
"postSeedPr.execution": "postSeedChecks",
|
|
10389
|
-
"postSeedPr.observability": "observability",
|
|
10390
|
-
"postSeedPr.execution.setupRoles": "postSeedChecks",
|
|
10391
|
-
"postSeedPr.observability.productionPreview": "observability",
|
|
10392
|
-
"postSeedPr.observability.collectSchemaStats": "observability",
|
|
10393
|
-
"postSeedPr.execution.staticChecks": "staticChecks",
|
|
10394
|
-
"postSeedPr.execution.buildAndPlaywright": "build",
|
|
10395
|
-
"postSeedPr.execution.appStart": "build",
|
|
10396
|
-
"postSeedPr.execution.capabilities": "build",
|
|
10397
|
-
"postSeedPr.execution.runCoreTests": "runTests",
|
|
10398
|
-
"postSeedPr.execution.coreTestsComplete": "runTests",
|
|
10399
|
-
"postSeedPr.execution.coreTestsFailed": "runTests",
|
|
10400
|
-
"postSeedPr.execution.e2ePhase": "runTests",
|
|
10401
|
-
"postSeedPr.execution.done": "runTests",
|
|
10402
|
-
decidePath: "postSeedChecks",
|
|
10403
|
-
installPgTap: "applySeeds",
|
|
10404
|
-
setupRoles: "applySeeds",
|
|
10405
|
-
// Build phase
|
|
10406
|
-
staticChecks: "staticChecks",
|
|
10407
|
-
buildAndPlaywright: "build",
|
|
10408
|
-
appStart: "build",
|
|
10409
|
-
capabilities: "build",
|
|
10410
|
-
// Test phase (ci-local)
|
|
10411
|
-
runTests: "runTests",
|
|
10412
|
-
testsComplete: "runTests",
|
|
10413
|
-
testsFailed: "runTests",
|
|
10414
|
-
// Test phase (ci-pr: blocking/non-blocking split)
|
|
10415
|
-
runCoreTests: "runTests",
|
|
10416
|
-
coreTestsComplete: "runTests",
|
|
10417
|
-
coreTestsFailed: "runTests",
|
|
10418
|
-
e2ePhase: "runTests",
|
|
10419
|
-
"e2ePhase.intermediateComment": "runTests",
|
|
10420
|
-
"e2ePhase.e2eTests": "runTests",
|
|
10421
|
-
// Finalize phase
|
|
10422
|
-
finalize: "finalize",
|
|
10423
|
-
"finalize.writeSummary": "finalize",
|
|
10424
|
-
"finalize.postComment": "finalize",
|
|
10425
|
-
"finalize.complete": "finalize",
|
|
10426
|
-
// Final states
|
|
10427
|
-
done: "finalize",
|
|
10428
|
-
failed: "finalize"
|
|
10429
|
-
};
|
|
10430
|
-
var STEP_ORDER = [
|
|
10431
|
-
"setup",
|
|
10432
|
-
"syncSchema",
|
|
10433
|
-
"applySeeds",
|
|
10434
|
-
"postSeedChecks",
|
|
10435
|
-
"observability",
|
|
10436
|
-
"staticChecks",
|
|
10437
|
-
"build",
|
|
10438
|
-
"runTests",
|
|
10439
|
-
"finalize"
|
|
10440
|
-
];
|
|
10553
|
+
if (progress.heartbeatTimer) {
|
|
10554
|
+
clearInterval(progress.heartbeatTimer);
|
|
10555
|
+
}
|
|
10556
|
+
Object.assign(progress, createProgressState());
|
|
10557
|
+
}
|
|
10441
10558
|
function resolveStepForState(state) {
|
|
10442
10559
|
let cursor = state;
|
|
10443
10560
|
while (cursor.length > 0) {
|
|
10444
|
-
const step =
|
|
10561
|
+
const step = PROGRESS_STATE_TO_STEP[cursor];
|
|
10445
10562
|
if (step) return step;
|
|
10446
10563
|
const nextDot = cursor.lastIndexOf(".");
|
|
10447
10564
|
if (nextDot === -1) break;
|
|
@@ -10449,20 +10566,22 @@ function resolveStepForState(state) {
|
|
|
10449
10566
|
}
|
|
10450
10567
|
return void 0;
|
|
10451
10568
|
}
|
|
10452
|
-
function getCompletedSteps(currentStep,
|
|
10569
|
+
function getCompletedSteps(currentStep, stepTimings = {}) {
|
|
10453
10570
|
const completed = new Set(
|
|
10454
|
-
Object.keys(
|
|
10571
|
+
Object.keys(stepTimings).filter(
|
|
10572
|
+
(step) => PROGRESS_STEP_ORDER.includes(step)
|
|
10573
|
+
)
|
|
10455
10574
|
);
|
|
10456
10575
|
completed.delete(currentStep);
|
|
10457
|
-
return
|
|
10576
|
+
return PROGRESS_STEP_ORDER.filter((step) => completed.has(step));
|
|
10458
10577
|
}
|
|
10459
10578
|
function recordStepTiming(newStep) {
|
|
10460
10579
|
const now = Date.now();
|
|
10461
|
-
if (currentTrackedStep !== null) {
|
|
10462
|
-
stepTimings[currentTrackedStep] = now - stepStartTime;
|
|
10580
|
+
if (progress.currentTrackedStep !== null) {
|
|
10581
|
+
progress.stepTimings[progress.currentTrackedStep] = now - progress.stepStartTime;
|
|
10463
10582
|
}
|
|
10464
|
-
currentTrackedStep = newStep;
|
|
10465
|
-
stepStartTime = now;
|
|
10583
|
+
progress.currentTrackedStep = newStep;
|
|
10584
|
+
progress.stepStartTime = now;
|
|
10466
10585
|
}
|
|
10467
10586
|
function parseProgressHeartbeatMs(context) {
|
|
10468
10587
|
const rawValue = context.input.progressIntervalSeconds?.trim();
|
|
@@ -10474,24 +10593,28 @@ function parseProgressHeartbeatMs(context) {
|
|
|
10474
10593
|
return parsedSeconds * 1e3;
|
|
10475
10594
|
}
|
|
10476
10595
|
function stopProgressHeartbeat() {
|
|
10477
|
-
if (
|
|
10478
|
-
clearInterval(
|
|
10479
|
-
|
|
10596
|
+
if (progress.heartbeatTimer) {
|
|
10597
|
+
clearInterval(progress.heartbeatTimer);
|
|
10598
|
+
progress.heartbeatTimer = null;
|
|
10480
10599
|
}
|
|
10481
10600
|
}
|
|
10482
10601
|
function refreshProgressHeartbeat(currentStep, failedStep = null) {
|
|
10483
|
-
heartbeatStep = currentStep;
|
|
10484
|
-
heartbeatFailedStep = failedStep;
|
|
10602
|
+
progress.heartbeatStep = currentStep;
|
|
10603
|
+
progress.heartbeatFailedStep = failedStep;
|
|
10485
10604
|
stopProgressHeartbeat();
|
|
10486
10605
|
if (!HEARTBEAT_ELIGIBLE_STEPS.has(currentStep)) {
|
|
10487
10606
|
return;
|
|
10488
10607
|
}
|
|
10489
|
-
const intervalMs = latestSnapshot ? parseProgressHeartbeatMs(latestSnapshot.context) : DEFAULT_PROGRESS_HEARTBEAT_MS;
|
|
10490
|
-
|
|
10491
|
-
if (!latestSnapshot || !heartbeatStep) return;
|
|
10492
|
-
updateProgressComment(
|
|
10608
|
+
const intervalMs = progress.latestSnapshot ? parseProgressHeartbeatMs(progress.latestSnapshot.context) : DEFAULT_PROGRESS_HEARTBEAT_MS;
|
|
10609
|
+
progress.heartbeatTimer = setInterval(() => {
|
|
10610
|
+
if (!progress.latestSnapshot || !progress.heartbeatStep) return;
|
|
10611
|
+
updateProgressComment(
|
|
10612
|
+
progress.latestSnapshot.context,
|
|
10613
|
+
progress.heartbeatStep,
|
|
10614
|
+
progress.heartbeatFailedStep
|
|
10615
|
+
);
|
|
10493
10616
|
}, intervalMs);
|
|
10494
|
-
|
|
10617
|
+
progress.heartbeatTimer.unref?.();
|
|
10495
10618
|
}
|
|
10496
10619
|
function updateProgressComment(context, currentStep, failedStep = null) {
|
|
10497
10620
|
if (!shouldPostGitHubComment(context)) return;
|
|
@@ -10500,25 +10623,26 @@ function updateProgressComment(context, currentStep, failedStep = null) {
|
|
|
10500
10623
|
if (!token) return;
|
|
10501
10624
|
const repository = process.env.GITHUB_REPOSITORY?.trim();
|
|
10502
10625
|
if (!repository || !repository.includes("/")) return;
|
|
10503
|
-
const mySeq = ++
|
|
10626
|
+
const mySeq = ++progress.updateSequence;
|
|
10627
|
+
progress.updateCount++;
|
|
10504
10628
|
const doUpdate = async () => {
|
|
10505
|
-
if (
|
|
10629
|
+
if (progress.pendingUpdate) {
|
|
10506
10630
|
try {
|
|
10507
|
-
await
|
|
10631
|
+
await progress.pendingUpdate;
|
|
10508
10632
|
} catch {
|
|
10509
10633
|
}
|
|
10510
10634
|
}
|
|
10511
|
-
if (mySeq <
|
|
10635
|
+
if (mySeq < progress.updateSequence) {
|
|
10512
10636
|
return;
|
|
10513
10637
|
}
|
|
10514
|
-
const freshContext = latestSnapshot?.context ?? context;
|
|
10515
|
-
const completedSteps = getCompletedSteps(currentStep, stepTimings);
|
|
10638
|
+
const freshContext = progress.latestSnapshot?.context ?? context;
|
|
10639
|
+
const completedSteps = getCompletedSteps(currentStep, progress.stepTimings);
|
|
10516
10640
|
const progressInput = createProgressCommentInput(
|
|
10517
10641
|
freshContext,
|
|
10518
10642
|
currentStep,
|
|
10519
10643
|
completedSteps,
|
|
10520
10644
|
failedStep,
|
|
10521
|
-
{ ...stepTimings }
|
|
10645
|
+
{ ...progress.stepTimings }
|
|
10522
10646
|
);
|
|
10523
10647
|
const body = `<!-- runa-ci-report -->
|
|
10524
10648
|
${generateProgressCommentBody(progressInput)}`;
|
|
@@ -10529,11 +10653,10 @@ ${generateProgressCommentBody(progressInput)}`;
|
|
|
10529
10653
|
marker: "<!-- runa-ci-report -->",
|
|
10530
10654
|
body
|
|
10531
10655
|
});
|
|
10532
|
-
|
|
10533
|
-
} catch (_error) {
|
|
10656
|
+
} catch {
|
|
10534
10657
|
}
|
|
10535
10658
|
};
|
|
10536
|
-
|
|
10659
|
+
progress.pendingUpdate = doUpdate();
|
|
10537
10660
|
}
|
|
10538
10661
|
function determineFailedStep(context) {
|
|
10539
10662
|
if (context.testsRun && Object.values(context.layerResults).some((r) => r.status === "failed"))
|
|
@@ -10559,8 +10682,8 @@ function handleProgressCommentUpdate(snapshot, prevState, currentState, activeSt
|
|
|
10559
10682
|
const currentStepSet = new Set(
|
|
10560
10683
|
activeStatePaths.map((p) => resolveStepForState(p)).filter((s) => s !== void 0)
|
|
10561
10684
|
);
|
|
10562
|
-
const parallelStepChanged = !setsEqual(previousActiveSteps, currentStepSet);
|
|
10563
|
-
previousActiveSteps = currentStepSet;
|
|
10685
|
+
const parallelStepChanged = !setsEqual(progress.previousActiveSteps, currentStepSet);
|
|
10686
|
+
progress.previousActiveSteps = currentStepSet;
|
|
10564
10687
|
if (!currentStep || !parallelStepChanged && currentStep === prevStep && !shouldRefreshWithinStep)
|
|
10565
10688
|
return;
|
|
10566
10689
|
if (!shouldRefreshWithinStep) {
|
|
@@ -10603,11 +10726,10 @@ async function runCiMachine(input, logger, onStateChange) {
|
|
|
10603
10726
|
let settled = false;
|
|
10604
10727
|
const stepTelemetry = createStepTelemetryTracker();
|
|
10605
10728
|
actor.subscribe((snapshot) => {
|
|
10606
|
-
latestSnapshot = snapshot;
|
|
10729
|
+
progress.latestSnapshot = snapshot;
|
|
10607
10730
|
const activeStatePaths = getSnapshotStatePaths(snapshot);
|
|
10608
10731
|
stepTelemetry.transition(activeStatePaths);
|
|
10609
10732
|
const currentState = pickPrimaryStatePath(activeStatePaths, previousState) ?? activeStatePaths[0] ?? previousState ?? "unknown";
|
|
10610
|
-
snapshot.status;
|
|
10611
10733
|
if (currentState && currentState !== "done" && currentState !== "failed" && !currentState.startsWith("finalize")) {
|
|
10612
10734
|
lastActionableState = currentState;
|
|
10613
10735
|
}
|
|
@@ -10974,7 +11096,9 @@ z.object({
|
|
|
10974
11096
|
/** Skip GitHub comment */
|
|
10975
11097
|
skipGithubComment: z.boolean().optional(),
|
|
10976
11098
|
/** Specific layers */
|
|
10977
|
-
layers: z.array(z.number().int()).optional()
|
|
11099
|
+
layers: z.array(z.number().int()).optional(),
|
|
11100
|
+
/** Internal workflow-only CI profile */
|
|
11101
|
+
internalProfile: z.enum(["schema-only"]).optional()
|
|
10978
11102
|
}).passthrough();
|
|
10979
11103
|
z.object({
|
|
10980
11104
|
passed: z.boolean(),
|
|
@@ -11122,6 +11246,43 @@ z.object({
|
|
|
11122
11246
|
port: z.number().int()
|
|
11123
11247
|
});
|
|
11124
11248
|
|
|
11249
|
+
// src/commands/ci/machine/commands/ci-pr-internal-profile.ts
|
|
11250
|
+
init_esm_shims();
|
|
11251
|
+
function parseInternalLayers(raw) {
|
|
11252
|
+
const values = raw.split(",").map((token) => token.trim()).filter((token) => token.length > 0);
|
|
11253
|
+
if (values.length === 0) {
|
|
11254
|
+
throw new Error("RUNA_CI_INTERNAL_LAYERS must include at least one layer number");
|
|
11255
|
+
}
|
|
11256
|
+
const layers = values.map((token) => {
|
|
11257
|
+
const parsed = Number.parseInt(token, 10);
|
|
11258
|
+
if (!Number.isInteger(parsed) || parsed < 1 || parsed > 4) {
|
|
11259
|
+
throw new Error(
|
|
11260
|
+
`Invalid RUNA_CI_INTERNAL_LAYERS value: "${raw}". Expected a comma-separated list of layers 1-4.`
|
|
11261
|
+
);
|
|
11262
|
+
}
|
|
11263
|
+
return parsed;
|
|
11264
|
+
});
|
|
11265
|
+
return Array.from(new Set(layers)).sort((a, b) => a - b);
|
|
11266
|
+
}
|
|
11267
|
+
function resolveInternalCiOverrides(env = process.env) {
|
|
11268
|
+
const profileRaw = env.RUNA_CI_INTERNAL_PROFILE?.trim();
|
|
11269
|
+
const layersRaw = env.RUNA_CI_INTERNAL_LAYERS?.trim();
|
|
11270
|
+
if (!profileRaw && !layersRaw) {
|
|
11271
|
+
return {};
|
|
11272
|
+
}
|
|
11273
|
+
let profile;
|
|
11274
|
+
if (profileRaw) {
|
|
11275
|
+
if (profileRaw !== "schema-only") {
|
|
11276
|
+
throw new Error(
|
|
11277
|
+
`Unsupported RUNA_CI_INTERNAL_PROFILE value: "${profileRaw}". Expected "schema-only".`
|
|
11278
|
+
);
|
|
11279
|
+
}
|
|
11280
|
+
profile = profileRaw;
|
|
11281
|
+
}
|
|
11282
|
+
const layers = layersRaw ? parseInternalLayers(layersRaw) : profile === "schema-only" ? [2] : void 0;
|
|
11283
|
+
return { profile, layers };
|
|
11284
|
+
}
|
|
11285
|
+
|
|
11125
11286
|
// src/commands/ci/machine/commands/ci-pr.ts
|
|
11126
11287
|
var cliOutputFormat = "human";
|
|
11127
11288
|
var isGitHubActionsMode2 = false;
|
|
@@ -11176,6 +11337,73 @@ function logSetupRolesStatus(ctx, logger) {
|
|
|
11176
11337
|
}
|
|
11177
11338
|
}
|
|
11178
11339
|
var CI_PR_UNKNOWN_STATE_LOG_SKIP = /* @__PURE__ */ new Set(["decidePath", "done", "failed"]);
|
|
11340
|
+
var logStaticChecks = (ctx, logger) => {
|
|
11341
|
+
logSetupRolesStatus(ctx, logger);
|
|
11342
|
+
logSection3("Static Checks: type-check + lint (parallel)");
|
|
11343
|
+
logger.info("Running: pnpm type-check || pnpm lint");
|
|
11344
|
+
};
|
|
11345
|
+
var logBuildAndPlaywright = (ctx, logger) => {
|
|
11346
|
+
if (ctx.staticChecksPassed) {
|
|
11347
|
+
logger.success("Static checks passed");
|
|
11348
|
+
}
|
|
11349
|
+
if (shouldReusePreparedPlaywright(ctx)) {
|
|
11350
|
+
logSection3("Build: App Build + Reuse Prepared Playwright");
|
|
11351
|
+
logger.info("Running: pnpm build (Playwright install owned by outer workflow)");
|
|
11352
|
+
return;
|
|
11353
|
+
}
|
|
11354
|
+
logSection3("Build: App Build + Playwright Install (parallel)");
|
|
11355
|
+
logger.info("Running: pnpm build || pnpm exec playwright install chromium");
|
|
11356
|
+
};
|
|
11357
|
+
var logLayerContent = (ctx, logger) => {
|
|
11358
|
+
if (ctx.staticChecksPassed) {
|
|
11359
|
+
logger.success("Static checks passed");
|
|
11360
|
+
}
|
|
11361
|
+
logSection3("Detect Runnable Layers");
|
|
11362
|
+
logger.info(`Selected layers before filtering: ${ctx.selectedLayers.join(", ")}`);
|
|
11363
|
+
};
|
|
11364
|
+
var logAppStart = (ctx, logger) => {
|
|
11365
|
+
if (ctx.appBuildPassed) {
|
|
11366
|
+
logger.success("App build completed");
|
|
11367
|
+
}
|
|
11368
|
+
if (ctx.manifestGenerated) {
|
|
11369
|
+
logger.success("Manifest generated (Layer 3 ready)");
|
|
11370
|
+
} else if (ctx.manifestGenerated === null) {
|
|
11371
|
+
logger.info("Manifest generation skipped (optional)");
|
|
11372
|
+
} else if (ctx.manifestGenerated === false) {
|
|
11373
|
+
logger.warn("Manifest generation failed (Layer 3 may be skipped)");
|
|
11374
|
+
}
|
|
11375
|
+
if (ctx.playwrightInstalled) {
|
|
11376
|
+
logger.success("Playwright browsers installed");
|
|
11377
|
+
}
|
|
11378
|
+
logSection3("Start Application");
|
|
11379
|
+
logger.info(`Starting app on port ${ctx.app?.port ?? 3e3}...`);
|
|
11380
|
+
};
|
|
11381
|
+
var logCapabilities = (ctx, logger) => {
|
|
11382
|
+
if (ctx.appStarted) {
|
|
11383
|
+
logger.success(`App started at ${ctx.baseUrl ?? "http://localhost:3000"}`);
|
|
11384
|
+
}
|
|
11385
|
+
logSection3("Detect Test Capabilities");
|
|
11386
|
+
};
|
|
11387
|
+
var logRunCoreTests = (ctx, logger) => {
|
|
11388
|
+
const coreLayers = ctx.selectedLayers.filter((l) => l !== 4);
|
|
11389
|
+
logSection3(`Core Tests: Layer ${coreLayers.join(", ")}`);
|
|
11390
|
+
logger.info(`Running core layers: ${coreLayers.map((l) => `test layer${l}`).join(", ")}`);
|
|
11391
|
+
};
|
|
11392
|
+
var logCollectSchemaStats = (ctx, logger) => {
|
|
11393
|
+
if (ctx.productionPreview?.executed) {
|
|
11394
|
+
if (ctx.productionPreview.hasChanges) {
|
|
11395
|
+
logger.info("Production preview: schema changes detected");
|
|
11396
|
+
} else {
|
|
11397
|
+
logger.success("Production preview: no schema changes");
|
|
11398
|
+
}
|
|
11399
|
+
}
|
|
11400
|
+
logSection3("Database: Collect Schema Statistics");
|
|
11401
|
+
logger.info("Comparing Local/CI/Production schemas...");
|
|
11402
|
+
};
|
|
11403
|
+
var logE2ePhase = (_ctx, logger) => {
|
|
11404
|
+
logSection3("E2E Phase: Layer 4 + Intermediate Comment");
|
|
11405
|
+
logger.info("Running Layer 4 E2E tests (parallel with intermediate PR comment)");
|
|
11406
|
+
};
|
|
11179
11407
|
var stateLogHandlers2 = {
|
|
11180
11408
|
// ─── Setup Phase ───────────────────────────────────────────
|
|
11181
11409
|
setup: (_ctx, _logger) => {
|
|
@@ -11196,9 +11424,12 @@ var stateLogHandlers2 = {
|
|
|
11196
11424
|
logger.info("Posting initial CI progress comment to PR...");
|
|
11197
11425
|
},
|
|
11198
11426
|
// ─── Database Phase ────────────────────────────────────────
|
|
11199
|
-
syncSchema: (
|
|
11200
|
-
|
|
11201
|
-
|
|
11427
|
+
syncSchema: (ctx, logger) => {
|
|
11428
|
+
const isCiLocalMode2 = ctx.mode === "ci-local";
|
|
11429
|
+
logSection3(`Database: Apply Schema (${isCiLocalMode2 ? "db sync" : "db apply --no-seed"})`);
|
|
11430
|
+
logger.info(
|
|
11431
|
+
isCiLocalMode2 ? "Running: pnpm exec runa db sync local --verbose" : "Running: pnpm exec runa db apply preview --no-seed --verbose"
|
|
11432
|
+
);
|
|
11202
11433
|
},
|
|
11203
11434
|
applySeeds: (ctx, logger) => {
|
|
11204
11435
|
if (ctx.schemaApplied) {
|
|
@@ -11207,6 +11438,7 @@ var stateLogHandlers2 = {
|
|
|
11207
11438
|
logSection3("Database: Apply Seeds (db seed)");
|
|
11208
11439
|
logger.info("Running: pnpm exec runa db seed ci --auto-approve");
|
|
11209
11440
|
},
|
|
11441
|
+
// Nested paths (postSeedPr.*) — differ from flat counterparts
|
|
11210
11442
|
"postSeedPr.execution.setupRoles": (ctx, logger) => {
|
|
11211
11443
|
if (ctx.seedsApplied) {
|
|
11212
11444
|
logger.success("Seeds applied successfully");
|
|
@@ -11219,68 +11451,18 @@ var stateLogHandlers2 = {
|
|
|
11219
11451
|
logger.success("Seeds applied successfully");
|
|
11220
11452
|
}
|
|
11221
11453
|
logSection3("Database: Production Preview (dry-run)");
|
|
11222
|
-
logger.info("Running: pnpm exec runa db
|
|
11223
|
-
},
|
|
11224
|
-
"postSeedPr.observability.collectSchemaStats": (ctx, logger) => {
|
|
11225
|
-
if (ctx.productionPreview?.executed) {
|
|
11226
|
-
if (ctx.productionPreview.hasChanges) {
|
|
11227
|
-
logger.info("Production preview: schema changes detected");
|
|
11228
|
-
} else {
|
|
11229
|
-
logger.success("Production preview: no schema changes");
|
|
11230
|
-
}
|
|
11231
|
-
}
|
|
11232
|
-
logSection3("Database: Collect Schema Statistics");
|
|
11233
|
-
logger.info("Comparing Local/CI/Production schemas...");
|
|
11234
|
-
},
|
|
11235
|
-
"postSeedPr.execution.staticChecks": (ctx, logger) => {
|
|
11236
|
-
logSetupRolesStatus(ctx, logger);
|
|
11237
|
-
logSection3("Static Checks: type-check + lint (parallel)");
|
|
11238
|
-
logger.info("Running: pnpm type-check || pnpm lint");
|
|
11239
|
-
},
|
|
11240
|
-
"postSeedPr.execution.buildAndPlaywright": (ctx, logger) => {
|
|
11241
|
-
if (ctx.staticChecksPassed) {
|
|
11242
|
-
logger.success("Static checks passed");
|
|
11243
|
-
}
|
|
11244
|
-
if (shouldReusePreparedPlaywright(ctx)) {
|
|
11245
|
-
logSection3("Build: App Build + Reuse Prepared Playwright");
|
|
11246
|
-
logger.info("Running: pnpm build (Playwright install owned by outer workflow)");
|
|
11247
|
-
return;
|
|
11248
|
-
}
|
|
11249
|
-
logSection3("Build: App Build + Playwright Install (parallel)");
|
|
11250
|
-
logger.info("Running: pnpm build || pnpm exec playwright install chromium");
|
|
11251
|
-
},
|
|
11252
|
-
"postSeedPr.execution.appStart": (ctx, logger) => {
|
|
11253
|
-
if (ctx.appBuildPassed) {
|
|
11254
|
-
logger.success("App build completed");
|
|
11255
|
-
}
|
|
11256
|
-
if (ctx.manifestGenerated) {
|
|
11257
|
-
logger.success("Manifest generated (Layer 3 ready)");
|
|
11258
|
-
} else if (ctx.manifestGenerated === null) {
|
|
11259
|
-
logger.info("Manifest generation skipped (optional)");
|
|
11260
|
-
} else if (ctx.manifestGenerated === false) {
|
|
11261
|
-
logger.warn("Manifest generation failed (Layer 3 may be skipped)");
|
|
11262
|
-
}
|
|
11263
|
-
if (ctx.playwrightInstalled) {
|
|
11264
|
-
logger.success("Playwright browsers installed");
|
|
11265
|
-
}
|
|
11266
|
-
logSection3("Start Application");
|
|
11267
|
-
logger.info(`Starting app on port ${ctx.app?.port ?? 3e3}...`);
|
|
11268
|
-
},
|
|
11269
|
-
"postSeedPr.execution.capabilities": (ctx, logger) => {
|
|
11270
|
-
if (ctx.appStarted) {
|
|
11271
|
-
logger.success(`App started at ${ctx.baseUrl ?? "http://localhost:3000"}`);
|
|
11272
|
-
}
|
|
11273
|
-
logSection3("Detect Test Capabilities");
|
|
11274
|
-
},
|
|
11275
|
-
"postSeedPr.execution.runCoreTests": (ctx, logger) => {
|
|
11276
|
-
const coreLayers = ctx.selectedLayers.filter((l) => l !== 4);
|
|
11277
|
-
logSection3(`Core Tests: Layer ${coreLayers.join(", ")}`);
|
|
11278
|
-
logger.info(`Running core layers: ${coreLayers.map((l) => `test layer${l}`).join(", ")}`);
|
|
11279
|
-
},
|
|
11280
|
-
"postSeedPr.execution.e2ePhase": (_ctx, logger) => {
|
|
11281
|
-
logSection3("E2E Phase: Layer 4 + Intermediate Comment");
|
|
11282
|
-
logger.info("Running Layer 4 E2E tests (parallel with intermediate PR comment)");
|
|
11454
|
+
logger.info("Running: pnpm exec runa db preview production --profile compare-only");
|
|
11283
11455
|
},
|
|
11456
|
+
// Shared handlers (nested paths alias to same handler as flat paths)
|
|
11457
|
+
"postSeedPr.observability.collectSchemaStats": logCollectSchemaStats,
|
|
11458
|
+
"postSeedPr.execution.staticChecks": logStaticChecks,
|
|
11459
|
+
"postSeedPr.execution.layerContent": logLayerContent,
|
|
11460
|
+
"postSeedPr.execution.buildAndPlaywright": logBuildAndPlaywright,
|
|
11461
|
+
"postSeedPr.execution.appStart": logAppStart,
|
|
11462
|
+
"postSeedPr.execution.capabilities": logCapabilities,
|
|
11463
|
+
"postSeedPr.execution.runCoreTests": logRunCoreTests,
|
|
11464
|
+
"postSeedPr.execution.e2ePhase": logE2ePhase,
|
|
11465
|
+
// Flat paths (differ from nested counterparts)
|
|
11284
11466
|
productionPreview: (ctx, logger) => {
|
|
11285
11467
|
if (ctx.seedsApplied) {
|
|
11286
11468
|
logger.success("Seeds applied successfully");
|
|
@@ -11288,19 +11470,9 @@ var stateLogHandlers2 = {
|
|
|
11288
11470
|
logger.info("Seeds skipped (no seed files or already seeded)");
|
|
11289
11471
|
}
|
|
11290
11472
|
logSection3("Database: Production Preview (dry-run)");
|
|
11291
|
-
logger.info("Running: pnpm exec runa db
|
|
11292
|
-
},
|
|
11293
|
-
collectSchemaStats: (ctx, logger) => {
|
|
11294
|
-
if (ctx.productionPreview?.executed) {
|
|
11295
|
-
if (ctx.productionPreview.hasChanges) {
|
|
11296
|
-
logger.info("Production preview: schema changes detected");
|
|
11297
|
-
} else {
|
|
11298
|
-
logger.success("Production preview: no schema changes");
|
|
11299
|
-
}
|
|
11300
|
-
}
|
|
11301
|
-
logSection3("Database: Collect Schema Statistics");
|
|
11302
|
-
logger.info("Comparing Local/CI/Production schemas...");
|
|
11473
|
+
logger.info("Running: pnpm exec runa db preview production --profile compare-only");
|
|
11303
11474
|
},
|
|
11475
|
+
collectSchemaStats: logCollectSchemaStats,
|
|
11304
11476
|
setupRoles: (ctx, logger) => {
|
|
11305
11477
|
if (ctx.schemaStats) {
|
|
11306
11478
|
logger.success("Schema statistics collected");
|
|
@@ -11309,57 +11481,18 @@ var stateLogHandlers2 = {
|
|
|
11309
11481
|
logger.info("Running: pnpm db:setup-roles");
|
|
11310
11482
|
},
|
|
11311
11483
|
// ─── Static Analysis Phase ─────────────────────────────────
|
|
11312
|
-
staticChecks:
|
|
11313
|
-
logSetupRolesStatus(ctx, logger);
|
|
11314
|
-
logSection3("Static Checks: type-check + lint (parallel)");
|
|
11315
|
-
logger.info("Running: pnpm type-check || pnpm lint");
|
|
11316
|
-
},
|
|
11484
|
+
staticChecks: logStaticChecks,
|
|
11317
11485
|
// ─── Build Phase ───────────────────────────────────────────
|
|
11318
|
-
|
|
11319
|
-
|
|
11320
|
-
|
|
11321
|
-
}
|
|
11322
|
-
if (shouldReusePreparedPlaywright(ctx)) {
|
|
11323
|
-
logSection3("Build: App Build + Reuse Prepared Playwright");
|
|
11324
|
-
logger.info("Running: pnpm build (Playwright install owned by outer workflow)");
|
|
11325
|
-
return;
|
|
11326
|
-
}
|
|
11327
|
-
logSection3("Build: App Build + Playwright Install (parallel)");
|
|
11328
|
-
logger.info("Running: pnpm build || pnpm exec playwright install chromium");
|
|
11329
|
-
},
|
|
11330
|
-
appStart: (ctx, logger) => {
|
|
11331
|
-
if (ctx.appBuildPassed) {
|
|
11332
|
-
logger.success("App build completed");
|
|
11333
|
-
}
|
|
11334
|
-
if (ctx.manifestGenerated) {
|
|
11335
|
-
logger.success("Manifest generated (Layer 3 ready)");
|
|
11336
|
-
} else if (ctx.manifestGenerated === null) {
|
|
11337
|
-
logger.info("Manifest generation skipped (optional)");
|
|
11338
|
-
} else if (ctx.manifestGenerated === false) {
|
|
11339
|
-
logger.warn("Manifest generation failed (Layer 3 may be skipped)");
|
|
11340
|
-
}
|
|
11341
|
-
if (ctx.playwrightInstalled) {
|
|
11342
|
-
logger.success("Playwright browsers installed");
|
|
11343
|
-
}
|
|
11344
|
-
logSection3("Start Application");
|
|
11345
|
-
logger.info(`Starting app on port ${ctx.app?.port ?? 3e3}...`);
|
|
11346
|
-
},
|
|
11486
|
+
layerContent: logLayerContent,
|
|
11487
|
+
buildAndPlaywright: logBuildAndPlaywright,
|
|
11488
|
+
appStart: logAppStart,
|
|
11347
11489
|
// ─── Test Phase ────────────────────────────────────────────
|
|
11348
|
-
capabilities:
|
|
11349
|
-
if (ctx.appStarted) {
|
|
11350
|
-
logger.success(`App started at ${ctx.baseUrl ?? "http://localhost:3000"}`);
|
|
11351
|
-
}
|
|
11352
|
-
logSection3("Detect Test Capabilities");
|
|
11353
|
-
},
|
|
11490
|
+
capabilities: logCapabilities,
|
|
11354
11491
|
runTests: (ctx, logger) => {
|
|
11355
11492
|
logSection3(`Run Tests: Layer ${ctx.selectedLayers.join(", ")}`);
|
|
11356
11493
|
logger.info(`Running layers: ${ctx.selectedLayers.map((l) => `test layer${l}`).join(", ")}`);
|
|
11357
11494
|
},
|
|
11358
|
-
runCoreTests:
|
|
11359
|
-
const coreLayers = ctx.selectedLayers.filter((l) => l !== 4);
|
|
11360
|
-
logSection3(`Core Tests: Layer ${coreLayers.join(", ")}`);
|
|
11361
|
-
logger.info(`Running core layers: ${coreLayers.map((l) => `test layer${l}`).join(", ")}`);
|
|
11362
|
-
},
|
|
11495
|
+
runCoreTests: logRunCoreTests,
|
|
11363
11496
|
coreTestsComplete: (ctx, logger) => {
|
|
11364
11497
|
const coreLayers = ctx.selectedLayers.filter((l) => l !== 4);
|
|
11365
11498
|
const passedLayers = coreLayers.filter(
|
|
@@ -11373,10 +11506,7 @@ var stateLogHandlers2 = {
|
|
|
11373
11506
|
const failedLayers = Object.entries(ctx.layerResults).filter(([_, result]) => result.status === "failed").map(([layer]) => layer);
|
|
11374
11507
|
logger.error(`Core tests failed: Layer ${failedLayers.join(", ")}`);
|
|
11375
11508
|
},
|
|
11376
|
-
e2ePhase:
|
|
11377
|
-
logSection3("E2E Phase: Layer 4 + Intermediate Comment");
|
|
11378
|
-
logger.info("Running Layer 4 E2E tests (parallel with intermediate PR comment)");
|
|
11379
|
-
},
|
|
11509
|
+
e2ePhase: logE2ePhase,
|
|
11380
11510
|
"e2ePhase.e2eTests": (_ctx, logger) => {
|
|
11381
11511
|
logger.info("Running Layer 4 E2E tests...");
|
|
11382
11512
|
},
|
|
@@ -11442,7 +11572,7 @@ function getGitHubEventAction() {
|
|
|
11442
11572
|
return void 0;
|
|
11443
11573
|
}
|
|
11444
11574
|
}
|
|
11445
|
-
function optionsToMachineInput(options) {
|
|
11575
|
+
function optionsToMachineInput(options, internalOverrides) {
|
|
11446
11576
|
return {
|
|
11447
11577
|
command: "pr",
|
|
11448
11578
|
// Explicit command for mode detection
|
|
@@ -11463,7 +11593,8 @@ function optionsToMachineInput(options) {
|
|
|
11463
11593
|
skipGithubComment: options.skipGithubComment,
|
|
11464
11594
|
verbose: options.verbose,
|
|
11465
11595
|
targetDir: process.cwd(),
|
|
11466
|
-
layers:
|
|
11596
|
+
layers: internalOverrides.layers,
|
|
11597
|
+
internalProfile: internalOverrides.profile,
|
|
11467
11598
|
// Environment capture (Environment Capture Pattern: capture at entry point, not in machine/guards)
|
|
11468
11599
|
databaseUrl: options.databaseUrl ?? process.env.DATABASE_URL,
|
|
11469
11600
|
// PRD: GH_DATABASE_URL_ADMIN = postgres role (DDL capable, for pg-schema-diff dry-run)
|
|
@@ -11497,7 +11628,7 @@ async function runCiPrCommand(options) {
|
|
|
11497
11628
|
const runExecutionPath = !observabilityPhase;
|
|
11498
11629
|
const preparedRuntime = options.skipLocalDbStart === true || options.assumeSupabaseReady === true;
|
|
11499
11630
|
const preparedPlaywright = options.skipPlaywrightInstall === true;
|
|
11500
|
-
const dbDescription = testOnlyPhase ? "Skip schema apply/seed and reuse the prepared database" : observabilityPhase ? "db apply \u2192 db seed \u2192 production preview \u2192 schema stats" : blockingPhase ? "db apply \u2192 db seed \u2192 db:setup-roles
|
|
11631
|
+
const dbDescription = testOnlyPhase ? "Skip schema apply/seed and reuse the prepared database" : observabilityPhase ? "db apply \u2192 db seed \u2192 production preview \u2192 schema stats" : blockingPhase ? "db apply \u2192 db seed \u2192 (production preview \u2225 schema stats \u2225 db:setup-roles)" : "db apply \u2192 db seed \u2192 (production preview \u2225 schema stats \u2225 db:setup-roles)";
|
|
11501
11632
|
const planSteps = [
|
|
11502
11633
|
{
|
|
11503
11634
|
id: "setup",
|
|
@@ -11508,18 +11639,30 @@ async function runCiPrCommand(options) {
|
|
|
11508
11639
|
...!runExecutionPath || skipBuild ? [] : [
|
|
11509
11640
|
{
|
|
11510
11641
|
id: "build",
|
|
11511
|
-
description: preparedPlaywright ? "pnpm build \u2192 manifest:generate (reuse preinstalled Playwright)" : "pnpm build \u2192 manifest:generate + playwright install"
|
|
11642
|
+
description: preparedPlaywright ? "Detect runnable layers \u2192 pnpm build \u2192 manifest:generate (reuse preinstalled Playwright)" : "Detect runnable layers \u2192 pnpm build \u2192 manifest:generate + playwright install"
|
|
11643
|
+
}
|
|
11644
|
+
],
|
|
11645
|
+
...!runExecutionPath ? [] : [
|
|
11646
|
+
{
|
|
11647
|
+
id: "test",
|
|
11648
|
+
description: "Start app if Layer 4 is runnable \u2192 detect capabilities \u2192 run layers"
|
|
11512
11649
|
}
|
|
11513
11650
|
],
|
|
11514
|
-
...!runExecutionPath ? [] : [{ id: "test", description: "Start app \u2192 detect capabilities \u2192 run layers 1-4" }],
|
|
11515
11651
|
{
|
|
11516
11652
|
id: "finalize",
|
|
11517
11653
|
description: "Write ci-summary.json + post GitHub comment"
|
|
11518
11654
|
}
|
|
11519
11655
|
];
|
|
11520
11656
|
logPlan2(planSteps);
|
|
11521
|
-
const machineInput = optionsToMachineInput(options);
|
|
11522
11657
|
try {
|
|
11658
|
+
const internalOverrides = resolveInternalCiOverrides();
|
|
11659
|
+
if (internalOverrides.profile && outputFormat !== "json") {
|
|
11660
|
+
logger.info(`Internal CI profile: ${internalOverrides.profile}`);
|
|
11661
|
+
if (internalOverrides.layers) {
|
|
11662
|
+
logger.info(`Internal CI layers: ${internalOverrides.layers.join(", ")}`);
|
|
11663
|
+
}
|
|
11664
|
+
}
|
|
11665
|
+
const machineInput = optionsToMachineInput(options, internalOverrides);
|
|
11523
11666
|
const result = await runCiMachine(machineInput, logger, handleStateChange2);
|
|
11524
11667
|
endCurrentGroup2();
|
|
11525
11668
|
printSummary2(logger, result);
|