@runa-ai/runa-cli 0.5.43 → 0.5.44
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/commands/ci/commands/ci-prod-apply.d.ts +2 -1
- package/dist/commands/ci/commands/ci-prod-apply.d.ts.map +1 -1
- package/dist/commands/ci/commands/ci-prod-types.d.ts +26 -0
- package/dist/commands/ci/commands/ci-prod-types.d.ts.map +1 -0
- package/dist/commands/ci/commands/ci-prod-workflow.d.ts +18 -0
- package/dist/commands/ci/commands/ci-prod-workflow.d.ts.map +1 -0
- package/dist/commands/db/constants.d.ts.map +1 -1
- package/dist/commands/test/commands/test-layer.d.ts.map +1 -1
- package/dist/constants/versions.d.ts +1 -1
- package/dist/index.js +561 -467
- package/package.json +2 -2
|
@@ -6,5 +6,6 @@
|
|
|
6
6
|
* - Safety sequence: stack → risks → snapshot → apply → audit → notify
|
|
7
7
|
*/
|
|
8
8
|
import { Command } from 'commander';
|
|
9
|
-
|
|
9
|
+
declare const ciProdApplyCommand: Command;
|
|
10
|
+
export { ciProdApplyCommand };
|
|
10
11
|
//# sourceMappingURL=ci-prod-apply.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ci-prod-apply.d.ts","sourceRoot":"","sources":["../../../../src/commands/ci/commands/ci-prod-apply.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAIH,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"ci-prod-apply.d.ts","sourceRoot":"","sources":["../../../../src/commands/ci/commands/ci-prod-apply.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAIH,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAsHpC,QAAA,MAAM,kBAAkB,SA0GpB,CAAC;AAEL,OAAO,EAAE,kBAAkB,EAAE,CAAC"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
/**
|
|
3
|
+
* Zod schema for ci prod-apply CLI options.
|
|
4
|
+
* Derived from commander option definitions in ci-prod-apply.ts.
|
|
5
|
+
*/
|
|
6
|
+
export declare const CiProdApplyActionOptionsSchema: z.ZodObject<{
|
|
7
|
+
mode: z.ZodOptional<z.ZodEnum<{
|
|
8
|
+
local: "local";
|
|
9
|
+
"github-actions": "github-actions";
|
|
10
|
+
}>>;
|
|
11
|
+
output: z.ZodOptional<z.ZodEnum<{
|
|
12
|
+
json: "json";
|
|
13
|
+
human: "human";
|
|
14
|
+
}>>;
|
|
15
|
+
config: z.ZodOptional<z.ZodString>;
|
|
16
|
+
autoApprove: z.ZodOptional<z.ZodBoolean>;
|
|
17
|
+
skipNotify: z.ZodOptional<z.ZodBoolean>;
|
|
18
|
+
skipGithubLabel: z.ZodOptional<z.ZodBoolean>;
|
|
19
|
+
skipRisks: z.ZodOptional<z.ZodBoolean>;
|
|
20
|
+
initialDeployment: z.ZodOptional<z.ZodBoolean>;
|
|
21
|
+
allowDataLoss: z.ZodOptional<z.ZodBoolean>;
|
|
22
|
+
confirmAuthzUpdate: z.ZodOptional<z.ZodBoolean>;
|
|
23
|
+
maxLockWaitMs: z.ZodOptional<z.ZodNumber>;
|
|
24
|
+
}, z.core.$strict>;
|
|
25
|
+
export type CiProdApplyActionOptions = z.infer<typeof CiProdApplyActionOptionsSchema>;
|
|
26
|
+
//# sourceMappingURL=ci-prod-types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ci-prod-types.d.ts","sourceRoot":"","sources":["../../../../src/commands/ci/commands/ci-prod-types.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB;;;GAGG;AACH,eAAO,MAAM,8BAA8B;;;;;;;;;;;;;;;;;;kBAchC,CAAC;AAEZ,MAAM,MAAM,wBAAwB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,8BAA8B,CAAC,CAAC"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { type createCLILogger } from '@runa-ai/runa';
|
|
2
|
+
import type { CiSummary } from '../utils/ci-summary.js';
|
|
3
|
+
import type { ProdApplyInputs } from './ci-prod-utils.js';
|
|
4
|
+
import type { CiProdApplyActionOptions } from './ci-prod-types.js';
|
|
5
|
+
export declare class CiProdApplyWorkflow {
|
|
6
|
+
private readonly steps;
|
|
7
|
+
private readonly ctx;
|
|
8
|
+
constructor(deps: {
|
|
9
|
+
options: CiProdApplyActionOptions;
|
|
10
|
+
logger: ReturnType<typeof createCLILogger>;
|
|
11
|
+
summary: CiSummary;
|
|
12
|
+
repoRoot: string;
|
|
13
|
+
tmpDir: string;
|
|
14
|
+
inputs: ProdApplyInputs;
|
|
15
|
+
});
|
|
16
|
+
run(): Promise<void>;
|
|
17
|
+
}
|
|
18
|
+
//# sourceMappingURL=ci-prod-workflow.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ci-prod-workflow.d.ts","sourceRoot":"","sources":["../../../../src/commands/ci/commands/ci-prod-workflow.ts"],"names":[],"mappings":"AAAA,OAAO,EAAY,KAAK,eAAe,EAAE,MAAM,eAAe,CAAC;AAE/D,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAC;AAaxD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;AAC1D,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,oBAAoB,CAAC;AAyBnE,qBAAa,mBAAmB;IAC9B,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAoB;IAC1C,OAAO,CAAC,QAAQ,CAAC,GAAG,CAA6B;gBAErC,IAAI,EAAE;QAChB,OAAO,EAAE,wBAAwB,CAAC;QAClC,MAAM,EAAE,UAAU,CAAC,OAAO,eAAe,CAAC,CAAC;QAC3C,OAAO,EAAE,SAAS,CAAC;QACnB,QAAQ,EAAE,MAAM,CAAC;QACjB,MAAM,EAAE,MAAM,CAAC;QACf,MAAM,EAAE,eAAe,CAAC;KACzB;IAkMK,GAAG,IAAI,OAAO,CAAC,IAAI,CAAC;CAoB3B"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../../../src/commands/db/constants.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAGH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAMjD,UAAU,aAAa;IACrB,GAAG,EAAE,MAAM,CAAC;IACZ,EAAE,EAAE,MAAM,CAAC;IACX,MAAM,EAAE,MAAM,CAAC;CAChB;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,mBAAmB,CAAC,WAAW,CAAC,EAAE,MAAM,GAAG,aAAa,CA6DvE;AAED;;;;;;GAMG;AACH,eAAO,MAAM,iBAAiB;;;
|
|
1
|
+
{"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../../../src/commands/db/constants.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAGH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAMjD,UAAU,aAAa;IACrB,GAAG,EAAE,MAAM,CAAC;IACZ,EAAE,EAAE,MAAM,CAAC;IACX,MAAM,EAAE,MAAM,CAAC;CAChB;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,mBAAmB,CAAC,WAAW,CAAC,EAAE,MAAM,GAAG,aAAa,CA6DvE;AAED;;;;;;GAMG;AACH,eAAO,MAAM,iBAAiB;;;uBASd,MAAM;;;;;;;;;;;;;;yBAuBF,MAAM;CA+ChB,CAAC;AAEX;;;;;GAKG;AACH,eAAO,MAAM,aAAa;;;;CAYhB,CAAC;AAEX;;GAEG;AACH,eAAO,MAAM,gBAAgB,EAAE,MAAM,CAAC,MAAM,EAAE,cAAc,CA0BlD,CAAC;AAEX;;GAEG;AACH,eAAO,MAAM,cAAc;4CACM,MAAM,gBAAgB,MAAM;;;;;;;CAQnD,CAAC;AAEX;;;;;;GAMG;AACH,wBAAgB,uBAAuB,CAAC,WAAW,CAAC,EAAE,MAAM,GAAG;IAC7D,MAAM,EAAE,MAAM,CAAC;IACf,GAAG,EAAE,MAAM,CAAC;IACZ,EAAE,EAAE,MAAM,CAAC;CACZ,CAOA"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"test-layer.d.ts","sourceRoot":"","sources":["../../../../src/commands/test/commands/test-layer.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"test-layer.d.ts","sourceRoot":"","sources":["../../../../src/commands/test/commands/test-layer.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAmNpC,eAAO,MAAM,iBAAiB,SAAwB,CAAC;AACvD,eAAO,MAAM,iBAAiB,SAAwB,CAAC;AACvD,eAAO,MAAM,iBAAiB,SAAwB,CAAC;AACvD,eAAO,MAAM,iBAAiB,SAAwB,CAAC;AACvD,eAAO,MAAM,iBAAiB,SAAwB,CAAC;AAmDvD,eAAO,MAAM,gBAAgB,SAI5B,CAAC"}
|
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
*
|
|
21
21
|
* Sync strategy: Keep this in sync with packages/runa-templates/package.json version.
|
|
22
22
|
*/
|
|
23
|
-
export declare const COMPATIBLE_TEMPLATES_VERSION = "0.5.
|
|
23
|
+
export declare const COMPATIBLE_TEMPLATES_VERSION = "0.5.44";
|
|
24
24
|
/**
|
|
25
25
|
* Templates package name on GitHub Packages.
|
|
26
26
|
* Published to npm.pkg.github.com (requires NODE_AUTH_TOKEN).
|
package/dist/index.js
CHANGED
|
@@ -6,7 +6,7 @@ import { fileURLToPath } from 'url';
|
|
|
6
6
|
import { execSync, spawnSync, execFileSync, exec, spawn } from 'child_process';
|
|
7
7
|
import * as fs5 from 'fs';
|
|
8
8
|
import fs5__default, { existsSync, readFileSync, readdirSync, mkdtempSync, writeFileSync, mkdirSync, copyFileSync, createWriteStream, statSync, rmSync, realpathSync, promises, lstatSync, accessSync, constants, chmodSync, unlinkSync } from 'fs';
|
|
9
|
-
import { createCLILogger, cacheClear, CacheClearOutputSchema, CLIError, cachePrune, CachePruneOutputSchema, cacheStats, CacheStatsOutputSchema, cacheList, CacheListOutputSchema, cacheInvalidate, CacheInvalidateOutputSchema, syncFromProduction, dbGenerateDiagram, DbDiagramGenerateOutputSchema, createDbSnapshot, syncDatabase, emitDbPushFailureCapsule, emitDbAnnotations, writeDbPushStepSummary, exportDbReportJson, DbSyncOutputSchema, databasePaths, detectRequiredServices, formatDetectionResults, dbStart, DbLifecycleStartOutputSchema, dbStop, DbLifecycleStopOutputSchema, dbReset, DbLifecycleResetOutputSchema, dbValidateSchemas, DbSchemaValidateOutputSchema, DbSchemaRisksOutputSchema, dbDetectSchemaRisks, dbApplySchemas, DbSchemaApplyOutputSchema, dbGenerateTypes, DbSchemaGenerateOutputSchema, extractSchemaFilter, dbSeedInit, DbSeedInitOutputSchema, dbSeedValidate, DbSeedValidateOutputSchema, dbSeedGenerate, DbSeedGenerateOutputSchema, dbVerifySeeds, DbSeedVerifyOutputSchema, DbSnapshotCreateOutputSchema, restoreDbSnapshot, DbSnapshotRestoreOutputSchema, listDbSnapshots, DbSnapshotListOutputSchema, dbGeneratePgTapTests, DbTestGenOutputSchema, dbUpdateGoldenRecord, DbTestUpdateGoldenOutputSchema, repairRunaConfig, detectExistingInitConfig, initProject, validateInitResult, linkCliGlobally, LinkCliOutputSchema, unlinkCliGlobally, UnlinkCliOutputSchema, checkRepoStatus, CheckRepoStatusOutputSchema, enableTelemetry, disableTelemetry, getTelemetryStatus, uploadTelemetry, TelemetryUploadOutputSchema, runTest, TestRunOutputSchema, runTestService, TestServiceOutputSchema, runTestIntegration, TestIntegrationOutputSchema, runTestStatic, TestStaticOutputSchema, generateOwaspTop10Tests, TestOwaspGenerateOutputSchema, updateGoldenRecord, generateE2ETests, generateSecurityTests, generateUnitTests, generateApiTests, generateComponentTests, generateE2EScaffold, validateConfig, ValidateConfigOutputSchema, deploySchemaToProduction, WorkflowNotifyOutputSchema, devopsSync, workflowSync, validateInfrastructure, emitWorkflowValidateFailureCapsule, emitWorkflowAnnotations, writeWorkflowValidateStepSummary, exportWorkflowReportJson, WorkflowValidateInfrastructureOutputSchema, createSuccessEnvelopeSchema, CLI_CONTRACT_VERSION, runChecks, RunCheckOutputSchema, formatDuration as formatDuration$1, GITHUB_API, loadRunaConfig, getClassificationForProfile, loadRunaConfigOrThrow, recordSchemaAudit, RecordSchemaAuditOutputSchema, createBackup, CreateBackupOutputSchema, listBackups, ListBackupsOutputSchema, getBackupMetadata, restoreBackup, RestoreBackupOutputSchema, deleteBackup, DeleteBackupOutputSchema, detectSchemaNames, SUPABASE_SYSTEM_SCHEMAS, dbSeedApply, writeDbSeedStepSummary, DbSeedApplyOutputSchema, emitDbSeedFailureCapsule, syncEnvironment, EnvSyncOutputSchema, detectDatabasePackage, findProjectRoot as findProjectRoot$1, TelemetryEnableOutputSchema, TelemetryDisableOutputSchema, TelemetryStatusOutputSchema, workflowNotify, DevOpsSyncOutputSchema, WorkflowSyncOutputSchema, formatCLIError,
|
|
9
|
+
import { createCLILogger, cacheClear, CacheClearOutputSchema, CLIError, cachePrune, CachePruneOutputSchema, cacheStats, CacheStatsOutputSchema, cacheList, CacheListOutputSchema, cacheInvalidate, CacheInvalidateOutputSchema, syncFromProduction, dbGenerateDiagram, DbDiagramGenerateOutputSchema, createDbSnapshot, syncDatabase, emitDbPushFailureCapsule, emitDbAnnotations, writeDbPushStepSummary, exportDbReportJson, DbSyncOutputSchema, databasePaths, detectRequiredServices, formatDetectionResults, dbStart, DbLifecycleStartOutputSchema, dbStop, DbLifecycleStopOutputSchema, dbReset, DbLifecycleResetOutputSchema, dbValidateSchemas, DbSchemaValidateOutputSchema, DbSchemaRisksOutputSchema, dbDetectSchemaRisks, dbApplySchemas, DbSchemaApplyOutputSchema, dbGenerateTypes, DbSchemaGenerateOutputSchema, extractSchemaFilter, dbSeedInit, DbSeedInitOutputSchema, dbSeedValidate, DbSeedValidateOutputSchema, dbSeedGenerate, DbSeedGenerateOutputSchema, dbVerifySeeds, DbSeedVerifyOutputSchema, DbSnapshotCreateOutputSchema, restoreDbSnapshot, DbSnapshotRestoreOutputSchema, listDbSnapshots, DbSnapshotListOutputSchema, dbGeneratePgTapTests, DbTestGenOutputSchema, dbUpdateGoldenRecord, DbTestUpdateGoldenOutputSchema, repairRunaConfig, detectExistingInitConfig, initProject, validateInitResult, linkCliGlobally, LinkCliOutputSchema, unlinkCliGlobally, UnlinkCliOutputSchema, checkRepoStatus, CheckRepoStatusOutputSchema, enableTelemetry, disableTelemetry, getTelemetryStatus, uploadTelemetry, TelemetryUploadOutputSchema, runTest, TestRunOutputSchema, runTestService, TestServiceOutputSchema, runTestIntegration, TestIntegrationOutputSchema, runTestStatic, TestStaticOutputSchema, generateOwaspTop10Tests, TestOwaspGenerateOutputSchema, updateGoldenRecord, generateE2ETests, generateSecurityTests, generateUnitTests, generateApiTests, generateComponentTests, generateE2EScaffold, validateConfig, ValidateConfigOutputSchema, deploySchemaToProduction, WorkflowNotifyOutputSchema, devopsSync, workflowSync, validateInfrastructure, emitWorkflowValidateFailureCapsule, emitWorkflowAnnotations, writeWorkflowValidateStepSummary, exportWorkflowReportJson, WorkflowValidateInfrastructureOutputSchema, createSuccessEnvelopeSchema, CLI_CONTRACT_VERSION, runChecks, RunCheckOutputSchema, formatDuration as formatDuration$1, GITHUB_API, loadRunaConfig, getClassificationForProfile, loadRunaConfigOrThrow, recordSchemaAudit, RecordSchemaAuditOutputSchema, createBackup, CreateBackupOutputSchema, listBackups, ListBackupsOutputSchema, getBackupMetadata, restoreBackup, RestoreBackupOutputSchema, deleteBackup, DeleteBackupOutputSchema, detectSchemaNames, SUPABASE_SYSTEM_SCHEMAS, dbSeedApply, writeDbSeedStepSummary, DbSeedApplyOutputSchema, emitDbSeedFailureCapsule, syncEnvironment, EnvSyncOutputSchema, detectDatabasePackage, findProjectRoot as findProjectRoot$1, TelemetryEnableOutputSchema, TelemetryDisableOutputSchema, TelemetryStatusOutputSchema, workflowNotify, DevOpsSyncOutputSchema, WorkflowSyncOutputSchema, formatCLIError, getStatusIcon as getStatusIcon$1, findWorkspaceRoot as findWorkspaceRoot$1, checkExtensionConfig, UpgradeTransaction, readRunaVersion, syncTemplates, SyncOutputSchema, DATABASE_PACKAGE_CANDIDATES, ErrorEnvelopeSchema, preCheckSync, findConflictFiles, TestUnitGenOutputSchema, TestE2EGenerateOutputSchema, TestSecurityGenOutputSchema, TestApiGenOutputSchema, TestComponentGenOutputSchema } from '@runa-ai/runa';
|
|
10
10
|
import { z } from 'zod';
|
|
11
11
|
import fs9, { mkdir, writeFile, appendFile, readFile, rm, stat, realpath, cp, readdir, lstat } from 'fs/promises';
|
|
12
12
|
import { promisify } from 'util';
|
|
@@ -147,6 +147,10 @@ var init_constants = __esm({
|
|
|
147
147
|
DATABASE_DEFAULTS = {
|
|
148
148
|
// Supabase Local defaults (for development)
|
|
149
149
|
// Note: PORT is detected from running Supabase instance
|
|
150
|
+
// AI HINT: Local Supabase Default Credentials (Non-Production)
|
|
151
|
+
// postgres:postgres is the standard default for `supabase start`.
|
|
152
|
+
// This is a documented public value, not a secret.
|
|
153
|
+
// Production uses environment variables via .env.production.
|
|
150
154
|
SUPABASE_LOCAL: {
|
|
151
155
|
HOST: "127.0.0.1",
|
|
152
156
|
get PORT() {
|
|
@@ -925,7 +929,7 @@ var CLI_VERSION, HAS_ADMIN_COMMAND;
|
|
|
925
929
|
var init_version = __esm({
|
|
926
930
|
"src/version.ts"() {
|
|
927
931
|
init_esm_shims();
|
|
928
|
-
CLI_VERSION = "0.5.
|
|
932
|
+
CLI_VERSION = "0.5.44";
|
|
929
933
|
HAS_ADMIN_COMMAND = false;
|
|
930
934
|
}
|
|
931
935
|
});
|
|
@@ -9194,158 +9198,176 @@ async function writeCiSummary(params) {
|
|
|
9194
9198
|
return filePath;
|
|
9195
9199
|
}
|
|
9196
9200
|
|
|
9197
|
-
// src/commands/ci/
|
|
9201
|
+
// src/commands/ci/commands/ci-prod-github.ts
|
|
9198
9202
|
init_esm_shims();
|
|
9199
|
-
|
|
9200
|
-
|
|
9201
|
-
|
|
9202
|
-
|
|
9203
|
+
|
|
9204
|
+
// src/commands/ci/utils/github-api.ts
|
|
9205
|
+
init_esm_shims();
|
|
9206
|
+
var RepoContextSchema = z.object({
|
|
9207
|
+
owner: z.string().min(1),
|
|
9208
|
+
repo: z.string().min(1)
|
|
9209
|
+
}).strict();
|
|
9210
|
+
function resolveGithubToken() {
|
|
9211
|
+
const token = process.env.GITHUB_TOKEN?.trim();
|
|
9212
|
+
if (token && token.length > 0) return token;
|
|
9213
|
+
throw new CLIError(
|
|
9214
|
+
"GITHUB_TOKEN is not set (required for GitHub API operations).",
|
|
9215
|
+
"GITHUB_TOKEN_MISSING",
|
|
9216
|
+
[
|
|
9217
|
+
"Ensure workflow permissions include issues: write / pull-requests: write",
|
|
9218
|
+
"Set GITHUB_TOKEN via env"
|
|
9219
|
+
],
|
|
9220
|
+
void 0,
|
|
9221
|
+
10
|
|
9222
|
+
);
|
|
9223
|
+
}
|
|
9224
|
+
function resolveRepoContextFromEnv() {
|
|
9225
|
+
const full = process.env.GITHUB_REPOSITORY?.trim() ?? "";
|
|
9226
|
+
const [owner, repo] = full.split("/");
|
|
9227
|
+
return RepoContextSchema.parse({ owner, repo });
|
|
9228
|
+
}
|
|
9229
|
+
var GITHUB_API_TIMEOUT_MS = 3e4;
|
|
9230
|
+
async function githubRequest(params) {
|
|
9231
|
+
const token = resolveGithubToken();
|
|
9232
|
+
const url = new URL(`${GITHUB_API.base}${params.path}`);
|
|
9233
|
+
for (const [k, v] of Object.entries(params.query ?? {})) {
|
|
9234
|
+
if (v === void 0) continue;
|
|
9235
|
+
url.searchParams.set(k, String(v));
|
|
9236
|
+
}
|
|
9237
|
+
const startTime = Date.now();
|
|
9238
|
+
console.log(`[DEBUG] GitHub API: ${params.method} ${params.path} (starting)`);
|
|
9239
|
+
const controller = new AbortController();
|
|
9240
|
+
const timeoutId = setTimeout(() => controller.abort(), GITHUB_API_TIMEOUT_MS);
|
|
9203
9241
|
try {
|
|
9204
|
-
const
|
|
9205
|
-
|
|
9206
|
-
|
|
9207
|
-
|
|
9208
|
-
|
|
9242
|
+
const res = await fetch(url.toString(), {
|
|
9243
|
+
method: params.method,
|
|
9244
|
+
headers: {
|
|
9245
|
+
Authorization: `Bearer ${token}`,
|
|
9246
|
+
Accept: "application/vnd.github+json",
|
|
9247
|
+
"X-GitHub-Api-Version": "2022-11-28",
|
|
9248
|
+
"Content-Type": "application/json"
|
|
9249
|
+
},
|
|
9250
|
+
body: params.body === void 0 ? void 0 : JSON.stringify(params.body),
|
|
9251
|
+
signal: controller.signal
|
|
9209
9252
|
});
|
|
9210
|
-
|
|
9211
|
-
|
|
9212
|
-
|
|
9213
|
-
|
|
9214
|
-
|
|
9215
|
-
|
|
9216
|
-
|
|
9217
|
-
|
|
9218
|
-
|
|
9219
|
-
|
|
9220
|
-
|
|
9221
|
-
|
|
9222
|
-
|
|
9223
|
-
|
|
9224
|
-
} catch {
|
|
9225
|
-
return void 0;
|
|
9253
|
+
clearTimeout(timeoutId);
|
|
9254
|
+
const duration = Date.now() - startTime;
|
|
9255
|
+
if (!res.ok) {
|
|
9256
|
+
const text = await res.text().catch(() => "");
|
|
9257
|
+
console.error(
|
|
9258
|
+
`[DEBUG] GitHub API: ${params.method} ${params.path} failed (${res.status}) after ${duration}ms`
|
|
9259
|
+
);
|
|
9260
|
+
throw new CLIError(
|
|
9261
|
+
`GitHub API request failed: ${params.method} ${params.path} (${res.status})`,
|
|
9262
|
+
"GITHUB_API_FAILED",
|
|
9263
|
+
["Check workflow permissions", "Check GITHUB_TOKEN availability"],
|
|
9264
|
+
new Error(text),
|
|
9265
|
+
20
|
|
9266
|
+
);
|
|
9226
9267
|
}
|
|
9268
|
+
console.log(
|
|
9269
|
+
`[DEBUG] GitHub API: ${params.method} ${params.path} succeeded (${res.status}) in ${duration}ms`
|
|
9270
|
+
);
|
|
9271
|
+
if (res.status === 204) return null;
|
|
9272
|
+
const json = await res.json().catch(() => null);
|
|
9273
|
+
return json;
|
|
9274
|
+
} catch (error) {
|
|
9275
|
+
clearTimeout(timeoutId);
|
|
9276
|
+
const duration = Date.now() - startTime;
|
|
9277
|
+
if (error instanceof Error && error.name === "AbortError") {
|
|
9278
|
+
console.error(
|
|
9279
|
+
`[DEBUG] GitHub API: ${params.method} ${params.path} timed out after ${duration}ms`
|
|
9280
|
+
);
|
|
9281
|
+
throw new CLIError(
|
|
9282
|
+
`GitHub API request timed out: ${params.method} ${params.path}`,
|
|
9283
|
+
"GITHUB_API_TIMEOUT",
|
|
9284
|
+
["Request exceeded 30 second timeout", "GitHub API may be slow or unreachable"],
|
|
9285
|
+
error,
|
|
9286
|
+
21
|
|
9287
|
+
);
|
|
9288
|
+
}
|
|
9289
|
+
console.error(
|
|
9290
|
+
`[DEBUG] GitHub API: ${params.method} ${params.path} error after ${duration}ms: ${error instanceof Error ? error.message : String(error)}`
|
|
9291
|
+
);
|
|
9292
|
+
throw error;
|
|
9227
9293
|
}
|
|
9228
9294
|
}
|
|
9229
|
-
|
|
9230
|
-
|
|
9231
|
-
|
|
9232
|
-
|
|
9233
|
-
|
|
9234
|
-
|
|
9235
|
-
|
|
9236
|
-
|
|
9237
|
-
|
|
9238
|
-
|
|
9239
|
-
|
|
9240
|
-
|
|
9241
|
-
|
|
9242
|
-
|
|
9243
|
-
|
|
9244
|
-
|
|
9245
|
-
|
|
9246
|
-
|
|
9295
|
+
z.object({
|
|
9296
|
+
filename: z.string().min(1)
|
|
9297
|
+
}).passthrough();
|
|
9298
|
+
var IssueCommentSchema = z.object({
|
|
9299
|
+
id: z.number().int(),
|
|
9300
|
+
body: z.string().nullable()
|
|
9301
|
+
}).passthrough();
|
|
9302
|
+
async function upsertIssueComment(params) {
|
|
9303
|
+
let page = 1;
|
|
9304
|
+
let existingId = null;
|
|
9305
|
+
while (true) {
|
|
9306
|
+
const raw = await githubRequest({
|
|
9307
|
+
method: "GET",
|
|
9308
|
+
path: `/repos/${params.repo.owner}/${params.repo.repo}/issues/${params.issueNumber}/comments`,
|
|
9309
|
+
query: { per_page: 100, page }
|
|
9310
|
+
});
|
|
9311
|
+
const arr = z.array(IssueCommentSchema).parse(raw);
|
|
9312
|
+
for (const c of arr) {
|
|
9313
|
+
const body = c.body ?? "";
|
|
9314
|
+
if (body.includes(params.marker)) {
|
|
9315
|
+
existingId = c.id;
|
|
9316
|
+
break;
|
|
9317
|
+
}
|
|
9318
|
+
}
|
|
9319
|
+
if (existingId) break;
|
|
9320
|
+
if (arr.length < 100) break;
|
|
9321
|
+
page += 1;
|
|
9322
|
+
if (page > 20) break;
|
|
9323
|
+
}
|
|
9324
|
+
if (existingId) {
|
|
9325
|
+
await githubRequest({
|
|
9326
|
+
method: "PATCH",
|
|
9327
|
+
path: `/repos/${params.repo.owner}/${params.repo.repo}/issues/comments/${existingId}`,
|
|
9328
|
+
body: { body: params.body }
|
|
9329
|
+
});
|
|
9330
|
+
return;
|
|
9331
|
+
}
|
|
9332
|
+
await githubRequest({
|
|
9333
|
+
method: "POST",
|
|
9334
|
+
path: `/repos/${params.repo.owner}/${params.repo.repo}/issues/${params.issueNumber}/comments`,
|
|
9335
|
+
body: { body: params.body }
|
|
9336
|
+
});
|
|
9247
9337
|
}
|
|
9248
|
-
async function
|
|
9249
|
-
|
|
9250
|
-
|
|
9251
|
-
|
|
9252
|
-
|
|
9253
|
-
v_count integer := 0;
|
|
9254
|
-
v_repaired integer := 0;
|
|
9255
|
-
v_pairs integer := 0;
|
|
9256
|
-
r record;
|
|
9257
|
-
BEGIN
|
|
9258
|
-
-- Generic repair: derive (lhs >= rhs) invariants from CHECK constraints.
|
|
9259
|
-
-- This avoids hardcoding schema/table/column names and works for pj-repos.
|
|
9260
|
-
FOR r IN
|
|
9261
|
-
WITH checks AS (
|
|
9262
|
-
SELECT
|
|
9263
|
-
n.nspname AS schema_name,
|
|
9264
|
-
c.relname AS table_name,
|
|
9265
|
-
pg_get_constraintdef(con.oid) AS constraint_def,
|
|
9266
|
-
(regexp_matches(
|
|
9267
|
-
pg_get_constraintdef(con.oid),
|
|
9268
|
-
'CHECK\\s*\\(\\s*\\(\\s*"?([a-zA-Z_][a-zA-Z0-9_]*)"?\\s*>=\\s*"?([a-zA-Z_][a-zA-Z0-9_]*)"?\\s*\\)\\s*\\)\\s*\\)'
|
|
9269
|
-
))[1] AS lhs_col,
|
|
9270
|
-
(regexp_matches(
|
|
9271
|
-
pg_get_constraintdef(con.oid),
|
|
9272
|
-
'CHECK\\s*\\(\\s*\\(\\s*"?([a-zA-Z_][a-zA-Z0-9_]*)"?\\s*>=\\s*"?([a-zA-Z_][a-zA-Z0-9_]*)"?\\s*\\)\\s*\\)\\s*\\)'
|
|
9273
|
-
))[2] AS rhs_col
|
|
9274
|
-
FROM pg_constraint con
|
|
9275
|
-
JOIN pg_class c ON c.oid = con.conrelid
|
|
9276
|
-
JOIN pg_namespace n ON n.oid = c.relnamespace
|
|
9277
|
-
WHERE con.contype = 'c'
|
|
9278
|
-
AND pg_get_constraintdef(con.oid) ~ '>='
|
|
9279
|
-
)
|
|
9280
|
-
SELECT
|
|
9281
|
-
schema_name,
|
|
9282
|
-
table_name,
|
|
9283
|
-
lhs_col,
|
|
9284
|
-
rhs_col
|
|
9285
|
-
FROM checks
|
|
9286
|
-
WHERE lhs_col IS NOT NULL
|
|
9287
|
-
AND rhs_col IS NOT NULL
|
|
9288
|
-
-- Only apply to temporal column pairs (avoid touching unrelated invariants).
|
|
9289
|
-
AND EXISTS (
|
|
9290
|
-
SELECT 1
|
|
9291
|
-
FROM pg_attribute a1
|
|
9292
|
-
JOIN pg_type t1 ON t1.oid = a1.atttypid
|
|
9293
|
-
WHERE a1.attrelid = to_regclass(format('%I.%I', schema_name, table_name))
|
|
9294
|
-
AND a1.attname = lhs_col
|
|
9295
|
-
AND t1.typname IN ('timestamptz', 'timestamp', 'date')
|
|
9296
|
-
)
|
|
9297
|
-
AND EXISTS (
|
|
9298
|
-
SELECT 1
|
|
9299
|
-
FROM pg_attribute a2
|
|
9300
|
-
JOIN pg_type t2 ON t2.oid = a2.atttypid
|
|
9301
|
-
WHERE a2.attrelid = to_regclass(format('%I.%I', schema_name, table_name))
|
|
9302
|
-
AND a2.attname = rhs_col
|
|
9303
|
-
AND t2.typname IN ('timestamptz', 'timestamp', 'date')
|
|
9304
|
-
)
|
|
9305
|
-
AND has_table_privilege(current_user, format('%I.%I', schema_name, table_name), 'UPDATE')
|
|
9306
|
-
LOOP
|
|
9307
|
-
v_pairs := v_pairs + 1;
|
|
9308
|
-
|
|
9309
|
-
-- Repair violations (best-effort, idempotent).
|
|
9310
|
-
EXECUTE format(
|
|
9311
|
-
'UPDATE %I.%I SET %I = %I WHERE %I < %I',
|
|
9312
|
-
r.schema_name,
|
|
9313
|
-
r.table_name,
|
|
9314
|
-
r.lhs_col,
|
|
9315
|
-
r.rhs_col,
|
|
9316
|
-
r.lhs_col,
|
|
9317
|
-
r.rhs_col
|
|
9318
|
-
);
|
|
9319
|
-
GET DIAGNOSTICS v_repaired = ROW_COUNT;
|
|
9320
|
-
|
|
9321
|
-
-- Verify remaining violations.
|
|
9322
|
-
EXECUTE format(
|
|
9323
|
-
'SELECT COUNT(*) FROM %I.%I WHERE %I < %I',
|
|
9324
|
-
r.schema_name,
|
|
9325
|
-
r.table_name,
|
|
9326
|
-
r.lhs_col,
|
|
9327
|
-
r.rhs_col
|
|
9328
|
-
) INTO v_count;
|
|
9329
|
-
v_total := v_total + COALESCE(v_count, 0);
|
|
9330
|
-
END LOOP;
|
|
9331
|
-
|
|
9332
|
-
RAISE NOTICE 'Repaired check-based invariants: pairs=% , remaining_violations=%', v_pairs, v_total;
|
|
9333
|
-
IF v_total <> 0 THEN
|
|
9334
|
-
RAISE EXCEPTION 'Timestamp invariants still violated after repair: % rows', v_total;
|
|
9335
|
-
END IF;
|
|
9336
|
-
END
|
|
9337
|
-
$$;
|
|
9338
|
-
`.trim();
|
|
9339
|
-
const psql = await buildPsqlParams(params.databaseUrl);
|
|
9340
|
-
await runLogged({
|
|
9341
|
-
cwd: params.repoRoot,
|
|
9342
|
-
env: psql.env,
|
|
9343
|
-
label: `repair timestamps (${params.labelPrefix})`,
|
|
9344
|
-
command: "psql",
|
|
9345
|
-
args: [...psql.args, "-v", "ON_ERROR_STOP=1", "-c", sql],
|
|
9346
|
-
logFile: path10__default.join(params.tmpDir, `timestamp-repair-${params.labelPrefix}.log`)
|
|
9338
|
+
async function addIssueLabels(params) {
|
|
9339
|
+
await githubRequest({
|
|
9340
|
+
method: "POST",
|
|
9341
|
+
path: `/repos/${params.repo.owner}/${params.repo.repo}/issues/${params.issueNumber}/labels`,
|
|
9342
|
+
body: { labels: params.labels }
|
|
9347
9343
|
});
|
|
9348
9344
|
}
|
|
9345
|
+
async function findPullRequestNumberForCommit(params) {
|
|
9346
|
+
const token = process.env.GITHUB_TOKEN;
|
|
9347
|
+
if (!token) return null;
|
|
9348
|
+
const res = await fetch(GITHUB_API.commitPulls(params.repo.owner, params.repo.repo, params.sha), {
|
|
9349
|
+
headers: {
|
|
9350
|
+
Accept: "application/vnd.github+json",
|
|
9351
|
+
Authorization: `Bearer ${token}`,
|
|
9352
|
+
"X-GitHub-Api-Version": "2022-11-28"
|
|
9353
|
+
}
|
|
9354
|
+
});
|
|
9355
|
+
if (!res.ok) return null;
|
|
9356
|
+
const json = await res.json();
|
|
9357
|
+
const arr = z.array(
|
|
9358
|
+
z.object({
|
|
9359
|
+
number: z.number().int().positive(),
|
|
9360
|
+
state: z.string().optional()
|
|
9361
|
+
}).passthrough()
|
|
9362
|
+
).parse(json);
|
|
9363
|
+
const open = arr.find((p) => p.state === "open");
|
|
9364
|
+
return (open ?? arr[0] ?? null)?.number ?? null;
|
|
9365
|
+
}
|
|
9366
|
+
function buildWorkflowRunUrl() {
|
|
9367
|
+
const repository = process.env.GITHUB_REPOSITORY || "";
|
|
9368
|
+
const runId = process.env.GITHUB_RUN_ID || "";
|
|
9369
|
+
return `${GITHUB_API.serverUrl}/${repository}/actions/runs/${runId}`;
|
|
9370
|
+
}
|
|
9349
9371
|
|
|
9350
9372
|
// src/commands/ci/commands/ci-prod-db-operations.ts
|
|
9351
9373
|
init_esm_shims();
|
|
@@ -9798,177 +9820,6 @@ async function notifyDeployment(repoRoot, tmpDir, params) {
|
|
|
9798
9820
|
});
|
|
9799
9821
|
}
|
|
9800
9822
|
|
|
9801
|
-
// src/commands/ci/commands/ci-prod-github.ts
|
|
9802
|
-
init_esm_shims();
|
|
9803
|
-
|
|
9804
|
-
// src/commands/ci/utils/github-api.ts
|
|
9805
|
-
init_esm_shims();
|
|
9806
|
-
var RepoContextSchema = z.object({
|
|
9807
|
-
owner: z.string().min(1),
|
|
9808
|
-
repo: z.string().min(1)
|
|
9809
|
-
}).strict();
|
|
9810
|
-
function resolveGithubToken() {
|
|
9811
|
-
const token = process.env.GITHUB_TOKEN?.trim();
|
|
9812
|
-
if (token && token.length > 0) return token;
|
|
9813
|
-
throw new CLIError(
|
|
9814
|
-
"GITHUB_TOKEN is not set (required for GitHub API operations).",
|
|
9815
|
-
"GITHUB_TOKEN_MISSING",
|
|
9816
|
-
[
|
|
9817
|
-
"Ensure workflow permissions include issues: write / pull-requests: write",
|
|
9818
|
-
"Set GITHUB_TOKEN via env"
|
|
9819
|
-
],
|
|
9820
|
-
void 0,
|
|
9821
|
-
10
|
|
9822
|
-
);
|
|
9823
|
-
}
|
|
9824
|
-
function resolveRepoContextFromEnv() {
|
|
9825
|
-
const full = process.env.GITHUB_REPOSITORY?.trim() ?? "";
|
|
9826
|
-
const [owner, repo] = full.split("/");
|
|
9827
|
-
return RepoContextSchema.parse({ owner, repo });
|
|
9828
|
-
}
|
|
9829
|
-
var GITHUB_API_TIMEOUT_MS = 3e4;
|
|
9830
|
-
async function githubRequest(params) {
|
|
9831
|
-
const token = resolveGithubToken();
|
|
9832
|
-
const url = new URL(`${GITHUB_API.base}${params.path}`);
|
|
9833
|
-
for (const [k, v] of Object.entries(params.query ?? {})) {
|
|
9834
|
-
if (v === void 0) continue;
|
|
9835
|
-
url.searchParams.set(k, String(v));
|
|
9836
|
-
}
|
|
9837
|
-
const startTime = Date.now();
|
|
9838
|
-
console.log(`[DEBUG] GitHub API: ${params.method} ${params.path} (starting)`);
|
|
9839
|
-
const controller = new AbortController();
|
|
9840
|
-
const timeoutId = setTimeout(() => controller.abort(), GITHUB_API_TIMEOUT_MS);
|
|
9841
|
-
try {
|
|
9842
|
-
const res = await fetch(url.toString(), {
|
|
9843
|
-
method: params.method,
|
|
9844
|
-
headers: {
|
|
9845
|
-
Authorization: `Bearer ${token}`,
|
|
9846
|
-
Accept: "application/vnd.github+json",
|
|
9847
|
-
"X-GitHub-Api-Version": "2022-11-28",
|
|
9848
|
-
"Content-Type": "application/json"
|
|
9849
|
-
},
|
|
9850
|
-
body: params.body === void 0 ? void 0 : JSON.stringify(params.body),
|
|
9851
|
-
signal: controller.signal
|
|
9852
|
-
});
|
|
9853
|
-
clearTimeout(timeoutId);
|
|
9854
|
-
const duration = Date.now() - startTime;
|
|
9855
|
-
if (!res.ok) {
|
|
9856
|
-
const text = await res.text().catch(() => "");
|
|
9857
|
-
console.error(
|
|
9858
|
-
`[DEBUG] GitHub API: ${params.method} ${params.path} failed (${res.status}) after ${duration}ms`
|
|
9859
|
-
);
|
|
9860
|
-
throw new CLIError(
|
|
9861
|
-
`GitHub API request failed: ${params.method} ${params.path} (${res.status})`,
|
|
9862
|
-
"GITHUB_API_FAILED",
|
|
9863
|
-
["Check workflow permissions", "Check GITHUB_TOKEN availability"],
|
|
9864
|
-
new Error(text),
|
|
9865
|
-
20
|
|
9866
|
-
);
|
|
9867
|
-
}
|
|
9868
|
-
console.log(
|
|
9869
|
-
`[DEBUG] GitHub API: ${params.method} ${params.path} succeeded (${res.status}) in ${duration}ms`
|
|
9870
|
-
);
|
|
9871
|
-
if (res.status === 204) return null;
|
|
9872
|
-
const json = await res.json().catch(() => null);
|
|
9873
|
-
return json;
|
|
9874
|
-
} catch (error) {
|
|
9875
|
-
clearTimeout(timeoutId);
|
|
9876
|
-
const duration = Date.now() - startTime;
|
|
9877
|
-
if (error instanceof Error && error.name === "AbortError") {
|
|
9878
|
-
console.error(
|
|
9879
|
-
`[DEBUG] GitHub API: ${params.method} ${params.path} timed out after ${duration}ms`
|
|
9880
|
-
);
|
|
9881
|
-
throw new CLIError(
|
|
9882
|
-
`GitHub API request timed out: ${params.method} ${params.path}`,
|
|
9883
|
-
"GITHUB_API_TIMEOUT",
|
|
9884
|
-
["Request exceeded 30 second timeout", "GitHub API may be slow or unreachable"],
|
|
9885
|
-
error,
|
|
9886
|
-
21
|
|
9887
|
-
);
|
|
9888
|
-
}
|
|
9889
|
-
console.error(
|
|
9890
|
-
`[DEBUG] GitHub API: ${params.method} ${params.path} error after ${duration}ms: ${error instanceof Error ? error.message : String(error)}`
|
|
9891
|
-
);
|
|
9892
|
-
throw error;
|
|
9893
|
-
}
|
|
9894
|
-
}
|
|
9895
|
-
z.object({
|
|
9896
|
-
filename: z.string().min(1)
|
|
9897
|
-
}).passthrough();
|
|
9898
|
-
var IssueCommentSchema = z.object({
|
|
9899
|
-
id: z.number().int(),
|
|
9900
|
-
body: z.string().nullable()
|
|
9901
|
-
}).passthrough();
|
|
9902
|
-
async function upsertIssueComment(params) {
|
|
9903
|
-
let page = 1;
|
|
9904
|
-
let existingId = null;
|
|
9905
|
-
while (true) {
|
|
9906
|
-
const raw = await githubRequest({
|
|
9907
|
-
method: "GET",
|
|
9908
|
-
path: `/repos/${params.repo.owner}/${params.repo.repo}/issues/${params.issueNumber}/comments`,
|
|
9909
|
-
query: { per_page: 100, page }
|
|
9910
|
-
});
|
|
9911
|
-
const arr = z.array(IssueCommentSchema).parse(raw);
|
|
9912
|
-
for (const c of arr) {
|
|
9913
|
-
const body = c.body ?? "";
|
|
9914
|
-
if (body.includes(params.marker)) {
|
|
9915
|
-
existingId = c.id;
|
|
9916
|
-
break;
|
|
9917
|
-
}
|
|
9918
|
-
}
|
|
9919
|
-
if (existingId) break;
|
|
9920
|
-
if (arr.length < 100) break;
|
|
9921
|
-
page += 1;
|
|
9922
|
-
if (page > 20) break;
|
|
9923
|
-
}
|
|
9924
|
-
if (existingId) {
|
|
9925
|
-
await githubRequest({
|
|
9926
|
-
method: "PATCH",
|
|
9927
|
-
path: `/repos/${params.repo.owner}/${params.repo.repo}/issues/comments/${existingId}`,
|
|
9928
|
-
body: { body: params.body }
|
|
9929
|
-
});
|
|
9930
|
-
return;
|
|
9931
|
-
}
|
|
9932
|
-
await githubRequest({
|
|
9933
|
-
method: "POST",
|
|
9934
|
-
path: `/repos/${params.repo.owner}/${params.repo.repo}/issues/${params.issueNumber}/comments`,
|
|
9935
|
-
body: { body: params.body }
|
|
9936
|
-
});
|
|
9937
|
-
}
|
|
9938
|
-
async function addIssueLabels(params) {
|
|
9939
|
-
await githubRequest({
|
|
9940
|
-
method: "POST",
|
|
9941
|
-
path: `/repos/${params.repo.owner}/${params.repo.repo}/issues/${params.issueNumber}/labels`,
|
|
9942
|
-
body: { labels: params.labels }
|
|
9943
|
-
});
|
|
9944
|
-
}
|
|
9945
|
-
async function findPullRequestNumberForCommit(params) {
|
|
9946
|
-
const token = process.env.GITHUB_TOKEN;
|
|
9947
|
-
if (!token) return null;
|
|
9948
|
-
const res = await fetch(GITHUB_API.commitPulls(params.repo.owner, params.repo.repo, params.sha), {
|
|
9949
|
-
headers: {
|
|
9950
|
-
Accept: "application/vnd.github+json",
|
|
9951
|
-
Authorization: `Bearer ${token}`,
|
|
9952
|
-
"X-GitHub-Api-Version": "2022-11-28"
|
|
9953
|
-
}
|
|
9954
|
-
});
|
|
9955
|
-
if (!res.ok) return null;
|
|
9956
|
-
const json = await res.json();
|
|
9957
|
-
const arr = z.array(
|
|
9958
|
-
z.object({
|
|
9959
|
-
number: z.number().int().positive(),
|
|
9960
|
-
state: z.string().optional()
|
|
9961
|
-
}).passthrough()
|
|
9962
|
-
).parse(json);
|
|
9963
|
-
const open = arr.find((p) => p.state === "open");
|
|
9964
|
-
return (open ?? arr[0] ?? null)?.number ?? null;
|
|
9965
|
-
}
|
|
9966
|
-
function buildWorkflowRunUrl() {
|
|
9967
|
-
const repository = process.env.GITHUB_REPOSITORY || "";
|
|
9968
|
-
const runId = process.env.GITHUB_RUN_ID || "";
|
|
9969
|
-
return `${GITHUB_API.serverUrl}/${repository}/actions/runs/${runId}`;
|
|
9970
|
-
}
|
|
9971
|
-
|
|
9972
9823
|
// src/commands/ci/commands/ci-prod-github.ts
|
|
9973
9824
|
async function maybeNotifyExternal(params) {
|
|
9974
9825
|
if (params.skipNotify) return;
|
|
@@ -10175,6 +10026,362 @@ function buildCiProdApplyStepSummaryMarkdown(params) {
|
|
|
10175
10026
|
return lines.join("\n");
|
|
10176
10027
|
}
|
|
10177
10028
|
|
|
10029
|
+
// src/commands/ci/commands/ci-prod-workflow.ts
|
|
10030
|
+
init_esm_shims();
|
|
10031
|
+
|
|
10032
|
+
// src/commands/ci/utils/timestamp-invariants.ts
|
|
10033
|
+
init_esm_shims();
|
|
10034
|
+
async function resolveHostaddrV4(host) {
|
|
10035
|
+
const ipVersion = isIP(host);
|
|
10036
|
+
if (ipVersion === 4) return host;
|
|
10037
|
+
if (ipVersion === 6) return void 0;
|
|
10038
|
+
try {
|
|
10039
|
+
const out = execFileSync("getent", ["ahostsv4", host], {
|
|
10040
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
10041
|
+
shell: false,
|
|
10042
|
+
encoding: "utf-8",
|
|
10043
|
+
timeout: 5e3
|
|
10044
|
+
});
|
|
10045
|
+
const firstLine = out.split("\n").find((l) => l.trim().length > 0);
|
|
10046
|
+
if (!firstLine) return void 0;
|
|
10047
|
+
const ip = firstLine.trim().split(/\s+/u)[0] ?? "";
|
|
10048
|
+
return isIP(ip) === 4 ? ip : void 0;
|
|
10049
|
+
} catch {
|
|
10050
|
+
try {
|
|
10051
|
+
const ips = await Promise.race([
|
|
10052
|
+
resolve4(host),
|
|
10053
|
+
new Promise(
|
|
10054
|
+
(_, reject) => setTimeout(() => reject(new Error("resolve4 timeout")), 2e3)
|
|
10055
|
+
)
|
|
10056
|
+
]);
|
|
10057
|
+
const ip = ips[0];
|
|
10058
|
+
return ip && isIP(ip) === 4 ? ip : void 0;
|
|
10059
|
+
} catch {
|
|
10060
|
+
return void 0;
|
|
10061
|
+
}
|
|
10062
|
+
}
|
|
10063
|
+
}
|
|
10064
|
+
async function buildPsqlParams(databaseUrl) {
|
|
10065
|
+
const url = new URL(databaseUrl);
|
|
10066
|
+
const username = decodeURIComponent(url.username);
|
|
10067
|
+
const password = decodeURIComponent(url.password);
|
|
10068
|
+
const host = url.hostname;
|
|
10069
|
+
const port = url.port || "5432";
|
|
10070
|
+
const database = url.pathname.replace(/^\//u, "");
|
|
10071
|
+
const hostaddrV4 = process.env.CI === "true" ? await resolveHostaddrV4(host) : void 0;
|
|
10072
|
+
const sslmode = url.searchParams.get("sslmode") ?? void 0;
|
|
10073
|
+
const env2 = {
|
|
10074
|
+
...process.env,
|
|
10075
|
+
PGPASSWORD: password,
|
|
10076
|
+
...sslmode ? { PGSSLMODE: sslmode } : {},
|
|
10077
|
+
...hostaddrV4 ? { PGHOSTADDR: hostaddrV4 } : {}
|
|
10078
|
+
};
|
|
10079
|
+
const hostForPsql = hostaddrV4 ?? host;
|
|
10080
|
+
const args = ["-h", hostForPsql, "-p", port, "-U", username, "-d", database];
|
|
10081
|
+
return { args, env: env2 };
|
|
10082
|
+
}
|
|
10083
|
+
async function repairTimestampInvariants(params) {
|
|
10084
|
+
const sql = `
|
|
10085
|
+
DO $$
|
|
10086
|
+
DECLARE
|
|
10087
|
+
v_total integer := 0;
|
|
10088
|
+
v_count integer := 0;
|
|
10089
|
+
v_repaired integer := 0;
|
|
10090
|
+
v_pairs integer := 0;
|
|
10091
|
+
r record;
|
|
10092
|
+
BEGIN
|
|
10093
|
+
-- Generic repair: derive (lhs >= rhs) invariants from CHECK constraints.
|
|
10094
|
+
-- This avoids hardcoding schema/table/column names and works for pj-repos.
|
|
10095
|
+
FOR r IN
|
|
10096
|
+
WITH checks AS (
|
|
10097
|
+
SELECT
|
|
10098
|
+
n.nspname AS schema_name,
|
|
10099
|
+
c.relname AS table_name,
|
|
10100
|
+
pg_get_constraintdef(con.oid) AS constraint_def,
|
|
10101
|
+
(regexp_matches(
|
|
10102
|
+
pg_get_constraintdef(con.oid),
|
|
10103
|
+
'CHECK\\s*\\(\\s*\\(\\s*"?([a-zA-Z_][a-zA-Z0-9_]*)"?\\s*>=\\s*"?([a-zA-Z_][a-zA-Z0-9_]*)"?\\s*\\)\\s*\\)\\s*\\)'
|
|
10104
|
+
))[1] AS lhs_col,
|
|
10105
|
+
(regexp_matches(
|
|
10106
|
+
pg_get_constraintdef(con.oid),
|
|
10107
|
+
'CHECK\\s*\\(\\s*\\(\\s*"?([a-zA-Z_][a-zA-Z0-9_]*)"?\\s*>=\\s*"?([a-zA-Z_][a-zA-Z0-9_]*)"?\\s*\\)\\s*\\)\\s*\\)'
|
|
10108
|
+
))[2] AS rhs_col
|
|
10109
|
+
FROM pg_constraint con
|
|
10110
|
+
JOIN pg_class c ON c.oid = con.conrelid
|
|
10111
|
+
JOIN pg_namespace n ON n.oid = c.relnamespace
|
|
10112
|
+
WHERE con.contype = 'c'
|
|
10113
|
+
AND pg_get_constraintdef(con.oid) ~ '>='
|
|
10114
|
+
)
|
|
10115
|
+
SELECT
|
|
10116
|
+
schema_name,
|
|
10117
|
+
table_name,
|
|
10118
|
+
lhs_col,
|
|
10119
|
+
rhs_col
|
|
10120
|
+
FROM checks
|
|
10121
|
+
WHERE lhs_col IS NOT NULL
|
|
10122
|
+
AND rhs_col IS NOT NULL
|
|
10123
|
+
-- Only apply to temporal column pairs (avoid touching unrelated invariants).
|
|
10124
|
+
AND EXISTS (
|
|
10125
|
+
SELECT 1
|
|
10126
|
+
FROM pg_attribute a1
|
|
10127
|
+
JOIN pg_type t1 ON t1.oid = a1.atttypid
|
|
10128
|
+
WHERE a1.attrelid = to_regclass(format('%I.%I', schema_name, table_name))
|
|
10129
|
+
AND a1.attname = lhs_col
|
|
10130
|
+
AND t1.typname IN ('timestamptz', 'timestamp', 'date')
|
|
10131
|
+
)
|
|
10132
|
+
AND EXISTS (
|
|
10133
|
+
SELECT 1
|
|
10134
|
+
FROM pg_attribute a2
|
|
10135
|
+
JOIN pg_type t2 ON t2.oid = a2.atttypid
|
|
10136
|
+
WHERE a2.attrelid = to_regclass(format('%I.%I', schema_name, table_name))
|
|
10137
|
+
AND a2.attname = rhs_col
|
|
10138
|
+
AND t2.typname IN ('timestamptz', 'timestamp', 'date')
|
|
10139
|
+
)
|
|
10140
|
+
AND has_table_privilege(current_user, format('%I.%I', schema_name, table_name), 'UPDATE')
|
|
10141
|
+
LOOP
|
|
10142
|
+
v_pairs := v_pairs + 1;
|
|
10143
|
+
|
|
10144
|
+
-- Repair violations (best-effort, idempotent).
|
|
10145
|
+
EXECUTE format(
|
|
10146
|
+
'UPDATE %I.%I SET %I = %I WHERE %I < %I',
|
|
10147
|
+
r.schema_name,
|
|
10148
|
+
r.table_name,
|
|
10149
|
+
r.lhs_col,
|
|
10150
|
+
r.rhs_col,
|
|
10151
|
+
r.lhs_col,
|
|
10152
|
+
r.rhs_col
|
|
10153
|
+
);
|
|
10154
|
+
GET DIAGNOSTICS v_repaired = ROW_COUNT;
|
|
10155
|
+
|
|
10156
|
+
-- Verify remaining violations.
|
|
10157
|
+
EXECUTE format(
|
|
10158
|
+
'SELECT COUNT(*) FROM %I.%I WHERE %I < %I',
|
|
10159
|
+
r.schema_name,
|
|
10160
|
+
r.table_name,
|
|
10161
|
+
r.lhs_col,
|
|
10162
|
+
r.rhs_col
|
|
10163
|
+
) INTO v_count;
|
|
10164
|
+
v_total := v_total + COALESCE(v_count, 0);
|
|
10165
|
+
END LOOP;
|
|
10166
|
+
|
|
10167
|
+
RAISE NOTICE 'Repaired check-based invariants: pairs=% , remaining_violations=%', v_pairs, v_total;
|
|
10168
|
+
IF v_total <> 0 THEN
|
|
10169
|
+
RAISE EXCEPTION 'Timestamp invariants still violated after repair: % rows', v_total;
|
|
10170
|
+
END IF;
|
|
10171
|
+
END
|
|
10172
|
+
$$;
|
|
10173
|
+
`.trim();
|
|
10174
|
+
const psql = await buildPsqlParams(params.databaseUrl);
|
|
10175
|
+
await runLogged({
|
|
10176
|
+
cwd: params.repoRoot,
|
|
10177
|
+
env: psql.env,
|
|
10178
|
+
label: `repair timestamps (${params.labelPrefix})`,
|
|
10179
|
+
command: "psql",
|
|
10180
|
+
args: [...psql.args, "-v", "ON_ERROR_STOP=1", "-c", sql],
|
|
10181
|
+
logFile: path10__default.join(params.tmpDir, `timestamp-repair-${params.labelPrefix}.log`)
|
|
10182
|
+
});
|
|
10183
|
+
}
|
|
10184
|
+
|
|
10185
|
+
// src/commands/ci/commands/ci-prod-workflow.ts
|
|
10186
|
+
var CiProdApplyWorkflow = class {
|
|
10187
|
+
steps;
|
|
10188
|
+
ctx;
|
|
10189
|
+
constructor(deps) {
|
|
10190
|
+
this.ctx = {
|
|
10191
|
+
options: deps.options,
|
|
10192
|
+
logger: deps.logger,
|
|
10193
|
+
summary: deps.summary,
|
|
10194
|
+
repoRoot: deps.repoRoot,
|
|
10195
|
+
tmpDir: deps.tmpDir,
|
|
10196
|
+
inputs: deps.inputs
|
|
10197
|
+
};
|
|
10198
|
+
this.steps = [
|
|
10199
|
+
{
|
|
10200
|
+
id: "stack",
|
|
10201
|
+
description: "Validate stack and inputs",
|
|
10202
|
+
run: async (ctx) => {
|
|
10203
|
+
const stack = await detectStack(
|
|
10204
|
+
ctx.repoRoot,
|
|
10205
|
+
ctx.tmpDir,
|
|
10206
|
+
ctx.inputs.productionDatabaseUrlAdmin
|
|
10207
|
+
);
|
|
10208
|
+
ctx.summary.detected.stack = stack;
|
|
10209
|
+
const supportedStacks = ["drizzle", "supabase"];
|
|
10210
|
+
if (!stack || !supportedStacks.includes(stack)) {
|
|
10211
|
+
throw new CLIError(
|
|
10212
|
+
`Production apply requires a supported database stack (detected: ${stack || "<empty>"}).`,
|
|
10213
|
+
"CI_PROD_STACK_UNSUPPORTED",
|
|
10214
|
+
[
|
|
10215
|
+
"Supported stacks: drizzle, supabase",
|
|
10216
|
+
"Run: runa init (to set up database structure)",
|
|
10217
|
+
"See: packages/database or supabase/schemas/"
|
|
10218
|
+
],
|
|
10219
|
+
void 0,
|
|
10220
|
+
11
|
|
10221
|
+
);
|
|
10222
|
+
}
|
|
10223
|
+
}
|
|
10224
|
+
},
|
|
10225
|
+
{
|
|
10226
|
+
id: "schema-diff",
|
|
10227
|
+
description: "Show schema diff",
|
|
10228
|
+
run: async (ctx) => {
|
|
10229
|
+
await showSchemaDiff(ctx.repoRoot, ctx.tmpDir);
|
|
10230
|
+
}
|
|
10231
|
+
},
|
|
10232
|
+
{
|
|
10233
|
+
id: "initial-detect",
|
|
10234
|
+
description: "Detect initial deployment",
|
|
10235
|
+
run: async (ctx) => {
|
|
10236
|
+
const result = checkIfInitialDeployment(
|
|
10237
|
+
ctx.repoRoot,
|
|
10238
|
+
ctx.inputs.productionDatabaseUrlAdmin
|
|
10239
|
+
);
|
|
10240
|
+
ctx.initialDeploymentCheck = result;
|
|
10241
|
+
if (result.checkSuccessful) {
|
|
10242
|
+
if (result.isInitialDeployment) {
|
|
10243
|
+
ctx.logger.info(`Initial deployment detected: ${result.reason}`);
|
|
10244
|
+
} else {
|
|
10245
|
+
ctx.logger.info(`Existing deployment: ${result.tableCount} table(s) found`);
|
|
10246
|
+
}
|
|
10247
|
+
} else {
|
|
10248
|
+
ctx.logger.warn(
|
|
10249
|
+
`Initial deployment check failed: ${result.reason}. Proceeding with risk analysis.`
|
|
10250
|
+
);
|
|
10251
|
+
}
|
|
10252
|
+
const skipViaFlag = ctx.options.skipRisks === true ? "--skip-risks flag" : void 0;
|
|
10253
|
+
const skipViaInitialFlag = ctx.options.initialDeployment === true ? "--initial-deployment flag" : void 0;
|
|
10254
|
+
const skipViaEnv = process.env.RUNA_SKIP_SCHEMA_RISK === "1" || process.env.RUNA_SKIP_SCHEMA_RISK === "true" ? "RUNA_SKIP_SCHEMA_RISK env" : void 0;
|
|
10255
|
+
const skipViaAutoDetect = result.checkSuccessful && result.isInitialDeployment ? `auto-detected initial deployment (${result.reason})` : void 0;
|
|
10256
|
+
ctx.skipRisksReason = skipViaFlag ?? skipViaInitialFlag ?? skipViaEnv ?? skipViaAutoDetect ?? void 0;
|
|
10257
|
+
if (ctx.skipRisksReason) {
|
|
10258
|
+
ctx.logger.info(`Risk detection skipped: ${ctx.skipRisksReason}`);
|
|
10259
|
+
}
|
|
10260
|
+
}
|
|
10261
|
+
},
|
|
10262
|
+
{
|
|
10263
|
+
id: "detect-risks",
|
|
10264
|
+
description: "Detect risky schema changes",
|
|
10265
|
+
shouldSkip: (ctx) => ctx.skipRisksReason ? { reason: ctx.skipRisksReason } : null,
|
|
10266
|
+
run: async (ctx) => {
|
|
10267
|
+
await detectRisks(ctx.repoRoot, ctx.tmpDir);
|
|
10268
|
+
}
|
|
10269
|
+
},
|
|
10270
|
+
{
|
|
10271
|
+
id: "snapshot",
|
|
10272
|
+
description: "Create pre-deploy snapshot",
|
|
10273
|
+
run: async (ctx) => {
|
|
10274
|
+
await snapshotCreate(
|
|
10275
|
+
ctx.repoRoot,
|
|
10276
|
+
ctx.tmpDir,
|
|
10277
|
+
ctx.inputs.productionDatabaseUrlAdmin,
|
|
10278
|
+
ctx.inputs.githubSha
|
|
10279
|
+
);
|
|
10280
|
+
}
|
|
10281
|
+
},
|
|
10282
|
+
{
|
|
10283
|
+
id: "repair-timestamps",
|
|
10284
|
+
description: "Repair timestamp invariants",
|
|
10285
|
+
run: async (ctx) => {
|
|
10286
|
+
await repairTimestampInvariants({
|
|
10287
|
+
repoRoot: ctx.repoRoot,
|
|
10288
|
+
tmpDir: ctx.tmpDir,
|
|
10289
|
+
databaseUrl: ctx.inputs.productionDatabaseUrlAdmin,
|
|
10290
|
+
labelPrefix: "production"
|
|
10291
|
+
});
|
|
10292
|
+
}
|
|
10293
|
+
},
|
|
10294
|
+
{
|
|
10295
|
+
id: "apply-schema",
|
|
10296
|
+
description: "Apply production schema",
|
|
10297
|
+
run: async (ctx) => {
|
|
10298
|
+
try {
|
|
10299
|
+
ctx.schemaApplyResult = await applyProductionSchema(
|
|
10300
|
+
ctx.repoRoot,
|
|
10301
|
+
ctx.tmpDir,
|
|
10302
|
+
ctx.inputs.productionDatabaseUrlAdmin,
|
|
10303
|
+
ctx.inputs.productionDatabaseUrl,
|
|
10304
|
+
{
|
|
10305
|
+
allowDataLoss: ctx.options.allowDataLoss === true,
|
|
10306
|
+
confirmAuthzUpdate: ctx.options.confirmAuthzUpdate === true,
|
|
10307
|
+
maxLockWaitMs: typeof ctx.options.maxLockWaitMs === "number" ? ctx.options.maxLockWaitMs : void 0
|
|
10308
|
+
}
|
|
10309
|
+
);
|
|
10310
|
+
} catch (error) {
|
|
10311
|
+
ctx.logger.error("Schema apply failed; attempting rollback from snapshot");
|
|
10312
|
+
await snapshotRestoreLatest(
|
|
10313
|
+
ctx.repoRoot,
|
|
10314
|
+
ctx.tmpDir,
|
|
10315
|
+
ctx.inputs.productionDatabaseUrlAdmin
|
|
10316
|
+
);
|
|
10317
|
+
throw error;
|
|
10318
|
+
}
|
|
10319
|
+
}
|
|
10320
|
+
},
|
|
10321
|
+
{
|
|
10322
|
+
id: "audit",
|
|
10323
|
+
description: "Record audit trail",
|
|
10324
|
+
run: async (ctx) => {
|
|
10325
|
+
try {
|
|
10326
|
+
await auditRecord(ctx.repoRoot, ctx.tmpDir, ctx.inputs.productionDatabaseUrlAdmin, {
|
|
10327
|
+
commit: ctx.inputs.githubSha,
|
|
10328
|
+
author: ctx.inputs.githubActor
|
|
10329
|
+
});
|
|
10330
|
+
} catch (error) {
|
|
10331
|
+
ctx.logger.warn(
|
|
10332
|
+
error instanceof Error ? error.message : "Audit record failed (best-effort)"
|
|
10333
|
+
);
|
|
10334
|
+
}
|
|
10335
|
+
}
|
|
10336
|
+
},
|
|
10337
|
+
{
|
|
10338
|
+
id: "notify",
|
|
10339
|
+
description: "Notify external systems",
|
|
10340
|
+
run: async (ctx) => {
|
|
10341
|
+
await maybeNotifyExternal({
|
|
10342
|
+
logger: ctx.logger,
|
|
10343
|
+
repoRoot: ctx.repoRoot,
|
|
10344
|
+
tmpDir: ctx.tmpDir,
|
|
10345
|
+
githubRepository: ctx.inputs.githubRepository,
|
|
10346
|
+
githubSha: ctx.inputs.githubSha,
|
|
10347
|
+
skipNotify: ctx.options.skipNotify === true
|
|
10348
|
+
});
|
|
10349
|
+
}
|
|
10350
|
+
},
|
|
10351
|
+
{
|
|
10352
|
+
id: "github-label",
|
|
10353
|
+
description: "Add GitHub labels/comments",
|
|
10354
|
+
run: async (ctx) => {
|
|
10355
|
+
await maybeAddGithubLabelAndComment({
|
|
10356
|
+
mode: ctx.summary.mode,
|
|
10357
|
+
skipGithubLabel: ctx.options.skipGithubLabel === true,
|
|
10358
|
+
githubSha: ctx.inputs.githubSha,
|
|
10359
|
+
schemaApplyResult: ctx.schemaApplyResult
|
|
10360
|
+
});
|
|
10361
|
+
}
|
|
10362
|
+
}
|
|
10363
|
+
];
|
|
10364
|
+
}
|
|
10365
|
+
async run() {
|
|
10366
|
+
logPlan(this.steps.map((step) => ({ id: step.id, description: step.description })));
|
|
10367
|
+
const total = this.steps.length;
|
|
10368
|
+
for (let i = 0; i < total; i++) {
|
|
10369
|
+
const step = this.steps[i];
|
|
10370
|
+
const prefix = `[DEBUG] Step ${i + 1}/${total}: ${step.description}`;
|
|
10371
|
+
const skipResult = step.shouldSkip?.(this.ctx);
|
|
10372
|
+
if (skipResult) {
|
|
10373
|
+
console.log(`${prefix} SKIPPED (${skipResult.reason})`);
|
|
10374
|
+
continue;
|
|
10375
|
+
}
|
|
10376
|
+
console.log(`${prefix}...`);
|
|
10377
|
+
const startedAt = Date.now();
|
|
10378
|
+
await step.run(this.ctx);
|
|
10379
|
+
const duration = Date.now() - startedAt;
|
|
10380
|
+
console.log(`${prefix} done (${duration}ms)`);
|
|
10381
|
+
}
|
|
10382
|
+
}
|
|
10383
|
+
};
|
|
10384
|
+
|
|
10178
10385
|
// src/commands/ci/commands/ci-resolvers.ts
|
|
10179
10386
|
init_esm_shims();
|
|
10180
10387
|
|
|
@@ -10398,18 +10605,6 @@ var CiProdApplyOutputSchema = z.object({
|
|
|
10398
10605
|
}).strict();
|
|
10399
10606
|
async function runCiProdApply(params) {
|
|
10400
10607
|
console.log("[DEBUG] runCiProdApply: starting");
|
|
10401
|
-
const steps = [
|
|
10402
|
-
{ id: "guard", description: "Acquire deployment lock and check idempotency" },
|
|
10403
|
-
{ id: "stack", description: "Validate stack and inputs" },
|
|
10404
|
-
{ id: "risks", description: "Detect risky changes" },
|
|
10405
|
-
{ id: "snapshot", description: "Create pre-deploy snapshot" },
|
|
10406
|
-
{ id: "apply", description: "Apply production schema" },
|
|
10407
|
-
{ id: "audit", description: "Record audit trail (best-effort)" },
|
|
10408
|
-
{ id: "notify", description: "Notify external system (best-effort)" },
|
|
10409
|
-
{ id: "summarize", description: "Write summary JSON" }
|
|
10410
|
-
];
|
|
10411
|
-
console.log("[DEBUG] runCiProdApply: logging plan");
|
|
10412
|
-
logPlan(steps);
|
|
10413
10608
|
const cfg = await loadCiConfig({
|
|
10414
10609
|
repoRoot: params.repoRoot,
|
|
10415
10610
|
overridePath: params.options.config
|
|
@@ -10419,7 +10614,7 @@ async function runCiProdApply(params) {
|
|
|
10419
10614
|
if (cfg.config) params.summary.detected.config = cfg.config;
|
|
10420
10615
|
requireCiAutoApprove({ mode: params.summary.mode, autoApprove: params.options.autoApprove });
|
|
10421
10616
|
const inputs = resolveProdApplyInputs();
|
|
10422
|
-
console.log("[DEBUG] Step 0
|
|
10617
|
+
console.log("[DEBUG] Step 0: guardProductionDeployment...");
|
|
10423
10618
|
const guardResult = guardProductionDeployment(
|
|
10424
10619
|
inputs.productionDatabaseUrlAdmin,
|
|
10425
10620
|
inputs.githubSha
|
|
@@ -10439,7 +10634,7 @@ async function runCiProdApply(params) {
|
|
|
10439
10634
|
"If stuck, check for orphaned advisory locks in the database"
|
|
10440
10635
|
]);
|
|
10441
10636
|
}
|
|
10442
|
-
console.log("[DEBUG] Step 0
|
|
10637
|
+
console.log("[DEBUG] Step 0: guardProductionDeployment done (lock acquired)");
|
|
10443
10638
|
try {
|
|
10444
10639
|
logKeyValueTable({
|
|
10445
10640
|
title: "Inputs",
|
|
@@ -10452,140 +10647,15 @@ async function runCiProdApply(params) {
|
|
|
10452
10647
|
]
|
|
10453
10648
|
});
|
|
10454
10649
|
logSection("Execute");
|
|
10455
|
-
|
|
10456
|
-
|
|
10457
|
-
params.repoRoot,
|
|
10458
|
-
params.tmpDir,
|
|
10459
|
-
inputs.productionDatabaseUrlAdmin
|
|
10460
|
-
);
|
|
10461
|
-
console.log(`[DEBUG] Step 1/7: detectStack done (stack=${stack})`);
|
|
10462
|
-
params.summary.detected.stack = stack;
|
|
10463
|
-
const supportedStacks = ["drizzle", "supabase"];
|
|
10464
|
-
if (!stack || !supportedStacks.includes(stack)) {
|
|
10465
|
-
throw new CLIError(
|
|
10466
|
-
`Production apply requires a supported database stack (detected: ${stack || "<empty>"}).`,
|
|
10467
|
-
"CI_PROD_STACK_UNSUPPORTED",
|
|
10468
|
-
[
|
|
10469
|
-
"Supported stacks: drizzle, supabase",
|
|
10470
|
-
"Run: runa init (to set up database structure)",
|
|
10471
|
-
"See: packages/database or supabase/schemas/"
|
|
10472
|
-
],
|
|
10473
|
-
void 0,
|
|
10474
|
-
11
|
|
10475
|
-
);
|
|
10476
|
-
}
|
|
10477
|
-
console.log("[DEBUG] Step 2/7: showSchemaDiff...");
|
|
10478
|
-
await showSchemaDiff(params.repoRoot, params.tmpDir);
|
|
10479
|
-
console.log("[DEBUG] Step 2/7: showSchemaDiff done");
|
|
10480
|
-
console.log("[DEBUG] Step 2.5/7: checkIfInitialDeployment...");
|
|
10481
|
-
const initialDeploymentCheck = checkIfInitialDeployment(
|
|
10482
|
-
params.repoRoot,
|
|
10483
|
-
inputs.productionDatabaseUrlAdmin
|
|
10484
|
-
);
|
|
10485
|
-
console.log(
|
|
10486
|
-
`[DEBUG] Step 2.5/7: checkIfInitialDeployment done (isInitial=${initialDeploymentCheck.isInitialDeployment}, tables=${initialDeploymentCheck.tableCount})`
|
|
10487
|
-
);
|
|
10488
|
-
if (initialDeploymentCheck.checkSuccessful) {
|
|
10489
|
-
if (initialDeploymentCheck.isInitialDeployment) {
|
|
10490
|
-
params.logger.info(`Initial deployment detected: ${initialDeploymentCheck.reason}`);
|
|
10491
|
-
} else {
|
|
10492
|
-
params.logger.info(
|
|
10493
|
-
`Existing deployment: ${initialDeploymentCheck.tableCount} table(s) found`
|
|
10494
|
-
);
|
|
10495
|
-
}
|
|
10496
|
-
} else {
|
|
10497
|
-
params.logger.warn(
|
|
10498
|
-
`Initial deployment check failed: ${initialDeploymentCheck.reason}. Proceeding with risk analysis.`
|
|
10499
|
-
);
|
|
10500
|
-
}
|
|
10501
|
-
const skipViaEnv = process.env.RUNA_SKIP_SCHEMA_RISK === "1" || process.env.RUNA_SKIP_SCHEMA_RISK === "true";
|
|
10502
|
-
const skipViaAutoDetect = initialDeploymentCheck.checkSuccessful && initialDeploymentCheck.isInitialDeployment;
|
|
10503
|
-
const shouldSkipRisks = params.options.skipRisks === true || params.options.initialDeployment === true || skipViaEnv || skipViaAutoDetect;
|
|
10504
|
-
if (shouldSkipRisks) {
|
|
10505
|
-
const skipReason = params.options.skipRisks ? "--skip-risks flag" : params.options.initialDeployment ? "--initial-deployment flag" : skipViaEnv ? "RUNA_SKIP_SCHEMA_RISK env" : `auto-detected initial deployment (${initialDeploymentCheck.reason})`;
|
|
10506
|
-
console.log(`[DEBUG] Step 3/7: detectRisks SKIPPED (${skipReason})`);
|
|
10507
|
-
params.logger.info(`Risk detection skipped: ${skipReason}`);
|
|
10508
|
-
} else {
|
|
10509
|
-
console.log("[DEBUG] Step 3/7: detectRisks...");
|
|
10510
|
-
await detectRisks(params.repoRoot, params.tmpDir);
|
|
10511
|
-
console.log("[DEBUG] Step 3/7: detectRisks done");
|
|
10512
|
-
}
|
|
10513
|
-
console.log("[DEBUG] Step 4/7: snapshotCreate...");
|
|
10514
|
-
await snapshotCreate(
|
|
10515
|
-
params.repoRoot,
|
|
10516
|
-
params.tmpDir,
|
|
10517
|
-
inputs.productionDatabaseUrlAdmin,
|
|
10518
|
-
inputs.githubSha
|
|
10519
|
-
);
|
|
10520
|
-
console.log("[DEBUG] Step 4/7: snapshotCreate done");
|
|
10521
|
-
console.log("[DEBUG] Step 5/7: repairTimestampInvariants...");
|
|
10522
|
-
await repairTimestampInvariants({
|
|
10523
|
-
repoRoot: params.repoRoot,
|
|
10524
|
-
tmpDir: params.tmpDir,
|
|
10525
|
-
databaseUrl: inputs.productionDatabaseUrlAdmin,
|
|
10526
|
-
labelPrefix: "production"
|
|
10527
|
-
});
|
|
10528
|
-
console.log("[DEBUG] Step 5/7: repairTimestampInvariants done");
|
|
10529
|
-
console.log("[DEBUG] Step 6/7: applyProductionSchema...");
|
|
10530
|
-
let schemaApplyResult;
|
|
10531
|
-
try {
|
|
10532
|
-
schemaApplyResult = await applyProductionSchema(
|
|
10533
|
-
params.repoRoot,
|
|
10534
|
-
params.tmpDir,
|
|
10535
|
-
inputs.productionDatabaseUrlAdmin,
|
|
10536
|
-
// DDL operations (pg-schema-diff)
|
|
10537
|
-
inputs.productionDatabaseUrl,
|
|
10538
|
-
// App verification (drizzle_app)
|
|
10539
|
-
{
|
|
10540
|
-
allowDataLoss: params.options.allowDataLoss === true,
|
|
10541
|
-
confirmAuthzUpdate: params.options.confirmAuthzUpdate === true,
|
|
10542
|
-
maxLockWaitMs: typeof params.options.maxLockWaitMs === "number" ? params.options.maxLockWaitMs : void 0
|
|
10543
|
-
}
|
|
10544
|
-
);
|
|
10545
|
-
console.log(
|
|
10546
|
-
`[DEBUG] Step 6/7: applyProductionSchema done (${schemaApplyResult.changeSummary})`
|
|
10547
|
-
);
|
|
10548
|
-
} catch (applyError) {
|
|
10549
|
-
params.logger.error("Schema apply failed; attempting rollback from snapshot");
|
|
10550
|
-
await snapshotRestoreLatest(
|
|
10551
|
-
params.repoRoot,
|
|
10552
|
-
params.tmpDir,
|
|
10553
|
-
inputs.productionDatabaseUrlAdmin
|
|
10554
|
-
);
|
|
10555
|
-
throw applyError;
|
|
10556
|
-
}
|
|
10557
|
-
console.log("[DEBUG] Step 7a/7: auditRecord...");
|
|
10558
|
-
try {
|
|
10559
|
-
await auditRecord(params.repoRoot, params.tmpDir, inputs.productionDatabaseUrlAdmin, {
|
|
10560
|
-
commit: inputs.githubSha,
|
|
10561
|
-
author: inputs.githubActor
|
|
10562
|
-
});
|
|
10563
|
-
console.log("[DEBUG] Step 7a/7: auditRecord done");
|
|
10564
|
-
} catch (auditError) {
|
|
10565
|
-
console.log("[DEBUG] Step 7a/7: auditRecord failed (non-blocking)");
|
|
10566
|
-
params.logger.warn(
|
|
10567
|
-
auditError instanceof Error ? auditError.message : "Audit record failed (best-effort)"
|
|
10568
|
-
);
|
|
10569
|
-
}
|
|
10570
|
-
console.log("[DEBUG] Step 7b/7: maybeNotifyExternal...");
|
|
10571
|
-
await maybeNotifyExternal({
|
|
10650
|
+
const workflow = new CiProdApplyWorkflow({
|
|
10651
|
+
options: params.options,
|
|
10572
10652
|
logger: params.logger,
|
|
10653
|
+
summary: params.summary,
|
|
10573
10654
|
repoRoot: params.repoRoot,
|
|
10574
10655
|
tmpDir: params.tmpDir,
|
|
10575
|
-
|
|
10576
|
-
githubSha: inputs.githubSha,
|
|
10577
|
-
skipNotify: params.options.skipNotify === true
|
|
10656
|
+
inputs
|
|
10578
10657
|
});
|
|
10579
|
-
|
|
10580
|
-
console.log("[DEBUG] Step 7c/7: maybeAddGithubLabelAndComment...");
|
|
10581
|
-
await maybeAddGithubLabelAndComment({
|
|
10582
|
-
mode: params.summary.mode,
|
|
10583
|
-
skipGithubLabel: params.options.skipGithubLabel === true,
|
|
10584
|
-
githubSha: inputs.githubSha,
|
|
10585
|
-
schemaApplyResult
|
|
10586
|
-
});
|
|
10587
|
-
console.log("[DEBUG] Step 7c/7: maybeAddGithubLabelAndComment done");
|
|
10588
|
-
console.log("[DEBUG] All steps complete, writing summary...");
|
|
10658
|
+
await workflow.run();
|
|
10589
10659
|
params.summary.status = "success";
|
|
10590
10660
|
params.summary.endedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
10591
10661
|
params.summary.durationMs = new Date(params.summary.endedAt).getTime() - new Date(params.summary.startedAt).getTime();
|
|
@@ -27085,7 +27155,7 @@ function parseGitHubUrl(url) {
|
|
|
27085
27155
|
}
|
|
27086
27156
|
}
|
|
27087
27157
|
const parts = cleanUrl.split("/");
|
|
27088
|
-
if (parts.length === 2
|
|
27158
|
+
if (parts.length === 2) {
|
|
27089
27159
|
const owner = parts[0];
|
|
27090
27160
|
const repo = parts[1];
|
|
27091
27161
|
if (isValidGitHubOwner(owner) && isValidGitHubRepo(repo)) {
|
|
@@ -30025,7 +30095,7 @@ init_esm_shims();
|
|
|
30025
30095
|
|
|
30026
30096
|
// src/constants/versions.ts
|
|
30027
30097
|
init_esm_shims();
|
|
30028
|
-
var COMPATIBLE_TEMPLATES_VERSION = "0.5.
|
|
30098
|
+
var COMPATIBLE_TEMPLATES_VERSION = "0.5.44";
|
|
30029
30099
|
var TEMPLATES_PACKAGE_NAME = "@r06-dev/runa-templates";
|
|
30030
30100
|
var GITHUB_PACKAGES_REGISTRY = "https://npm.pkg.github.com";
|
|
30031
30101
|
|
|
@@ -34777,8 +34847,9 @@ var testIntegrationCommand = new Command("test:integration").description("Run La
|
|
|
34777
34847
|
|
|
34778
34848
|
// src/commands/test/commands/test-layer.ts
|
|
34779
34849
|
init_esm_shims();
|
|
34780
|
-
function injectTestAttrsIfLayer4(layer,
|
|
34850
|
+
function injectTestAttrsIfLayer4(layer, options) {
|
|
34781
34851
|
if (layer !== 4) return;
|
|
34852
|
+
const { verbose, requireManifest } = options ?? {};
|
|
34782
34853
|
try {
|
|
34783
34854
|
if (verbose) {
|
|
34784
34855
|
console.log("[Layer 4] Auto-injecting XState test attributes...");
|
|
@@ -34790,14 +34861,30 @@ function injectTestAttrsIfLayer4(layer, verbose) {
|
|
|
34790
34861
|
if (verbose) {
|
|
34791
34862
|
console.log("[Layer 4] Test attributes injected successfully");
|
|
34792
34863
|
}
|
|
34793
|
-
} catch {
|
|
34864
|
+
} catch (error) {
|
|
34865
|
+
if (requireManifest) {
|
|
34866
|
+
throw new CLIError(
|
|
34867
|
+
"XState test attribute injection failed",
|
|
34868
|
+
"INJECT_TEST_ATTRS_FAILED",
|
|
34869
|
+
[
|
|
34870
|
+
"Run `runa inject-test-attrs` manually to see detailed errors",
|
|
34871
|
+
"Ensure XState machines exist in your project",
|
|
34872
|
+
"Check that machines have valid meta.e2e definitions",
|
|
34873
|
+
"Or remove --require-manifest flag to continue with fallback behavior"
|
|
34874
|
+
],
|
|
34875
|
+
error instanceof Error ? error : void 0
|
|
34876
|
+
);
|
|
34877
|
+
}
|
|
34794
34878
|
if (verbose) {
|
|
34795
34879
|
console.warn("[Layer 4] Test attribute injection failed, continuing with tests");
|
|
34796
34880
|
}
|
|
34797
34881
|
}
|
|
34798
34882
|
}
|
|
34799
34883
|
async function runSingleLayer(params) {
|
|
34800
|
-
injectTestAttrsIfLayer4(params.layer,
|
|
34884
|
+
injectTestAttrsIfLayer4(params.layer, {
|
|
34885
|
+
verbose: params.options.verbose,
|
|
34886
|
+
requireManifest: params.options.requireManifest
|
|
34887
|
+
});
|
|
34801
34888
|
try {
|
|
34802
34889
|
const output3 = await runTest({
|
|
34803
34890
|
layer: params.layer,
|
|
@@ -34813,6 +34900,7 @@ async function runSingleLayer(params) {
|
|
|
34813
34900
|
reviewSnapshots: params.options.reviewSnapshots,
|
|
34814
34901
|
forceRegenerate: params.options.force,
|
|
34815
34902
|
skipGeneration: params.options.skipGeneration,
|
|
34903
|
+
requireManifest: params.options.requireManifest,
|
|
34816
34904
|
invokedAs: `runa test:layer${params.layer}`
|
|
34817
34905
|
});
|
|
34818
34906
|
emitJsonSuccess(params.cmd, TestRunOutputSchema, output3);
|
|
@@ -34875,10 +34963,16 @@ function createLayerCommand(layer) {
|
|
|
34875
34963
|
await runSingleLayer({ cmd, layer, options });
|
|
34876
34964
|
});
|
|
34877
34965
|
if (layer === 3) {
|
|
34878
|
-
cmd.option("--skip-generation", "Skip auto-generation of API tests (run manual tests only)")
|
|
34966
|
+
cmd.option("--skip-generation", "Skip auto-generation of API tests (run manual tests only)").option(
|
|
34967
|
+
"--require-manifest",
|
|
34968
|
+
"Require manifest to be present and valid (fail if missing or stale)"
|
|
34969
|
+
);
|
|
34879
34970
|
}
|
|
34880
34971
|
if (layer >= 4) {
|
|
34881
|
-
cmd.option("--generate", "Generate tests from XState before running (Layer 4)").option("--advanced", "Generate advanced tests (requires --generate)").option("--auto", "Zero-config auto-generation (implementation-driven)").option("--update-snapshots", "Update visual baselines (Layer 4)").option("--filter <pattern>", 'Filter tests by pattern (e.g., "checkout-*")').option("--review-snapshots", "Interactive review of changed visual snapshots (Layer 4)").option("--force", "Force regenerate all tests, bypassing cache")
|
|
34972
|
+
cmd.option("--generate", "Generate tests from XState before running (Layer 4)").option("--advanced", "Generate advanced tests (requires --generate)").option("--auto", "Zero-config auto-generation (implementation-driven)").option("--update-snapshots", "Update visual baselines (Layer 4)").option("--filter <pattern>", 'Filter tests by pattern (e.g., "checkout-*")').option("--review-snapshots", "Interactive review of changed visual snapshots (Layer 4)").option("--force", "Force regenerate all tests, bypassing cache").option(
|
|
34973
|
+
"--require-manifest",
|
|
34974
|
+
"Require manifest to be present and valid (fail if missing or stale)"
|
|
34975
|
+
);
|
|
34882
34976
|
}
|
|
34883
34977
|
return cmd;
|
|
34884
34978
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@runa-ai/runa-cli",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.44",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "AI-powered DevOps CLI",
|
|
6
6
|
"type": "module",
|
|
@@ -53,7 +53,7 @@
|
|
|
53
53
|
"typescript": "5.9.3",
|
|
54
54
|
"xstate": "5.25.0",
|
|
55
55
|
"zod": "4.3.5",
|
|
56
|
-
"@runa-ai/runa": "0.5.
|
|
56
|
+
"@runa-ai/runa": "0.5.44",
|
|
57
57
|
"@runa-ai/runa-xstate-test-plugin": "0.5.35"
|
|
58
58
|
},
|
|
59
59
|
"engines": {
|