@runa-ai/runa-cli 0.7.2 → 0.8.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/{chunk-Z7A4BEWF.js → chunk-3JO6YP3T.js} +1 -1
- package/dist/chunk-6E2DRXIL.js +452 -0
- package/dist/{chunk-PMXE5XOJ.js → chunk-GHQH6UC5.js} +1 -1
- package/dist/{chunk-LCK2LGVR.js → chunk-PAWNJA3N.js} +1 -1
- package/dist/{chunk-FWMGC5FP.js → chunk-RB2ZUS76.js} +249 -12
- package/dist/{chunk-CKRLVEIO.js → chunk-ZYT7OQJB.js} +16 -11
- package/dist/{ci-Z4525QW6.js → ci-ZK3LKYFX.js} +305 -429
- package/dist/{cli-Q2XIQDRS.js → cli-ZY5VRIJA.js} +13 -13
- package/dist/commands/ci/commands/ci-resolvers.d.ts +1 -2
- package/dist/commands/ci/machine/actors/setup/pr-common.d.ts +1 -1
- package/dist/commands/ci/machine/contract.d.ts +6 -1
- package/dist/commands/ci/machine/guards.d.ts +16 -0
- package/dist/commands/ci/machine/machine.d.ts +11 -3
- package/dist/commands/db/apply/actors/seed-actors.d.ts +1 -0
- package/dist/commands/db/apply/contract.d.ts +23 -0
- package/dist/commands/db/apply/helpers/fresh-db-handler.d.ts +2 -1
- package/dist/commands/db/apply/helpers/hazard-handler.d.ts +19 -8
- package/dist/commands/db/apply/helpers/index.d.ts +2 -1
- package/dist/commands/db/apply/helpers/no-change-plan.d.ts +2 -0
- package/dist/commands/db/apply/helpers/plan-check-filter.d.ts +11 -0
- package/dist/commands/db/apply/machine.d.ts +52 -1
- package/dist/commands/db/utils/boundary-policy/types.d.ts +2 -0
- package/dist/commands/db/utils/duplicate-function-ownership.d.ts +35 -0
- package/dist/commands/db/utils/plan-size-guard.d.ts +16 -0
- package/dist/commands/db/utils/preflight-checks/duplicate-function-ownership-checks.d.ts +4 -0
- package/dist/constants/versions.d.ts +1 -1
- package/dist/{db-BPQ2TEQM.js → db-EPI2DQYN.js} +1203 -410
- package/dist/{dev-MLRKIP7F.js → dev-GB5ERUVR.js} +1 -1
- package/dist/{env-WNHJVLOT.js → env-WP74UUMO.js} +1 -1
- package/dist/{hotfix-Z5EGVSMH.js → hotfix-TOSGTVCW.js} +1 -1
- package/dist/index.js +3 -3
- package/dist/{init-S2ATHLJ6.js → init-35JLDFHI.js} +1 -1
- package/dist/{risk-detector-VO5HJR4R.js → risk-detector-S7XQF4I2.js} +1 -1
- package/dist/{risk-detector-core-7WZJZ5ZI.js → risk-detector-core-TGFKWHRS.js} +1 -1
- package/dist/{risk-detector-plpgsql-ULV7NLDB.js → risk-detector-plpgsql-O32TUR34.js} +103 -5
- package/dist/{upgrade-BDUWBRT5.js → upgrade-7L4JIE4K.js} +1 -1
- package/dist/{vuln-check-66RXX3TO.js → vuln-check-G6I4YYDC.js} +1 -1
- package/dist/{vuln-checker-FFOGOJPT.js → vuln-checker-CT2AYPIS.js} +1 -1
- package/dist/{watch-ITYW57SL.js → watch-AL4LCBRM.js} +1 -1
- package/package.json +3 -3
- package/dist/chunk-4XHZQRRK.js +0 -215
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { createRequire } from 'module';
|
|
3
|
-
import { normalizeDatabaseUrlForDdl, parseBoolish, enhanceConnectionError, detectAppSchemas, formatSchemasForSql } from './chunk-
|
|
4
|
-
import { isPathContained } from './chunk-DRSUEMAK.js';
|
|
3
|
+
import { normalizeDatabaseUrlForDdl, parseBoolish, enhanceConnectionError, isIdempotentRoleHazard, detectAppSchemas, formatSchemasForSql } from './chunk-6E2DRXIL.js';
|
|
5
4
|
import './chunk-QDF7QXBL.js';
|
|
6
5
|
import { getSnapshotStateName, getSnapshotStatePaths, isSnapshotComplete } from './chunk-XVNDDHAF.js';
|
|
7
|
-
import { writeEnvLocal, startAppBackground, waitForAppReady, executePrSetupBase, createErrorOutput } from './chunk-
|
|
6
|
+
import { createInitialSummary, resolveMode, appendGithubStepSummary, buildCiProdApplyStepSummaryMarkdown, setSummaryErrorFromUnknown, writeEnvLocal, startAppBackground, waitForAppReady, executePrSetupBase, createErrorOutput, requireCiAutoApprove, resolveProdApplyInputs, parseIntOr, addGithubMask } from './chunk-RB2ZUS76.js';
|
|
8
7
|
import { parsePostgresUrl, buildPsqlArgs, buildPsqlEnv, psqlSyncQuery } from './chunk-A6A7JIRD.js';
|
|
9
8
|
import { ensureRunaTmpDir, runLogged } from './chunk-6FAU4IGR.js';
|
|
10
9
|
import { createMachineStateChangeLogger } from './chunk-5FT3F36G.js';
|
|
@@ -13,13 +12,13 @@ import { init_constants, init_local_supabase, detectLocalSupabasePorts } from '.
|
|
|
13
12
|
import { emitJsonSuccess } from './chunk-KE6QJBZG.js';
|
|
14
13
|
import './chunk-WJXC4MVY.js';
|
|
15
14
|
import { setOutputFormat } from './chunk-HKUWEGUX.js';
|
|
16
|
-
import
|
|
17
|
-
import { init_esm_shims
|
|
15
|
+
import './chunk-JMJP4A47.js';
|
|
16
|
+
import { init_esm_shims } from './chunk-VRXHCR5K.js';
|
|
18
17
|
import { Command } from 'commander';
|
|
19
18
|
import { spawnSync, spawn, execFileSync } from 'child_process';
|
|
20
|
-
import { mkdir, writeFile,
|
|
19
|
+
import { mkdir, writeFile, readFile } from 'fs/promises';
|
|
21
20
|
import path4, { join } from 'path';
|
|
22
|
-
import { CommandOutcomeSchema, createCLILogger, CLIError, syncFromProduction,
|
|
21
|
+
import { CommandOutcomeSchema, createCLILogger, CLIError, syncFromProduction, GITHUB_API, getClassificationForProfile, formatDuration, isTimeoutLikeMessage, buildCommandOutcomeSummary, deriveCommandExitMode, getStatusIcon, detectDatabasePackage, DATABASE_PACKAGE_CANDIDATES } from '@runa-ai/runa';
|
|
23
22
|
import { z } from 'zod';
|
|
24
23
|
import { existsSync, createWriteStream, readFileSync, readdirSync, statSync, promises, lstatSync } from 'fs';
|
|
25
24
|
import { resolve4 } from 'dns/promises';
|
|
@@ -37,28 +36,6 @@ init_esm_shims();
|
|
|
37
36
|
|
|
38
37
|
// src/commands/ci/commands/ci-checks.ts
|
|
39
38
|
init_esm_shims();
|
|
40
|
-
|
|
41
|
-
// src/commands/ci/utils/github.ts
|
|
42
|
-
init_esm_shims();
|
|
43
|
-
async function appendGithubStepSummary(markdown) {
|
|
44
|
-
const file = process.env.GITHUB_STEP_SUMMARY;
|
|
45
|
-
if (!file) return;
|
|
46
|
-
await appendFile(file, `${markdown.trimEnd()}
|
|
47
|
-
|
|
48
|
-
`, "utf-8");
|
|
49
|
-
}
|
|
50
|
-
function addGithubMask(value) {
|
|
51
|
-
if (!value) return;
|
|
52
|
-
console.log(`::add-mask::${value}`);
|
|
53
|
-
}
|
|
54
|
-
z.object({
|
|
55
|
-
action: z.string().optional(),
|
|
56
|
-
pull_request: z.object({
|
|
57
|
-
number: z.number().int()
|
|
58
|
-
})
|
|
59
|
-
}).passthrough();
|
|
60
|
-
|
|
61
|
-
// src/commands/ci/commands/ci-checks.ts
|
|
62
39
|
async function runTool(params) {
|
|
63
40
|
const startTime = Date.now();
|
|
64
41
|
return new Promise((resolve) => {
|
|
@@ -859,8 +836,8 @@ async function detectRisks(repoRoot, tmpDir, timeoutMs) {
|
|
|
859
836
|
} catch (error) {
|
|
860
837
|
let logContent = "";
|
|
861
838
|
try {
|
|
862
|
-
const { readFileSync:
|
|
863
|
-
logContent =
|
|
839
|
+
const { readFileSync: readFileSync4 } = await import('fs');
|
|
840
|
+
logContent = readFileSync4(logFile, "utf-8");
|
|
864
841
|
} catch {
|
|
865
842
|
}
|
|
866
843
|
const isInitialDeployment = logContent.includes("No common ancestor") || logContent.includes("INITIAL DEPLOYMENT");
|
|
@@ -991,8 +968,8 @@ async function applyProductionSchema(repoRoot, tmpDir, productionDbUrlAdmin, pro
|
|
|
991
968
|
const totalMs = Date.now() - startTime;
|
|
992
969
|
let logContent = "";
|
|
993
970
|
try {
|
|
994
|
-
const { readFileSync:
|
|
995
|
-
logContent =
|
|
971
|
+
const { readFileSync: readFileSync4 } = await import('fs');
|
|
972
|
+
logContent = readFileSync4(logPath, "utf-8");
|
|
996
973
|
} catch {
|
|
997
974
|
}
|
|
998
975
|
const parsed = parseApplyLog(logContent);
|
|
@@ -1194,135 +1171,6 @@ async function maybePostFailureComment(params) {
|
|
|
1194
1171
|
}
|
|
1195
1172
|
}
|
|
1196
1173
|
|
|
1197
|
-
// src/commands/ci/commands/ci-prod-utils.ts
|
|
1198
|
-
init_esm_shims();
|
|
1199
|
-
function requireEnv(name) {
|
|
1200
|
-
const v = process.env[name];
|
|
1201
|
-
if (v && v.trim().length > 0) return v.trim();
|
|
1202
|
-
throw new CLIError(
|
|
1203
|
-
`Missing required environment variable: ${name}`,
|
|
1204
|
-
"CI_INPUT_MISSING",
|
|
1205
|
-
[`Set ${name} in GitHub Actions secrets/env`, "If unsure, run: runa check"],
|
|
1206
|
-
void 0,
|
|
1207
|
-
10
|
|
1208
|
-
);
|
|
1209
|
-
}
|
|
1210
|
-
function resolveRepoKind() {
|
|
1211
|
-
const env = detectEnvironment(process.cwd());
|
|
1212
|
-
const result = env === "runa-repo" ? "monorepo" : env === "pj-repo" ? "pj-repo" : "unknown";
|
|
1213
|
-
if (process.env.RUNA_DEBUG === "true") {
|
|
1214
|
-
console.error("[DEBUG:resolveRepoKind]", {
|
|
1215
|
-
cwd: process.cwd(),
|
|
1216
|
-
detectedEnv: env,
|
|
1217
|
-
result
|
|
1218
|
-
});
|
|
1219
|
-
}
|
|
1220
|
-
return result;
|
|
1221
|
-
}
|
|
1222
|
-
function createInitialSummary(params) {
|
|
1223
|
-
return {
|
|
1224
|
-
version: "1.0",
|
|
1225
|
-
mode: params.mode,
|
|
1226
|
-
command: "ci prod-apply",
|
|
1227
|
-
status: "failure",
|
|
1228
|
-
startedAt: params.startedAt.toISOString(),
|
|
1229
|
-
endedAt: params.startedAt.toISOString(),
|
|
1230
|
-
durationMs: 0,
|
|
1231
|
-
repoKind: resolveRepoKind(),
|
|
1232
|
-
detected: {},
|
|
1233
|
-
diagnostics: {},
|
|
1234
|
-
steps: {},
|
|
1235
|
-
layers: {},
|
|
1236
|
-
errors: []
|
|
1237
|
-
};
|
|
1238
|
-
}
|
|
1239
|
-
function requireCiAutoApprove(params) {
|
|
1240
|
-
if (params.mode !== "github-actions") return;
|
|
1241
|
-
if (params.autoApprove === true) return;
|
|
1242
|
-
throw new CLIError(
|
|
1243
|
-
"Missing required flag: --auto-approve (required in CI mode)",
|
|
1244
|
-
"CI_AUTO_APPROVE_REQUIRED",
|
|
1245
|
-
["Re-run with: runa ci prod-apply --auto-approve", "Keep CI non-interactive and deterministic"],
|
|
1246
|
-
void 0,
|
|
1247
|
-
10
|
|
1248
|
-
);
|
|
1249
|
-
}
|
|
1250
|
-
function resolveProdApplyInputs() {
|
|
1251
|
-
const productionDatabaseUrlAdmin = requireEnv("GH_DATABASE_URL_ADMIN");
|
|
1252
|
-
const productionDatabaseUrl = requireEnv("GH_DATABASE_URL");
|
|
1253
|
-
addGithubMask(productionDatabaseUrlAdmin);
|
|
1254
|
-
addGithubMask(productionDatabaseUrl);
|
|
1255
|
-
return {
|
|
1256
|
-
productionDatabaseUrlAdmin,
|
|
1257
|
-
productionDatabaseUrl,
|
|
1258
|
-
githubSha: process.env.GITHUB_SHA ?? "unknown",
|
|
1259
|
-
githubActor: process.env.GITHUB_ACTOR ?? "unknown",
|
|
1260
|
-
githubRepository: process.env.GITHUB_REPOSITORY ?? "unknown"
|
|
1261
|
-
};
|
|
1262
|
-
}
|
|
1263
|
-
function setSummaryErrorFromUnknown(summary, error) {
|
|
1264
|
-
if (error instanceof CLIError) {
|
|
1265
|
-
summary.errors.push({
|
|
1266
|
-
code: error.code ?? "CI_ERROR",
|
|
1267
|
-
message: error.message,
|
|
1268
|
-
step: "ci prod-apply",
|
|
1269
|
-
details: error.cause instanceof Error ? error.cause.message : void 0
|
|
1270
|
-
});
|
|
1271
|
-
return;
|
|
1272
|
-
}
|
|
1273
|
-
if (error instanceof Error) {
|
|
1274
|
-
summary.errors.push({ code: "CI_ERROR", message: error.message, step: "ci prod-apply" });
|
|
1275
|
-
return;
|
|
1276
|
-
}
|
|
1277
|
-
summary.errors.push({ code: "CI_ERROR", message: String(error), step: "ci prod-apply" });
|
|
1278
|
-
}
|
|
1279
|
-
function buildCiProdApplyStepSummaryMarkdown(params) {
|
|
1280
|
-
const { summary } = params;
|
|
1281
|
-
const lines = [];
|
|
1282
|
-
const statusEmoji = summary.status === "success" ? "\u2705" : "\u274C";
|
|
1283
|
-
const duration = formatDuration(summary.durationMs);
|
|
1284
|
-
lines.push(
|
|
1285
|
-
`## ${statusEmoji} Production Deploy ${summary.status === "success" ? "Completed" : "Failed"}`
|
|
1286
|
-
);
|
|
1287
|
-
lines.push("");
|
|
1288
|
-
lines.push(`**Duration**: ${duration}`);
|
|
1289
|
-
lines.push("");
|
|
1290
|
-
if (summary.dbOutcome) {
|
|
1291
|
-
lines.push(`**Exit mode**: \`${summary.dbOutcome.exitMode}\``);
|
|
1292
|
-
const failedPhase = summary.dbOutcome.phases.find(
|
|
1293
|
-
(phase) => phase.status === "failed" || phase.status === "timeout"
|
|
1294
|
-
);
|
|
1295
|
-
if (failedPhase) {
|
|
1296
|
-
lines.push(`**Failed phase**: \`${failedPhase.id}\``);
|
|
1297
|
-
}
|
|
1298
|
-
if (summary.dbOutcome.summary.warnings > 0) {
|
|
1299
|
-
lines.push(`**Warnings**: ${summary.dbOutcome.summary.warnings}`);
|
|
1300
|
-
}
|
|
1301
|
-
lines.push("");
|
|
1302
|
-
}
|
|
1303
|
-
if (summary.errors.length > 0) {
|
|
1304
|
-
lines.push("### \u274C Errors");
|
|
1305
|
-
lines.push("");
|
|
1306
|
-
for (const e of summary.errors) {
|
|
1307
|
-
lines.push(`- **${e.code}**: ${e.message}`);
|
|
1308
|
-
}
|
|
1309
|
-
lines.push("");
|
|
1310
|
-
}
|
|
1311
|
-
lines.push("<details>");
|
|
1312
|
-
lines.push("<summary>\u{1F4CB} Technical Details</summary>");
|
|
1313
|
-
lines.push("");
|
|
1314
|
-
lines.push(`- Command: \`${summary.command}\``);
|
|
1315
|
-
lines.push(`- Mode: \`${summary.mode}\``);
|
|
1316
|
-
lines.push(`- Summary: \`${params.summaryPath}\``);
|
|
1317
|
-
if (summary.dbOutcome) {
|
|
1318
|
-
lines.push(`- DB exit mode: \`${summary.dbOutcome.exitMode}\``);
|
|
1319
|
-
}
|
|
1320
|
-
lines.push("");
|
|
1321
|
-
lines.push("</details>");
|
|
1322
|
-
lines.push("");
|
|
1323
|
-
return lines.join("\n");
|
|
1324
|
-
}
|
|
1325
|
-
|
|
1326
1174
|
// src/commands/ci/commands/ci-prod-workflow.ts
|
|
1327
1175
|
init_esm_shims();
|
|
1328
1176
|
|
|
@@ -1756,22 +1604,6 @@ var CiProdApplyWorkflow = class {
|
|
|
1756
1604
|
}
|
|
1757
1605
|
};
|
|
1758
1606
|
|
|
1759
|
-
// src/commands/ci/commands/ci-resolvers.ts
|
|
1760
|
-
init_esm_shims();
|
|
1761
|
-
|
|
1762
|
-
// src/commands/ci/utils/config-readers.ts
|
|
1763
|
-
init_esm_shims();
|
|
1764
|
-
|
|
1765
|
-
// src/commands/ci/commands/ci-resolvers.ts
|
|
1766
|
-
function resolveMode(modeRaw) {
|
|
1767
|
-
if (modeRaw === "github-actions" || modeRaw === "local") return modeRaw;
|
|
1768
|
-
return process.env.GITHUB_ACTIONS === "true" ? "github-actions" : "local";
|
|
1769
|
-
}
|
|
1770
|
-
function parseIntOr(value, fallback) {
|
|
1771
|
-
const n = Number.parseInt(String(value ?? ""), 10);
|
|
1772
|
-
return Number.isNaN(n) ? fallback : n;
|
|
1773
|
-
}
|
|
1774
|
-
|
|
1775
1607
|
// src/commands/ci/utils/workflow-idempotency.ts
|
|
1776
1608
|
init_esm_shims();
|
|
1777
1609
|
var PROD_DEPLOY_LOCK_ID = 88889;
|
|
@@ -4256,41 +4088,6 @@ function extractSqlFromSchemaChanges(fullOutput) {
|
|
|
4256
4088
|
}
|
|
4257
4089
|
return null;
|
|
4258
4090
|
}
|
|
4259
|
-
function getIdempotentRoleNames(repoRoot) {
|
|
4260
|
-
const idempotentDir = path4.join(repoRoot, "supabase", "schemas", "idempotent");
|
|
4261
|
-
const roles = [];
|
|
4262
|
-
try {
|
|
4263
|
-
if (!existsSync(idempotentDir)) return [];
|
|
4264
|
-
const files = readdirSync(idempotentDir).filter((f) => f.endsWith(".sql"));
|
|
4265
|
-
for (const file of files) {
|
|
4266
|
-
const content = readFileSync(path4.join(idempotentDir, file), "utf-8");
|
|
4267
|
-
const roleMatches = content.matchAll(/CREATE\s+ROLE\s+(\w+)\s+WITH/gi);
|
|
4268
|
-
for (const match of roleMatches) {
|
|
4269
|
-
if (match[1]) roles.push(match[1].toLowerCase());
|
|
4270
|
-
}
|
|
4271
|
-
const existsMatches = content.matchAll(/rolname\s*=\s*'(\w+)'/gi);
|
|
4272
|
-
for (const match of existsMatches) {
|
|
4273
|
-
if (match[1] && !roles.includes(match[1].toLowerCase())) {
|
|
4274
|
-
roles.push(match[1].toLowerCase());
|
|
4275
|
-
}
|
|
4276
|
-
}
|
|
4277
|
-
}
|
|
4278
|
-
} catch {
|
|
4279
|
-
}
|
|
4280
|
-
return [...new Set(roles)];
|
|
4281
|
-
}
|
|
4282
|
-
function isIdempotentRoleHazard(hazardType, hazardMessage, causingSql, idempotentRoles) {
|
|
4283
|
-
if (hazardType.toUpperCase() !== "AUTHZ_UPDATE") return false;
|
|
4284
|
-
if (idempotentRoles.length === 0) return false;
|
|
4285
|
-
const sql = (causingSql || "").trim().toLowerCase();
|
|
4286
|
-
const isGrantRevoke = /^\s*(?:grant|revoke)\b/i.test(sql);
|
|
4287
|
-
if (sql && !isGrantRevoke) return false;
|
|
4288
|
-
const textToCheck = sql || hazardMessage.toLowerCase();
|
|
4289
|
-
for (const role of idempotentRoles) {
|
|
4290
|
-
if (textToCheck.includes(role)) return true;
|
|
4291
|
-
}
|
|
4292
|
-
return false;
|
|
4293
|
-
}
|
|
4294
4091
|
function getHazardSeverity(type) {
|
|
4295
4092
|
switch (type.toUpperCase()) {
|
|
4296
4093
|
case "DELETES_DATA":
|
|
@@ -4332,20 +4129,26 @@ function createHazardDetail(type, message, causingSql) {
|
|
|
4332
4129
|
causingSql
|
|
4333
4130
|
};
|
|
4334
4131
|
}
|
|
4335
|
-
function collectCommentHazards(lines,
|
|
4132
|
+
function collectCommentHazards(lines, schemasDir) {
|
|
4336
4133
|
const hazards = [];
|
|
4337
4134
|
for (let i = 0; i < lines.length; i++) {
|
|
4338
4135
|
const parsed = parseHazardLine(lines[i] ?? "");
|
|
4339
4136
|
if (!parsed) continue;
|
|
4340
4137
|
const causingSql = findCausingSqlLine(lines, i);
|
|
4341
|
-
|
|
4138
|
+
const registryHazard = {
|
|
4139
|
+
type: parsed.type,
|
|
4140
|
+
message: parsed.message,
|
|
4141
|
+
fullMatch: lines[i] ?? "",
|
|
4142
|
+
causingSql
|
|
4143
|
+
};
|
|
4144
|
+
if (isIdempotentRoleHazard(registryHazard, schemasDir)) {
|
|
4342
4145
|
continue;
|
|
4343
4146
|
}
|
|
4344
4147
|
hazards.push(createHazardDetail(parsed.type, parsed.message, causingSql));
|
|
4345
4148
|
}
|
|
4346
4149
|
return hazards;
|
|
4347
4150
|
}
|
|
4348
|
-
function appendEmojiHazards(fullOutput,
|
|
4151
|
+
function appendEmojiHazards(fullOutput, schemasDir, hazards) {
|
|
4349
4152
|
const hazardPrefixes = ["\u{1F534}", "\u{1F7E0}", "\u{1F7E1}", "\u26A0"];
|
|
4350
4153
|
for (const line of fullOutput.split("\n")) {
|
|
4351
4154
|
const trimmed = line.trim();
|
|
@@ -4356,7 +4159,12 @@ function appendEmojiHazards(fullOutput, idempotentRoles, hazards) {
|
|
|
4356
4159
|
if (!match?.[1] || !match[2]) continue;
|
|
4357
4160
|
const type = match[1].toUpperCase();
|
|
4358
4161
|
const message = match[2].trim();
|
|
4359
|
-
|
|
4162
|
+
const registryHazard = {
|
|
4163
|
+
type,
|
|
4164
|
+
message,
|
|
4165
|
+
fullMatch: trimmed
|
|
4166
|
+
};
|
|
4167
|
+
if (isIdempotentRoleHazard(registryHazard, schemasDir)) {
|
|
4360
4168
|
continue;
|
|
4361
4169
|
}
|
|
4362
4170
|
const isDuplicate = hazards.some(
|
|
@@ -4369,9 +4177,9 @@ function appendEmojiHazards(fullOutput, idempotentRoles, hazards) {
|
|
|
4369
4177
|
}
|
|
4370
4178
|
function extractHazardsWithContext(fullOutput, repoRoot) {
|
|
4371
4179
|
const lines = fullOutput.split("\n");
|
|
4372
|
-
const
|
|
4373
|
-
const hazards = collectCommentHazards(lines,
|
|
4374
|
-
appendEmojiHazards(fullOutput,
|
|
4180
|
+
const schemasDir = repoRoot ? path4.join(repoRoot, "supabase", "schemas", "declarative") : void 0;
|
|
4181
|
+
const hazards = collectCommentHazards(lines, schemasDir);
|
|
4182
|
+
appendEmojiHazards(fullOutput, schemasDir, hazards);
|
|
4375
4183
|
return hazards;
|
|
4376
4184
|
}
|
|
4377
4185
|
function stripAnsi(text) {
|
|
@@ -4870,51 +4678,6 @@ function getSchemaGitDiff(repoRoot) {
|
|
|
4870
4678
|
return null;
|
|
4871
4679
|
}
|
|
4872
4680
|
}
|
|
4873
|
-
function extractRolesFromSql(content) {
|
|
4874
|
-
const roles = [];
|
|
4875
|
-
const roleMatches = content.matchAll(/CREATE\s+ROLE\s+(\w+)\s+WITH/gi);
|
|
4876
|
-
for (const match of roleMatches) {
|
|
4877
|
-
if (match[1]) roles.push(match[1].toLowerCase());
|
|
4878
|
-
}
|
|
4879
|
-
const existsMatches = content.matchAll(/rolname\s*=\s*'(\w+)'/gi);
|
|
4880
|
-
for (const match of existsMatches) {
|
|
4881
|
-
if (match[1] && !roles.includes(match[1].toLowerCase())) {
|
|
4882
|
-
roles.push(match[1].toLowerCase());
|
|
4883
|
-
}
|
|
4884
|
-
}
|
|
4885
|
-
return roles;
|
|
4886
|
-
}
|
|
4887
|
-
function getIdempotentRoleNames2(repoRoot) {
|
|
4888
|
-
const idempotentDir = path4.join(repoRoot, "supabase", "schemas", "idempotent");
|
|
4889
|
-
const roles = [];
|
|
4890
|
-
try {
|
|
4891
|
-
const fs2 = __require("fs");
|
|
4892
|
-
if (!fs2.existsSync(idempotentDir)) return [];
|
|
4893
|
-
const files = fs2.readdirSync(idempotentDir).filter((f) => f.endsWith(".sql"));
|
|
4894
|
-
for (const file of files) {
|
|
4895
|
-
const filePath = path4.join(idempotentDir, file);
|
|
4896
|
-
if (!isPathContained(idempotentDir, filePath)) {
|
|
4897
|
-
continue;
|
|
4898
|
-
}
|
|
4899
|
-
const content = fs2.readFileSync(filePath, "utf-8");
|
|
4900
|
-
roles.push(...extractRolesFromSql(content));
|
|
4901
|
-
}
|
|
4902
|
-
} catch {
|
|
4903
|
-
}
|
|
4904
|
-
return [...new Set(roles)];
|
|
4905
|
-
}
|
|
4906
|
-
function isIdempotentRoleHazard2(hazardType, hazardMessage, idempotentRoles, causingSql) {
|
|
4907
|
-
if (hazardType !== "AUTHZ_UPDATE") return false;
|
|
4908
|
-
if (idempotentRoles.length === 0) return false;
|
|
4909
|
-
const sql = ("").trim().toLowerCase();
|
|
4910
|
-
const isGrantRevoke = /^\s*(?:grant|revoke)\b/i.test(sql);
|
|
4911
|
-
if (sql && !isGrantRevoke) return false;
|
|
4912
|
-
const textToCheck = sql || hazardMessage.toLowerCase();
|
|
4913
|
-
for (const role of idempotentRoles) {
|
|
4914
|
-
if (textToCheck.includes(role)) return true;
|
|
4915
|
-
}
|
|
4916
|
-
return false;
|
|
4917
|
-
}
|
|
4918
4681
|
function createEmptySchemaChangeStats() {
|
|
4919
4682
|
return {
|
|
4920
4683
|
creates: { tables: 0, indexes: 0, policies: 0, functions: 0, other: 0 },
|
|
@@ -4934,13 +4697,18 @@ function resolveSqlInput(sqlInput, logPath) {
|
|
|
4934
4697
|
return null;
|
|
4935
4698
|
}
|
|
4936
4699
|
}
|
|
4937
|
-
function parseHazards(sql,
|
|
4700
|
+
function parseHazards(sql, schemasDir) {
|
|
4938
4701
|
const hazards = [];
|
|
4939
4702
|
const hazardMatches = sql.matchAll(/-- Hazard (\w+): (.+)/g);
|
|
4940
4703
|
for (const match of hazardMatches) {
|
|
4941
4704
|
const hazardType = match[1];
|
|
4942
4705
|
const hazardMessage = match[2];
|
|
4943
|
-
|
|
4706
|
+
const registryHazard = {
|
|
4707
|
+
type: hazardType,
|
|
4708
|
+
message: hazardMessage,
|
|
4709
|
+
fullMatch: match[0]
|
|
4710
|
+
};
|
|
4711
|
+
if (isIdempotentRoleHazard(registryHazard, schemasDir)) {
|
|
4944
4712
|
continue;
|
|
4945
4713
|
}
|
|
4946
4714
|
hazards.push(`${hazardType}: ${hazardMessage}`);
|
|
@@ -4960,8 +4728,8 @@ function parseSchemaChangeStats(sqlInput, logPath, repoRoot) {
|
|
|
4960
4728
|
stats.creates.other = countMatches(sql, /CREATE (TRIGGER|TYPE|SEQUENCE|VIEW|SCHEMA)/gi);
|
|
4961
4729
|
stats.alters = countMatches(sql, /ALTER (TABLE|COLUMN|INDEX|POLICY|FUNCTION)/gi);
|
|
4962
4730
|
stats.drops = countMatches(sql, /DROP (TABLE|COLUMN|INDEX|POLICY|FUNCTION|TRIGGER)/gi);
|
|
4963
|
-
const
|
|
4964
|
-
stats.hazards = parseHazards(sql,
|
|
4731
|
+
const schemasDir = repoRoot ? path4.join(repoRoot, "supabase", "schemas", "declarative") : void 0;
|
|
4732
|
+
stats.hazards = parseHazards(sql, schemasDir);
|
|
4965
4733
|
return stats;
|
|
4966
4734
|
}
|
|
4967
4735
|
function isCheckSummaryOutput(output) {
|
|
@@ -5235,9 +5003,9 @@ function detectSupabaseContainers() {
|
|
|
5235
5003
|
async function checkSupabasePortConflicts(repoRoot) {
|
|
5236
5004
|
let dbPort = 54322;
|
|
5237
5005
|
try {
|
|
5238
|
-
const { readFileSync:
|
|
5006
|
+
const { readFileSync: readFileSync4 } = await import('fs');
|
|
5239
5007
|
const configPath = path4.join(repoRoot, "supabase", "config.toml");
|
|
5240
|
-
const content =
|
|
5008
|
+
const content = readFileSync4(configPath, "utf-8");
|
|
5241
5009
|
const match = /\[db\][^[]*?port\s*=\s*(\d+)/s.exec(content);
|
|
5242
5010
|
if (match?.[1]) dbPort = Number.parseInt(match[1], 10);
|
|
5243
5011
|
} catch {
|
|
@@ -5654,116 +5422,96 @@ async function queryScalar(params) {
|
|
|
5654
5422
|
async function detectDbCapabilities(params) {
|
|
5655
5423
|
const caps = /* @__PURE__ */ new Set();
|
|
5656
5424
|
const diagnostics = {};
|
|
5657
|
-
const
|
|
5425
|
+
const batchedSql = `
|
|
5426
|
+
SELECT json_build_object(
|
|
5427
|
+
'isPostgres', (SELECT version() LIKE 'PostgreSQL%'),
|
|
5428
|
+
'tableCount', (
|
|
5429
|
+
SELECT COUNT(*)::int
|
|
5430
|
+
FROM information_schema.tables
|
|
5431
|
+
WHERE table_type = 'BASE TABLE'
|
|
5432
|
+
AND table_schema NOT LIKE 'pg_%'
|
|
5433
|
+
AND table_schema NOT IN ('information_schema')
|
|
5434
|
+
),
|
|
5435
|
+
'rlsCount', (
|
|
5436
|
+
SELECT COUNT(*)::int
|
|
5437
|
+
FROM pg_class c
|
|
5438
|
+
JOIN pg_namespace n ON n.oid = c.relnamespace
|
|
5439
|
+
WHERE c.relkind IN ('r', 'p')
|
|
5440
|
+
AND n.nspname NOT LIKE 'pg_%'
|
|
5441
|
+
AND n.nspname NOT IN ('information_schema')
|
|
5442
|
+
AND c.relrowsecurity
|
|
5443
|
+
),
|
|
5444
|
+
'canSelectAny', (
|
|
5445
|
+
SELECT COALESCE(bool_or(has_table_privilege(current_user, tbl, 'select')), false)
|
|
5446
|
+
FROM (
|
|
5447
|
+
SELECT quote_ident(table_schema) || '.' || quote_ident(table_name) AS tbl
|
|
5448
|
+
FROM information_schema.tables
|
|
5449
|
+
WHERE table_type = 'BASE TABLE'
|
|
5450
|
+
AND table_schema NOT LIKE 'pg_%'
|
|
5451
|
+
AND table_schema NOT IN ('information_schema')
|
|
5452
|
+
LIMIT 50 -- cap sampling to prevent slow scans on large databases
|
|
5453
|
+
) candidates
|
|
5454
|
+
),
|
|
5455
|
+
'authenticatedExists', (
|
|
5456
|
+
SELECT EXISTS (SELECT 1 FROM pg_roles WHERE rolname = 'authenticated')
|
|
5457
|
+
),
|
|
5458
|
+
'isMember', (
|
|
5459
|
+
SELECT CASE
|
|
5460
|
+
WHEN EXISTS (SELECT 1 FROM pg_roles WHERE rolname = 'authenticated')
|
|
5461
|
+
THEN pg_has_role(current_user, 'authenticated', 'member')
|
|
5462
|
+
ELSE false
|
|
5463
|
+
END
|
|
5464
|
+
)
|
|
5465
|
+
)::text;
|
|
5466
|
+
`.trim();
|
|
5467
|
+
const raw = await queryScalar({
|
|
5658
5468
|
repoRoot: params.repoRoot,
|
|
5659
5469
|
tmpDir: params.tmpDir,
|
|
5660
5470
|
databaseUrl: params.databaseUrlApp,
|
|
5661
|
-
sql:
|
|
5662
|
-
logName: "cap-
|
|
5471
|
+
sql: batchedSql,
|
|
5472
|
+
logName: "cap-db-batch"
|
|
5663
5473
|
});
|
|
5664
|
-
|
|
5665
|
-
|
|
5474
|
+
let parsed;
|
|
5475
|
+
try {
|
|
5476
|
+
parsed = JSON.parse(raw);
|
|
5477
|
+
} catch {
|
|
5478
|
+
diagnostics["db.postgres"] = `Failed to parse batched query result (raw=${raw.substring(0, 200)})`;
|
|
5479
|
+
return { capabilities: caps, diagnostics };
|
|
5480
|
+
}
|
|
5481
|
+
if (parsed.isPostgres) {
|
|
5666
5482
|
caps.add("db.postgres");
|
|
5667
5483
|
diagnostics["db.postgres"] = "PostgreSQL detected via SELECT version()";
|
|
5668
5484
|
} else {
|
|
5669
|
-
diagnostics["db.postgres"] = `Not PostgreSQL
|
|
5485
|
+
diagnostics["db.postgres"] = `Not PostgreSQL`;
|
|
5670
5486
|
return { capabilities: caps, diagnostics };
|
|
5671
5487
|
}
|
|
5672
|
-
|
|
5673
|
-
repoRoot: params.repoRoot,
|
|
5674
|
-
tmpDir: params.tmpDir,
|
|
5675
|
-
databaseUrl: params.databaseUrlApp,
|
|
5676
|
-
sql: `
|
|
5677
|
-
SELECT COUNT(*)::int
|
|
5678
|
-
FROM information_schema.tables
|
|
5679
|
-
WHERE table_type='BASE TABLE'
|
|
5680
|
-
AND table_schema NOT LIKE 'pg_%'
|
|
5681
|
-
AND table_schema NOT IN ('information_schema');
|
|
5682
|
-
`.trim(),
|
|
5683
|
-
logName: "cap-table-count"
|
|
5684
|
-
});
|
|
5685
|
-
const tableCount = Number.parseInt(tablesRaw, 10);
|
|
5686
|
-
if (Number.isFinite(tableCount) && tableCount > 0) {
|
|
5488
|
+
if (Number.isFinite(parsed.tableCount) && parsed.tableCount > 0) {
|
|
5687
5489
|
caps.add("db.schemaApplied");
|
|
5688
|
-
diagnostics["db.schemaApplied"] = `Non-system base tables detected: ${tableCount}`;
|
|
5490
|
+
diagnostics["db.schemaApplied"] = `Non-system base tables detected: ${parsed.tableCount}`;
|
|
5689
5491
|
} else {
|
|
5690
|
-
diagnostics["db.schemaApplied"] = `No non-system base tables detected (
|
|
5492
|
+
diagnostics["db.schemaApplied"] = `No non-system base tables detected (count=${parsed.tableCount})`;
|
|
5691
5493
|
}
|
|
5692
|
-
|
|
5693
|
-
repoRoot: params.repoRoot,
|
|
5694
|
-
tmpDir: params.tmpDir,
|
|
5695
|
-
databaseUrl: params.databaseUrlApp,
|
|
5696
|
-
sql: `
|
|
5697
|
-
SELECT COUNT(*)::int
|
|
5698
|
-
FROM pg_class c
|
|
5699
|
-
JOIN pg_namespace n ON n.oid=c.relnamespace
|
|
5700
|
-
WHERE c.relkind IN ('r','p')
|
|
5701
|
-
AND n.nspname NOT LIKE 'pg_%'
|
|
5702
|
-
AND n.nspname NOT IN ('information_schema')
|
|
5703
|
-
AND c.relrowsecurity;
|
|
5704
|
-
`.trim(),
|
|
5705
|
-
logName: "cap-rls-count"
|
|
5706
|
-
});
|
|
5707
|
-
const rlsCount = Number.parseInt(rlsCountRaw, 10);
|
|
5708
|
-
if (Number.isFinite(rlsCount) && rlsCount > 0) {
|
|
5494
|
+
if (Number.isFinite(parsed.rlsCount) && parsed.rlsCount > 0) {
|
|
5709
5495
|
caps.add("db.rlsEnabled");
|
|
5710
|
-
diagnostics["db.rlsEnabled"] = `RLS-enabled tables detected: ${rlsCount}`;
|
|
5496
|
+
diagnostics["db.rlsEnabled"] = `RLS-enabled tables detected: ${parsed.rlsCount}`;
|
|
5711
5497
|
} else {
|
|
5712
|
-
diagnostics["db.rlsEnabled"] = `No RLS-enabled tables detected (
|
|
5498
|
+
diagnostics["db.rlsEnabled"] = `No RLS-enabled tables detected (count=${parsed.rlsCount})`;
|
|
5713
5499
|
}
|
|
5714
|
-
|
|
5715
|
-
repoRoot: params.repoRoot,
|
|
5716
|
-
tmpDir: params.tmpDir,
|
|
5717
|
-
databaseUrl: params.databaseUrlApp,
|
|
5718
|
-
sql: `
|
|
5719
|
-
WITH candidates AS (
|
|
5720
|
-
SELECT quote_ident(table_schema)||'.'||quote_ident(table_name) AS tbl
|
|
5721
|
-
FROM information_schema.tables
|
|
5722
|
-
WHERE table_type='BASE TABLE'
|
|
5723
|
-
AND table_schema NOT LIKE 'pg_%'
|
|
5724
|
-
AND table_schema NOT IN ('information_schema')
|
|
5725
|
-
LIMIT 50
|
|
5726
|
-
),
|
|
5727
|
-
checks AS (
|
|
5728
|
-
SELECT bool_or(has_table_privilege(current_user, tbl, 'select')) AS ok
|
|
5729
|
-
FROM candidates
|
|
5730
|
-
)
|
|
5731
|
-
SELECT COALESCE(ok,false) FROM checks;
|
|
5732
|
-
`.trim(),
|
|
5733
|
-
logName: "cap-select-any"
|
|
5734
|
-
});
|
|
5735
|
-
const canSelectAny = parseBoolish(canSelectAnyRaw);
|
|
5736
|
-
if (canSelectAny) {
|
|
5500
|
+
if (parsed.canSelectAny) {
|
|
5737
5501
|
caps.add("db.appCanSelectSomeTable");
|
|
5738
5502
|
diagnostics["db.appCanSelectSomeTable"] = "has_table_privilege(select) true for at least one table";
|
|
5739
5503
|
} else {
|
|
5740
|
-
diagnostics["db.appCanSelectSomeTable"] =
|
|
5504
|
+
diagnostics["db.appCanSelectSomeTable"] = "App role cannot SELECT any sampled table";
|
|
5741
5505
|
}
|
|
5742
|
-
|
|
5743
|
-
repoRoot: params.repoRoot,
|
|
5744
|
-
tmpDir: params.tmpDir,
|
|
5745
|
-
databaseUrl: params.databaseUrlApp,
|
|
5746
|
-
sql: "SELECT EXISTS (SELECT 1 FROM pg_roles WHERE rolname='authenticated');",
|
|
5747
|
-
logName: "cap-authenticated-role-exists"
|
|
5748
|
-
});
|
|
5749
|
-
const authenticatedExists = parseBoolish(authenticatedExistsRaw);
|
|
5750
|
-
if (!authenticatedExists) {
|
|
5506
|
+
if (!parsed.authenticatedExists) {
|
|
5751
5507
|
diagnostics["db.appIsAuthenticatedMember"] = "n/a (authenticated role does not exist)";
|
|
5752
5508
|
return { capabilities: caps, diagnostics };
|
|
5753
5509
|
}
|
|
5754
|
-
|
|
5755
|
-
repoRoot: params.repoRoot,
|
|
5756
|
-
tmpDir: params.tmpDir,
|
|
5757
|
-
databaseUrl: params.databaseUrlApp,
|
|
5758
|
-
sql: "SELECT pg_has_role(current_user, 'authenticated', 'member');",
|
|
5759
|
-
logName: "cap-app-is-authenticated-member"
|
|
5760
|
-
});
|
|
5761
|
-
const isMember = parseBoolish(isMemberRaw);
|
|
5762
|
-
if (isMember) {
|
|
5510
|
+
if (parsed.isMember) {
|
|
5763
5511
|
caps.add("db.appIsAuthenticatedMember");
|
|
5764
5512
|
diagnostics["db.appIsAuthenticatedMember"] = "pg_has_role(member, authenticated) is true";
|
|
5765
5513
|
} else {
|
|
5766
|
-
diagnostics["db.appIsAuthenticatedMember"] =
|
|
5514
|
+
diagnostics["db.appIsAuthenticatedMember"] = "pg_has_role(member, authenticated)=false";
|
|
5767
5515
|
}
|
|
5768
5516
|
return { capabilities: caps, diagnostics };
|
|
5769
5517
|
}
|
|
@@ -5874,23 +5622,25 @@ async function detectUiContractReady(params) {
|
|
|
5874
5622
|
async function detectCiCapabilities(params) {
|
|
5875
5623
|
const caps = /* @__PURE__ */ new Set();
|
|
5876
5624
|
const diagnostics = {};
|
|
5877
|
-
const
|
|
5878
|
-
|
|
5879
|
-
|
|
5880
|
-
|
|
5881
|
-
}
|
|
5882
|
-
|
|
5883
|
-
|
|
5884
|
-
|
|
5885
|
-
|
|
5886
|
-
|
|
5625
|
+
const mergeDiagnostics = (result) => {
|
|
5626
|
+
for (const c of result.capabilities) caps.add(c);
|
|
5627
|
+
for (const [k, v] of Object.entries(result.diagnostics))
|
|
5628
|
+
diagnostics[k] = String(v);
|
|
5629
|
+
};
|
|
5630
|
+
const [db, e2e, ui] = await Promise.all([
|
|
5631
|
+
detectDbCapabilities({
|
|
5632
|
+
repoRoot: params.repoRoot,
|
|
5633
|
+
tmpDir: params.tmpDir,
|
|
5634
|
+
databaseUrlApp: params.databaseUrlApp
|
|
5635
|
+
}),
|
|
5636
|
+
detectGeneratedE2eCapabilities({ repoRoot: params.repoRoot }),
|
|
5637
|
+
detectUiContractReady({ baseUrl: params.baseUrl })
|
|
5638
|
+
]);
|
|
5639
|
+
mergeDiagnostics(db);
|
|
5640
|
+
mergeDiagnostics(e2e);
|
|
5641
|
+
mergeDiagnostics(ui);
|
|
5887
5642
|
const preview = await detectPreviewReachable({ baseUrl: params.baseUrl });
|
|
5888
|
-
|
|
5889
|
-
for (const [k, v] of Object.entries(preview.diagnostics))
|
|
5890
|
-
diagnostics[k] = String(v);
|
|
5891
|
-
const ui = await detectUiContractReady({ baseUrl: params.baseUrl });
|
|
5892
|
-
for (const c of ui.capabilities) caps.add(c);
|
|
5893
|
-
for (const [k, v] of Object.entries(ui.diagnostics)) diagnostics[k] = String(v);
|
|
5643
|
+
mergeDiagnostics(preview);
|
|
5894
5644
|
return { capabilities: Array.from(caps), diagnostics };
|
|
5895
5645
|
}
|
|
5896
5646
|
function resolveRequiredCapabilities(params) {
|
|
@@ -6711,9 +6461,6 @@ var runLayersActor = fromPromise(
|
|
|
6711
6461
|
progressIntervalSeconds = 30
|
|
6712
6462
|
} = input;
|
|
6713
6463
|
try {
|
|
6714
|
-
console.log(
|
|
6715
|
-
`[DEBUG] runLayersActor: Starting with layers=${JSON.stringify(layers)}, failFast=${failFast}`
|
|
6716
|
-
);
|
|
6717
6464
|
const results = await runLayersInParallel({
|
|
6718
6465
|
repoRoot,
|
|
6719
6466
|
tmpDir,
|
|
@@ -6727,15 +6474,9 @@ var runLayersActor = fromPromise(
|
|
|
6727
6474
|
});
|
|
6728
6475
|
const failedLayers = results.filter((r) => !r.success).map((r) => r.layer);
|
|
6729
6476
|
const allPassed = failedLayers.length === 0;
|
|
6730
|
-
console.log(
|
|
6731
|
-
`[DEBUG] runLayersActor: Complete. results=${results.length}, failedLayers=${JSON.stringify(failedLayers)}, allPassed=${allPassed}`
|
|
6732
|
-
);
|
|
6733
6477
|
return { results, allPassed, failedLayers };
|
|
6734
6478
|
} catch (error) {
|
|
6735
6479
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
6736
|
-
console.log(
|
|
6737
|
-
`[DEBUG] runLayersActor: Caught error: ${errorMessage}. Returning failedLayers=${JSON.stringify(layers)}`
|
|
6738
|
-
);
|
|
6739
6480
|
return {
|
|
6740
6481
|
results: [],
|
|
6741
6482
|
allPassed: false,
|
|
@@ -6812,8 +6553,22 @@ function shouldPostGitHubComment(context) {
|
|
|
6812
6553
|
if (context.input.skipGithubComment === true) return false;
|
|
6813
6554
|
return true;
|
|
6814
6555
|
}
|
|
6556
|
+
function isBlockingPhase(context) {
|
|
6557
|
+
return isCiPrMode(context) && context.phase === "blocking";
|
|
6558
|
+
}
|
|
6559
|
+
function isObservabilityPhase(context) {
|
|
6560
|
+
return isCiPrMode(context) && context.phase === "observability";
|
|
6561
|
+
}
|
|
6815
6562
|
function isTestPhase(context) {
|
|
6816
|
-
return isCiPrMode(context) && context.
|
|
6563
|
+
return isCiPrMode(context) && context.phase === "test";
|
|
6564
|
+
}
|
|
6565
|
+
function shouldRunPrExecutionPhase(context) {
|
|
6566
|
+
if (!isCiPrMode(context)) return true;
|
|
6567
|
+
return !isObservabilityPhase(context);
|
|
6568
|
+
}
|
|
6569
|
+
function shouldRunPrObservabilityPhase(context) {
|
|
6570
|
+
if (!isCiPrMode(context)) return true;
|
|
6571
|
+
return !isBlockingPhase(context) && !isTestPhase(context);
|
|
6817
6572
|
}
|
|
6818
6573
|
function hasError(context) {
|
|
6819
6574
|
return context.error !== null;
|
|
@@ -6868,7 +6623,7 @@ init_esm_shims();
|
|
|
6868
6623
|
|
|
6869
6624
|
// src/commands/ci/machine/formatters/sections/format-helpers.ts
|
|
6870
6625
|
init_esm_shims();
|
|
6871
|
-
function
|
|
6626
|
+
function formatDuration3(ms) {
|
|
6872
6627
|
if (ms < 1e3) return `${ms}ms`;
|
|
6873
6628
|
const seconds = Math.floor(ms / 1e3);
|
|
6874
6629
|
if (seconds < 60) return `${seconds}s`;
|
|
@@ -7713,6 +7468,10 @@ function generateKnownDriftOnlySection(schemaStats, expectedDrift) {
|
|
|
7713
7468
|
}
|
|
7714
7469
|
function generateProductionPreviewSection(phase, prodPreview, schemaDrift, schemaStats, expectedDrift) {
|
|
7715
7470
|
const signals = getProductionSchemaSignals(prodPreview, schemaStats, expectedDrift);
|
|
7471
|
+
const deferredSection = getDeferredProductionPreviewSection(phase, prodPreview);
|
|
7472
|
+
if (deferredSection.length > 0) {
|
|
7473
|
+
return deferredSection;
|
|
7474
|
+
}
|
|
7716
7475
|
if (!prodPreview?.executed) {
|
|
7717
7476
|
if (schemaDrift?.gitDiff?.filesChanged?.length) {
|
|
7718
7477
|
return [
|
|
@@ -7736,6 +7495,27 @@ function generateProductionPreviewSection(phase, prodPreview, schemaDrift, schem
|
|
|
7736
7495
|
}
|
|
7737
7496
|
return ["**\u672C\u756A\u30D7\u30EC\u30D3\u30E5\u30FC**: \u2705 \u672C\u756A\u306B\u9069\u7528\u3059\u308B\u30B9\u30AD\u30FC\u30DE\u5909\u66F4\u306F\u3042\u308A\u307E\u305B\u3093", ""];
|
|
7738
7497
|
}
|
|
7498
|
+
function getDeferredProductionPreviewSection(phase, prodPreview) {
|
|
7499
|
+
if (prodPreview?.executed) return [];
|
|
7500
|
+
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
|
+
case "test":
|
|
7509
|
+
return [
|
|
7510
|
+
"**\u672C\u756A\u30D7\u30EC\u30D3\u30E5\u30FC**: \u23ED\uFE0F phase=test \u306E\u305F\u3081\u30B9\u30AD\u30C3\u30D7",
|
|
7511
|
+
"",
|
|
7512
|
+
"> workflow-prepared DB \u3092\u518D\u5229\u7528\u3059\u308B\u8EFD\u91CF\u30D5\u30A7\u30FC\u30BA\u306E\u305F\u3081\u3001\u672C\u756A\u6BD4\u8F03\u306F\u5B9F\u884C\u3057\u3066\u3044\u307E\u305B\u3093\u3002",
|
|
7513
|
+
""
|
|
7514
|
+
];
|
|
7515
|
+
default:
|
|
7516
|
+
return [];
|
|
7517
|
+
}
|
|
7518
|
+
}
|
|
7739
7519
|
function shouldShowDeployButton(productionPreview, schemaDrift, hasSchemaChanges2) {
|
|
7740
7520
|
if (hasSchemaChanges2) return true;
|
|
7741
7521
|
if (productionPreview?.error) return true;
|
|
@@ -7744,6 +7524,7 @@ function shouldShowDeployButton(productionPreview, schemaDrift, hasSchemaChanges
|
|
|
7744
7524
|
}
|
|
7745
7525
|
function generateDeploySection(exitCode, layerResults, phase, gitBranchName, productionPreview, schemaDrift, schemaStats, expectedDrift, env) {
|
|
7746
7526
|
if (!checkIfDeployable(exitCode, layerResults)) return [];
|
|
7527
|
+
if (phase === "blocking" || phase === "test") return [];
|
|
7747
7528
|
const deployWorkflowUrl = env.repository ? `${env.serverUrl}/${env.repository}/actions/workflows/deploy-db.yml` : null;
|
|
7748
7529
|
const signals = getProductionSchemaSignals(productionPreview, schemaStats, expectedDrift);
|
|
7749
7530
|
const hasSchemaChanges2 = signals.requiresDeploy;
|
|
@@ -7778,12 +7559,20 @@ function generateDeploySection(exitCode, layerResults, phase, gitBranchName, pro
|
|
|
7778
7559
|
function generateObservabilitySectionBody(input) {
|
|
7779
7560
|
const env = getGitHubEnv();
|
|
7780
7561
|
const gitBranchName = input.prContext?.headBranch ?? input.branchName ?? "unknown";
|
|
7562
|
+
const deferredPreviewLines = input.phase === "blocking" || input.phase === "test" ? generateProductionPreviewSection(
|
|
7563
|
+
input.phase,
|
|
7564
|
+
input.productionPreview,
|
|
7565
|
+
input.schemaDrift,
|
|
7566
|
+
input.schemaStats ?? null,
|
|
7567
|
+
input.expectedDrift ?? []
|
|
7568
|
+
) : [];
|
|
7781
7569
|
const lines = [
|
|
7782
7570
|
...formatSchemaMatrix(
|
|
7783
7571
|
input.schemaStats,
|
|
7784
7572
|
input.schemaDrift?.gitDiff ?? null,
|
|
7785
7573
|
input.expectedDrift ?? []
|
|
7786
7574
|
),
|
|
7575
|
+
...deferredPreviewLines,
|
|
7787
7576
|
...generateDeploySection(
|
|
7788
7577
|
input.exitCode,
|
|
7789
7578
|
input.layerResults,
|
|
@@ -7867,7 +7656,7 @@ function calculateTotalElapsed(stepTimings2) {
|
|
|
7867
7656
|
if (!stepTimings2) return 0;
|
|
7868
7657
|
return Object.values(stepTimings2).reduce((sum, t) => sum + (t ?? 0), 0);
|
|
7869
7658
|
}
|
|
7870
|
-
function formatProgressBar(
|
|
7659
|
+
function formatProgressBar(_currentStep, completedSteps, failedStep, skippedSteps) {
|
|
7871
7660
|
const total = CI_STEPS.length;
|
|
7872
7661
|
const completedBase = completedSteps.length + skippedSteps.length;
|
|
7873
7662
|
const completed = failedStep ? completedBase : completedBase + 1;
|
|
@@ -8004,7 +7793,7 @@ function generateProgressHeader(currentStep, failedStep, completedSteps, skipped
|
|
|
8004
7793
|
);
|
|
8005
7794
|
}
|
|
8006
7795
|
if (totalElapsed > 0) {
|
|
8007
|
-
lines.push(`<sub>\u23F1\uFE0F \u7D4C\u904E\u6642\u9593: ${
|
|
7796
|
+
lines.push(`<sub>\u23F1\uFE0F \u7D4C\u904E\u6642\u9593: ${formatDuration3(totalElapsed)}</sub>`);
|
|
8008
7797
|
}
|
|
8009
7798
|
lines.push("");
|
|
8010
7799
|
return lines;
|
|
@@ -8024,7 +7813,7 @@ function generateProgressSteps(currentStep, completedSteps, failedStep, skippedS
|
|
|
8024
7813
|
const status = getStepStatus(step, currentStep, completedSteps, failedStep, skippedSteps);
|
|
8025
7814
|
const icon = getStepIcon(status);
|
|
8026
7815
|
const timing = stepTimings2?.[step];
|
|
8027
|
-
const timingStr = timing !== void 0 ? ` \`${
|
|
7816
|
+
const timingStr = timing !== void 0 ? ` \`${formatDuration3(timing)}\`` : "";
|
|
8028
7817
|
const stepDetail = getStepDetail(step, status, schemaDrift, layerResults);
|
|
8029
7818
|
const runningDetail = getRunningStepDetail(step, detail, productionPreview);
|
|
8030
7819
|
if (status === "running" && runningDetail) {
|
|
@@ -8125,6 +7914,11 @@ function generateTestResultsSection(layerResults) {
|
|
|
8125
7914
|
}
|
|
8126
7915
|
function generateProductionSchemaSection(productionPreview, phase, deployStatus, env) {
|
|
8127
7916
|
const lines = ["### \u{1F4CB} \u672C\u756A\u30B9\u30AD\u30FC\u30DE\u72B6\u6CC1", ""];
|
|
7917
|
+
const deferredMessage = getDeferredProductionPreviewMessage(phase, productionPreview);
|
|
7918
|
+
if (!deployStatus?.deployed && deferredMessage) {
|
|
7919
|
+
lines.push(deferredMessage, "");
|
|
7920
|
+
return lines;
|
|
7921
|
+
}
|
|
8128
7922
|
const state = resolveProductionSchemaState(productionPreview, deployStatus);
|
|
8129
7923
|
switch (state) {
|
|
8130
7924
|
case "deployed":
|
|
@@ -8149,6 +7943,17 @@ function generateProductionSchemaSection(productionPreview, phase, deployStatus,
|
|
|
8149
7943
|
}
|
|
8150
7944
|
return lines;
|
|
8151
7945
|
}
|
|
7946
|
+
function getDeferredProductionPreviewMessage(phase, productionPreview) {
|
|
7947
|
+
if (productionPreview?.executed) return null;
|
|
7948
|
+
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
|
+
case "test":
|
|
7952
|
+
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
|
+
default:
|
|
7954
|
+
return null;
|
|
7955
|
+
}
|
|
7956
|
+
}
|
|
8152
7957
|
function resolveProductionSchemaState(productionPreview, deployStatus) {
|
|
8153
7958
|
if (deployStatus?.deployed) return "deployed";
|
|
8154
7959
|
if (!productionPreview) return "checking";
|
|
@@ -8197,7 +8002,8 @@ function appendChangesPendingSection(lines, productionPreview, env) {
|
|
|
8197
8002
|
}
|
|
8198
8003
|
appendDeployLink(lines, env);
|
|
8199
8004
|
}
|
|
8200
|
-
function generateDbDeploySection(layerResults, gitBranchName, productionPreview, env) {
|
|
8005
|
+
function generateDbDeploySection(phase, layerResults, gitBranchName, productionPreview, env) {
|
|
8006
|
+
if (phase === "blocking" || phase === "test") return [];
|
|
8201
8007
|
if (!checkBlockingLayersPassed(layerResults)) return [];
|
|
8202
8008
|
if (productionPreview?.hasChanges) return [];
|
|
8203
8009
|
const deployWorkflowUrl = env.repository ? `${env.serverUrl}/${env.repository}/actions/workflows/deploy-db.yml` : null;
|
|
@@ -8260,7 +8066,13 @@ function generateProgressCommentBody(input) {
|
|
|
8260
8066
|
...generateErrorSection(error ?? void 0, failedStep),
|
|
8261
8067
|
...generateTestResultsSection(input.layerResults),
|
|
8262
8068
|
...generateProductionSchemaSection(productionPreview, input.phase, deployStatus, env),
|
|
8263
|
-
...generateDbDeploySection(
|
|
8069
|
+
...generateDbDeploySection(
|
|
8070
|
+
input.phase,
|
|
8071
|
+
input.layerResults,
|
|
8072
|
+
gitBranchName,
|
|
8073
|
+
productionPreview,
|
|
8074
|
+
env
|
|
8075
|
+
)
|
|
8264
8076
|
];
|
|
8265
8077
|
const jst = new Date(Date.now() + 9 * 60 * 60 * 1e3);
|
|
8266
8078
|
const now = jst.toISOString().replace("T", " ").substring(0, 19);
|
|
@@ -8269,13 +8081,25 @@ function generateProgressCommentBody(input) {
|
|
|
8269
8081
|
}
|
|
8270
8082
|
|
|
8271
8083
|
// src/commands/ci/machine/formatters/github-comment.ts
|
|
8084
|
+
function getSkippedStepsForPhase(phase) {
|
|
8085
|
+
switch (phase) {
|
|
8086
|
+
case "test":
|
|
8087
|
+
return ["syncSchema", "applySeeds", "observability"];
|
|
8088
|
+
case "blocking":
|
|
8089
|
+
return ["observability"];
|
|
8090
|
+
case "observability":
|
|
8091
|
+
return ["postSeedChecks", "staticChecks", "build", "runTests"];
|
|
8092
|
+
default:
|
|
8093
|
+
return [];
|
|
8094
|
+
}
|
|
8095
|
+
}
|
|
8272
8096
|
function createCommentInput(context, options) {
|
|
8273
8097
|
const skippedLayerNumbers = Object.keys(context.layerSkipReasons || {}).map(Number);
|
|
8274
8098
|
const originalSelectedLayers = [
|
|
8275
8099
|
.../* @__PURE__ */ new Set([...skippedLayerNumbers, ...context.selectedLayers])
|
|
8276
8100
|
].sort((a, b) => a - b);
|
|
8277
8101
|
return {
|
|
8278
|
-
phase: context.
|
|
8102
|
+
phase: context.phase,
|
|
8279
8103
|
exitCode: context.exitCode,
|
|
8280
8104
|
branchName: context.branchName,
|
|
8281
8105
|
prContext: context.prContext,
|
|
@@ -8292,8 +8116,8 @@ function createCommentInput(context, options) {
|
|
|
8292
8116
|
};
|
|
8293
8117
|
}
|
|
8294
8118
|
function createProgressCommentInput(context, currentStep, completedSteps, failedStep = null, stepTimings2) {
|
|
8295
|
-
const phase = context.
|
|
8296
|
-
const skippedSteps = phase
|
|
8119
|
+
const phase = context.phase;
|
|
8120
|
+
const skippedSteps = getSkippedStepsForPhase(phase);
|
|
8297
8121
|
return {
|
|
8298
8122
|
phase,
|
|
8299
8123
|
currentStep,
|
|
@@ -8728,6 +8552,8 @@ var ciMachine = setup({
|
|
|
8728
8552
|
shouldInstallPgTap: ({ context }) => shouldInstallPgTap(context),
|
|
8729
8553
|
shouldSetupRoles: ({ context }) => shouldSetupRoles(context),
|
|
8730
8554
|
shouldPostGitHubComment: ({ context }) => shouldPostGitHubComment(context),
|
|
8555
|
+
shouldRunPrExecutionPhase: ({ context }) => shouldRunPrExecutionPhase(context),
|
|
8556
|
+
shouldRunPrObservabilityPhase: ({ context }) => shouldRunPrObservabilityPhase(context),
|
|
8731
8557
|
isDryRun: ({ context }) => isDryRun(context),
|
|
8732
8558
|
hasError: ({ context }) => hasError(context),
|
|
8733
8559
|
allTestsPassed: ({ context }) => allTestsPassed(context)
|
|
@@ -9062,8 +8888,14 @@ var ciMachine = setup({
|
|
|
9062
8888
|
},
|
|
9063
8889
|
states: {
|
|
9064
8890
|
execution: {
|
|
9065
|
-
initial: "
|
|
8891
|
+
initial: "gate",
|
|
9066
8892
|
states: {
|
|
8893
|
+
gate: {
|
|
8894
|
+
always: [
|
|
8895
|
+
{ guard: "shouldRunPrExecutionPhase", target: "setupRoles" },
|
|
8896
|
+
{ target: "done" }
|
|
8897
|
+
]
|
|
8898
|
+
},
|
|
9067
8899
|
setupRoles: {
|
|
9068
8900
|
invoke: {
|
|
9069
8901
|
src: "setupRoles",
|
|
@@ -9357,8 +9189,14 @@ var ciMachine = setup({
|
|
|
9357
9189
|
}
|
|
9358
9190
|
},
|
|
9359
9191
|
observability: {
|
|
9360
|
-
initial: "
|
|
9192
|
+
initial: "gate",
|
|
9361
9193
|
states: {
|
|
9194
|
+
gate: {
|
|
9195
|
+
always: [
|
|
9196
|
+
{ guard: "shouldRunPrObservabilityPhase", target: "productionPreview" },
|
|
9197
|
+
{ target: "done" }
|
|
9198
|
+
]
|
|
9199
|
+
},
|
|
9362
9200
|
productionPreview: {
|
|
9363
9201
|
always: [
|
|
9364
9202
|
{
|
|
@@ -10190,9 +10028,15 @@ function createSyntheticSummary(stepId, status, params = {}) {
|
|
|
10190
10028
|
];
|
|
10191
10029
|
}
|
|
10192
10030
|
function getBuildSkipReason(context) {
|
|
10031
|
+
if (!shouldRunPrExecutionPhase(context)) {
|
|
10032
|
+
return getExecutionSkipReason(context);
|
|
10033
|
+
}
|
|
10193
10034
|
return isCiLocalMode(context) ? "Skipped in ci-local mode" : context.input.skipBuild === true ? "Skipped by --skip-build" : "Skipped by step guard";
|
|
10194
10035
|
}
|
|
10195
10036
|
function getPlaywrightSkipReason(context) {
|
|
10037
|
+
if (!shouldRunPrExecutionPhase(context)) {
|
|
10038
|
+
return getExecutionSkipReason(context);
|
|
10039
|
+
}
|
|
10196
10040
|
if (isCiLocalMode(context)) return "Skipped in ci-local mode";
|
|
10197
10041
|
if (!context.selectedLayers.includes(4)) return "Skipped because Layer 4 is not selected";
|
|
10198
10042
|
if (shouldReusePreparedPlaywright(context)) {
|
|
@@ -10203,6 +10047,21 @@ function getPlaywrightSkipReason(context) {
|
|
|
10203
10047
|
}
|
|
10204
10048
|
return "Skipped by step guard";
|
|
10205
10049
|
}
|
|
10050
|
+
function getObservabilitySkipReason(context) {
|
|
10051
|
+
if (isTestPhase(context)) {
|
|
10052
|
+
return "Skipped in phase=test; observability DB checks are disabled";
|
|
10053
|
+
}
|
|
10054
|
+
if (isBlockingPhase(context)) {
|
|
10055
|
+
return "Skipped in phase=blocking; observability runs outside the blocking path";
|
|
10056
|
+
}
|
|
10057
|
+
return "Skipped by phase selection";
|
|
10058
|
+
}
|
|
10059
|
+
function getExecutionSkipReason(context) {
|
|
10060
|
+
if (isObservabilityPhase(context)) {
|
|
10061
|
+
return "Skipped in phase=observability; execution work is disabled";
|
|
10062
|
+
}
|
|
10063
|
+
return "Skipped by phase selection";
|
|
10064
|
+
}
|
|
10206
10065
|
function getSyntheticBuildEntries(context, trackedStepIds, _stepStates, _nowMs) {
|
|
10207
10066
|
const entries = [];
|
|
10208
10067
|
const pushEntry = (entry) => {
|
|
@@ -10211,6 +10070,17 @@ function getSyntheticBuildEntries(context, trackedStepIds, _stepStates, _nowMs)
|
|
|
10211
10070
|
entries.push(entry);
|
|
10212
10071
|
};
|
|
10213
10072
|
const syntheticTimingParams = {};
|
|
10073
|
+
if (!shouldRunPrExecutionPhase(context)) {
|
|
10074
|
+
const reason = getExecutionSkipReason(context);
|
|
10075
|
+
pushEntry(createSkippedSummary("postSeedPr.execution.buildAndPlaywright.build", reason));
|
|
10076
|
+
pushEntry(
|
|
10077
|
+
createSkippedSummary("postSeedPr.execution.buildAndPlaywright.manifestGenerate", reason)
|
|
10078
|
+
);
|
|
10079
|
+
pushEntry(
|
|
10080
|
+
createSkippedSummary("postSeedPr.execution.buildAndPlaywright.playwrightInstall", reason)
|
|
10081
|
+
);
|
|
10082
|
+
return entries;
|
|
10083
|
+
}
|
|
10214
10084
|
if (shouldSkipBuild(context)) {
|
|
10215
10085
|
const reason = getBuildSkipReason(context);
|
|
10216
10086
|
pushEntry(createSkippedSummary("postSeedPr.execution.buildAndPlaywright.build", reason));
|
|
@@ -10333,38 +10203,46 @@ function getSkippedStepEntries(context, trackedStepIds) {
|
|
|
10333
10203
|
const entry = createSkippedSummary(stepId, reason);
|
|
10334
10204
|
if (entry) skipped.push(entry);
|
|
10335
10205
|
};
|
|
10206
|
+
if (isCiPrMode(context) && !shouldRunPrObservabilityPhase(context)) {
|
|
10207
|
+
const reason = getObservabilitySkipReason(context);
|
|
10208
|
+
pushSkipped("postSeedPr.observability.productionPreview", reason);
|
|
10209
|
+
pushSkipped("postSeedPr.observability.collectSchemaStats", reason);
|
|
10210
|
+
}
|
|
10336
10211
|
if (isTestPhase(context)) {
|
|
10337
10212
|
pushSkipped("syncSchema", "Skipped in phase=test; reusing prepared database");
|
|
10338
10213
|
pushSkipped("applySeeds", "Skipped in phase=test; reusing prepared database");
|
|
10339
|
-
pushSkipped(
|
|
10340
|
-
"postSeedPr.observability.productionPreview",
|
|
10341
|
-
"Skipped in phase=test; observability DB checks are disabled"
|
|
10342
|
-
);
|
|
10343
|
-
pushSkipped(
|
|
10344
|
-
"postSeedPr.observability.collectSchemaStats",
|
|
10345
|
-
"Skipped in phase=test; observability DB checks are disabled"
|
|
10346
|
-
);
|
|
10347
10214
|
}
|
|
10348
10215
|
if (!shouldPostGitHubComment(context) || context.prContext?.prNumber === null) {
|
|
10349
10216
|
const commentReason = context.input.skipGithubComment === true ? "Skipped by --skip-github-comment" : context.executionEnv !== "github-actions" ? "Skipped outside GitHub Actions" : "Skipped because PR context is unavailable";
|
|
10350
10217
|
pushSkipped("initialComment", commentReason);
|
|
10351
10218
|
pushSkipped("finalize.postComment", commentReason);
|
|
10352
10219
|
}
|
|
10353
|
-
if (
|
|
10354
|
-
const reason =
|
|
10220
|
+
if (isCiPrMode(context) && !shouldRunPrExecutionPhase(context)) {
|
|
10221
|
+
const reason = getExecutionSkipReason(context);
|
|
10222
|
+
pushSkipped("postSeedPr.execution.setupRoles", reason);
|
|
10355
10223
|
pushSkipped("postSeedPr.execution.staticChecks", reason);
|
|
10356
|
-
|
|
10357
|
-
if (shouldSkipBuild(context)) {
|
|
10358
|
-
pushSkipped("postSeedPr.execution.buildAndPlaywright", getBuildSkipReason(context));
|
|
10359
|
-
}
|
|
10360
|
-
if (shouldSkipAppStart(context)) {
|
|
10361
|
-
const reason = isCiLocalMode(context) ? "Skipped in ci-local mode" : !context.selectedLayers.includes(4) ? "Skipped because Layer 4 is not selected" : "Skipped by step guard";
|
|
10224
|
+
pushSkipped("postSeedPr.execution.buildAndPlaywright", reason);
|
|
10362
10225
|
pushSkipped("postSeedPr.execution.appStart", reason);
|
|
10363
|
-
|
|
10364
|
-
|
|
10365
|
-
pushSkipped("postSeedPr.execution.e2ePhase",
|
|
10366
|
-
} else
|
|
10367
|
-
|
|
10226
|
+
pushSkipped("postSeedPr.execution.capabilities", reason);
|
|
10227
|
+
pushSkipped("postSeedPr.execution.runCoreTests", reason);
|
|
10228
|
+
pushSkipped("postSeedPr.execution.e2ePhase", reason);
|
|
10229
|
+
} else {
|
|
10230
|
+
if (shouldSkipStaticChecks(context)) {
|
|
10231
|
+
const reason = isCiLocalMode(context) ? "Skipped in ci-local mode" : context.input.skipStaticChecks === true ? "Skipped by --skip-static-checks" : "Skipped by step guard";
|
|
10232
|
+
pushSkipped("postSeedPr.execution.staticChecks", reason);
|
|
10233
|
+
}
|
|
10234
|
+
if (shouldSkipBuild(context)) {
|
|
10235
|
+
pushSkipped("postSeedPr.execution.buildAndPlaywright", getBuildSkipReason(context));
|
|
10236
|
+
}
|
|
10237
|
+
if (shouldSkipAppStart(context)) {
|
|
10238
|
+
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("postSeedPr.execution.appStart", reason);
|
|
10240
|
+
}
|
|
10241
|
+
if (!context.selectedLayers.includes(4)) {
|
|
10242
|
+
pushSkipped("postSeedPr.execution.e2ePhase", "Skipped because Layer 4 is not selected");
|
|
10243
|
+
} else if (Object.values(context.layerResults).some((result) => result.status === "failed")) {
|
|
10244
|
+
pushSkipped("postSeedPr.execution.e2ePhase", "Skipped because blocking core tests failed");
|
|
10245
|
+
}
|
|
10368
10246
|
}
|
|
10369
10247
|
return skipped;
|
|
10370
10248
|
}
|
|
@@ -10460,7 +10338,7 @@ var progressHeartbeatTimer = null;
|
|
|
10460
10338
|
var latestSnapshot = null;
|
|
10461
10339
|
var heartbeatStep = null;
|
|
10462
10340
|
var heartbeatFailedStep = null;
|
|
10463
|
-
var
|
|
10341
|
+
var _progressUpdateSuccessCount = 0;
|
|
10464
10342
|
var stepStartTime = Date.now();
|
|
10465
10343
|
var currentTrackedStep = null;
|
|
10466
10344
|
var stepTimings = {};
|
|
@@ -10484,7 +10362,7 @@ function resetProgressTracking() {
|
|
|
10484
10362
|
latestSnapshot = null;
|
|
10485
10363
|
heartbeatStep = null;
|
|
10486
10364
|
heartbeatFailedStep = null;
|
|
10487
|
-
|
|
10365
|
+
_progressUpdateSuccessCount = 0;
|
|
10488
10366
|
previousActiveSteps = /* @__PURE__ */ new Set();
|
|
10489
10367
|
stepStartTime = Date.now();
|
|
10490
10368
|
currentTrackedStep = null;
|
|
@@ -10651,8 +10529,8 @@ ${generateProgressCommentBody(progressInput)}`;
|
|
|
10651
10529
|
marker: "<!-- runa-ci-report -->",
|
|
10652
10530
|
body
|
|
10653
10531
|
});
|
|
10654
|
-
|
|
10655
|
-
} catch (
|
|
10532
|
+
_progressUpdateSuccessCount++;
|
|
10533
|
+
} catch (_error) {
|
|
10656
10534
|
}
|
|
10657
10535
|
};
|
|
10658
10536
|
pendingProgressUpdate = doUpdate();
|
|
@@ -11006,7 +10884,7 @@ init_esm_shims();
|
|
|
11006
10884
|
init_esm_shims();
|
|
11007
10885
|
var CiModeSchema = z.enum(["ci-local", "ci-pr-local"]);
|
|
11008
10886
|
var CiExecutionEnvSchema = z.enum(["github-actions", "local"]);
|
|
11009
|
-
var CiPhaseSchema = z.enum(["all", "test"]);
|
|
10887
|
+
var CiPhaseSchema = z.enum(["all", "blocking", "observability", "test"]);
|
|
11010
10888
|
var CiDbModeSchema = z.enum(["auto", "local"]);
|
|
11011
10889
|
var RepoKindSchema = z.enum(["monorepo", "pj-repo", "unknown"]);
|
|
11012
10890
|
var StepStatusSchema = z.enum(["passed", "failed", "skipped", "killed", "timeout"]);
|
|
@@ -11235,7 +11113,6 @@ z.object({
|
|
|
11235
11113
|
});
|
|
11236
11114
|
z.object({
|
|
11237
11115
|
allowLocalFallback: z.boolean(),
|
|
11238
|
-
allowPartialPhases: z.boolean(),
|
|
11239
11116
|
source: z.enum(["config", "default"])
|
|
11240
11117
|
});
|
|
11241
11118
|
z.object({
|
|
@@ -11614,28 +11491,27 @@ async function runCiPrCommand(options) {
|
|
|
11614
11491
|
const skipStaticChecks = options.skipStaticChecks === true;
|
|
11615
11492
|
const skipBuild = options.skipBuild === true;
|
|
11616
11493
|
const phase = options.phase ?? "all";
|
|
11494
|
+
const blockingPhase = phase === "blocking";
|
|
11495
|
+
const observabilityPhase = phase === "observability";
|
|
11617
11496
|
const testOnlyPhase = phase === "test";
|
|
11497
|
+
const runExecutionPath = !observabilityPhase;
|
|
11618
11498
|
const preparedRuntime = options.skipLocalDbStart === true || options.assumeSupabaseReady === true;
|
|
11619
11499
|
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 (observability deferred)" : "db apply \u2192 db seed \u2192 (production preview \u2225 schema stats \u2225 db:setup-roles)";
|
|
11620
11501
|
const planSteps = [
|
|
11621
11502
|
{
|
|
11622
11503
|
id: "setup",
|
|
11623
11504
|
description: testOnlyPhase ? "Resolve repo/app context (optionally reuse pre-started local Supabase)" : preparedRuntime ? "Reuse workflow-prepared local Supabase runtime" : "Start local Supabase instance"
|
|
11624
11505
|
},
|
|
11625
|
-
|
|
11626
|
-
|
|
11627
|
-
|
|
11628
|
-
description: "db apply \u2192 db seed \u2192 (production preview \u2225 schema stats \u2225 db:setup-roles)"
|
|
11629
|
-
}
|
|
11630
|
-
],
|
|
11631
|
-
...skipStaticChecks ? [] : [{ id: "static", description: "pnpm type-check + pnpm lint (parallel)" }],
|
|
11632
|
-
...skipBuild ? [] : [
|
|
11506
|
+
{ id: "db", description: dbDescription },
|
|
11507
|
+
...!runExecutionPath || skipStaticChecks ? [] : [{ id: "static", description: "pnpm type-check + pnpm lint (parallel)" }],
|
|
11508
|
+
...!runExecutionPath || skipBuild ? [] : [
|
|
11633
11509
|
{
|
|
11634
11510
|
id: "build",
|
|
11635
11511
|
description: preparedPlaywright ? "pnpm build \u2192 manifest:generate (reuse preinstalled Playwright)" : "pnpm build \u2192 manifest:generate + playwright install"
|
|
11636
11512
|
}
|
|
11637
11513
|
],
|
|
11638
|
-
{ id: "test", description: "Start app \u2192 detect capabilities \u2192 run layers 1-4" },
|
|
11514
|
+
...!runExecutionPath ? [] : [{ id: "test", description: "Start app \u2192 detect capabilities \u2192 run layers 1-4" }],
|
|
11639
11515
|
{
|
|
11640
11516
|
id: "finalize",
|
|
11641
11517
|
description: "Write ci-summary.json + post GitHub comment"
|