@runa-ai/runa-cli 0.5.58 → 0.5.60
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-supabase-local.d.ts +5 -0
- package/dist/commands/ci/commands/ci-supabase-local.d.ts.map +1 -1
- package/dist/commands/ci/machine/actors/db/sync-schema.d.ts.map +1 -1
- package/dist/commands/db/apply/actors.d.ts +4 -0
- package/dist/commands/db/apply/actors.d.ts.map +1 -1
- package/dist/commands/db/apply/contract.d.ts +1 -0
- package/dist/commands/db/apply/contract.d.ts.map +1 -1
- package/dist/commands/db/apply/helpers/index.d.ts +4 -2
- package/dist/commands/db/apply/helpers/index.d.ts.map +1 -1
- package/dist/commands/db/apply/helpers/plan-validator.d.ts +61 -0
- package/dist/commands/db/apply/helpers/plan-validator.d.ts.map +1 -0
- package/dist/commands/db/apply/helpers/retry-logic.d.ts +25 -0
- package/dist/commands/db/apply/helpers/retry-logic.d.ts.map +1 -1
- package/dist/commands/db/apply/machine.d.ts +5 -0
- package/dist/commands/db/apply/machine.d.ts.map +1 -1
- package/dist/constants/versions.d.ts +1 -1
- package/dist/index.js +317 -89
- package/package.json +2 -2
|
@@ -6,6 +6,11 @@
|
|
|
6
6
|
* Note:
|
|
7
7
|
* - This module used to be named `ci-pr-supabase-local.ts`. It is now shared across CI commands.
|
|
8
8
|
*/
|
|
9
|
+
/**
|
|
10
|
+
* Pre-flight check for port conflicts before starting Supabase.
|
|
11
|
+
* Warns if DB port is already occupied by another Supabase instance.
|
|
12
|
+
*/
|
|
13
|
+
export declare function checkSupabasePortConflicts(repoRoot: string): Promise<void>;
|
|
9
14
|
export declare function startSupabaseLocal(params: {
|
|
10
15
|
repoRoot: string;
|
|
11
16
|
tmpDir: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ci-supabase-local.d.ts","sourceRoot":"","sources":["../../../../src/commands/ci/commands/ci-supabase-local.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;
|
|
1
|
+
{"version":3,"file":"ci-supabase-local.d.ts","sourceRoot":"","sources":["../../../../src/commands/ci/commands/ci-supabase-local.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AA6CH;;;GAGG;AACH,wBAAsB,0BAA0B,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CA2ChF;AAED,wBAAsB,kBAAkB,CAAC,MAAM,EAAE;IAC/C,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;CAChB,GAAG,OAAO,CAAC,IAAI,CAAC,CAehB;AAED,wBAAsB,iBAAiB,CAAC,MAAM,EAAE;IAC9C,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;CAChB,GAAG,OAAO,CAAC,IAAI,CAAC,CAShB;AAED;;;;;GAKG;AACH,wBAAgB,0BAA0B,CAAC,QAAQ,CAAC,EAAE,MAAM,GAAG,MAAM,CAGpE;AAED;;;;;;GAMG;AACH,wBAAgB,0BAA0B,CAAC,QAAQ,CAAC,EAAE,MAAM,GAAG,MAAM,CASpE;AAED;;;GAGG;AACH,eAAO,MAAM,sBAAsB,8JAC0H,CAAC;AAE9J,wBAAsB,uBAAuB,CAAC,MAAM,EAAE;IACpD,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;CAChB,GAAG,OAAO,CAAC;IAAE,WAAW,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAAC,CAqDpD;AAED,wBAAsB,0BAA0B,CAAC,MAAM,EAAE;IACvD,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,kBAAkB,EAAE,MAAM,CAAC;CAC5B,GAAG,OAAO,CAAC;IAAE,cAAc,EAAE,MAAM,CAAA;CAAE,CAAC,CA0DtC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sync-schema.d.ts","sourceRoot":"","sources":["../../../../../../src/commands/ci/machine/actors/db/sync-schema.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AASH,OAAO,KAAK,EAAE,MAAM,EAAqB,mBAAmB,EAAiB,MAAM,gBAAgB,CAAC;AAEpG,MAAM,WAAW,eAAe;IAC9B,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,EAAE,MAAM,CAAC;IACpB,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB;AAED,MAAM,WAAW,gBAAgB;IAC/B,OAAO,EAAE,OAAO,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,mBAAmB,CAAC;CACnC;AAmQD;;;;;;GAMG;AACH,eAAO,MAAM,eAAe,
|
|
1
|
+
{"version":3,"file":"sync-schema.d.ts","sourceRoot":"","sources":["../../../../../../src/commands/ci/machine/actors/db/sync-schema.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AASH,OAAO,KAAK,EAAE,MAAM,EAAqB,mBAAmB,EAAiB,MAAM,gBAAgB,CAAC;AAEpG,MAAM,WAAW,eAAe;IAC9B,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,EAAE,MAAM,CAAC;IACpB,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB;AAED,MAAM,WAAW,gBAAgB;IAC/B,OAAO,EAAE,OAAO,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,mBAAmB,CAAC;CACnC;AAmQD;;;;;;GAMG;AACH,eAAO,MAAM,eAAe,qGA0G3B,CAAC"}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { DbApplyInput } from './contract.js';
|
|
2
2
|
export interface ApplyResult {
|
|
3
3
|
filesApplied: number;
|
|
4
|
+
filesSkipped: number;
|
|
4
5
|
rolePasswordsSet: number;
|
|
5
6
|
}
|
|
6
7
|
export interface PgSchemaDiffResult {
|
|
@@ -18,10 +19,13 @@ export interface PgSchemaDiffResult {
|
|
|
18
19
|
* Flow:
|
|
19
20
|
* 1. Apply idempotent/*.sql files (includes 15_rbac_roles.sql for ROLE creation)
|
|
20
21
|
* 2. Set passwords for drizzle_app/drizzle_service if env vars present
|
|
22
|
+
*
|
|
23
|
+
* @param pass - 'pre' = 1st pass (graceful skip on error), 'post' = 2nd pass (strict)
|
|
21
24
|
*/
|
|
22
25
|
export declare const applyIdempotentSchemas: import("xstate").PromiseActorLogic<ApplyResult, {
|
|
23
26
|
input: DbApplyInput;
|
|
24
27
|
targetDir: string;
|
|
28
|
+
pass: "pre" | "post";
|
|
25
29
|
}, import("xstate").EventObject>;
|
|
26
30
|
/**
|
|
27
31
|
* Apply schema changes using pg-schema-diff (or direct psql for fresh DB).
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"actors.d.ts","sourceRoot":"","sources":["../../../../src/commands/db/apply/actors.ts"],"names":[],"mappings":"AAuCA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AA4ElD,MAAM,WAAW,WAAW;IAC1B,YAAY,EAAE,MAAM,CAAC;IACrB,gBAAgB,EAAE,MAAM,CAAC;CAC1B;AAED,MAAM,WAAW,kBAAkB;IACjC,GAAG,EAAE,MAAM,CAAC;IACZ,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,OAAO,EAAE,OAAO,CAAC;IACjB,kDAAkD;IAClD,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,oDAAoD;IACpD,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;
|
|
1
|
+
{"version":3,"file":"actors.d.ts","sourceRoot":"","sources":["../../../../src/commands/db/apply/actors.ts"],"names":[],"mappings":"AAuCA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AA4ElD,MAAM,WAAW,WAAW;IAC1B,YAAY,EAAE,MAAM,CAAC;IACrB,YAAY,EAAE,MAAM,CAAC;IACrB,gBAAgB,EAAE,MAAM,CAAC;CAC1B;AAED,MAAM,WAAW,kBAAkB;IACjC,GAAG,EAAE,MAAM,CAAC;IACZ,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,OAAO,EAAE,OAAO,CAAC;IACjB,kDAAkD;IAClD,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,oDAAoD;IACpD,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AA2yBD;;;;;;;;GAQG;AACH,eAAO,MAAM,sBAAsB;WAExB,YAAY;eAAa,MAAM;UAAQ,KAAK,GAAG,MAAM;gCA+C9D,CAAC;AAEH;;;;;;;;;;GAUG;AACH,eAAO,MAAM,iBAAiB;WAEnB,YAAY;eAAa,MAAM;gCAmHxC,CAAC;AAEH;;GAEG;AACH,eAAO,MAAM,UAAU;aACV,OAAO;;WACT,YAAY;eAAa,MAAM;gCAqDxC,CAAC"}
|
|
@@ -43,6 +43,7 @@ export type DbApplyMetrics = z.infer<typeof DbApplyMetricsSchema>;
|
|
|
43
43
|
export declare const DbApplyOutputSchema: z.ZodObject<{
|
|
44
44
|
success: z.ZodBoolean;
|
|
45
45
|
idempotentSchemasApplied: z.ZodNumber;
|
|
46
|
+
idempotentSchemasSkipped: z.ZodOptional<z.ZodNumber>;
|
|
46
47
|
rolePasswordsSet: z.ZodOptional<z.ZodNumber>;
|
|
47
48
|
schemaChangesApplied: z.ZodBoolean;
|
|
48
49
|
hazards: z.ZodArray<z.ZodString>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"contract.d.ts","sourceRoot":"","sources":["../../../../src/commands/db/apply/contract.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AACH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,eAAO,MAAM,kBAAkB;;;;;;;;;;;;;;;kBAoCpB,CAAC;AAEZ,MAAM,MAAM,YAAY,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,kBAAkB,CAAC,CAAC;AAE9D;;GAEG;AACH,eAAO,MAAM,oBAAoB;;;;;;;;iBAe/B,CAAC;AAEH,MAAM,MAAM,cAAc,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,oBAAoB,CAAC,CAAC;AAElE,eAAO,MAAM,mBAAmB
|
|
1
|
+
{"version":3,"file":"contract.d.ts","sourceRoot":"","sources":["../../../../src/commands/db/apply/contract.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AACH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,eAAO,MAAM,kBAAkB;;;;;;;;;;;;;;;kBAoCpB,CAAC;AAEZ,MAAM,MAAM,YAAY,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,kBAAkB,CAAC,CAAC;AAE9D;;GAEG;AACH,eAAO,MAAM,oBAAoB;;;;;;;;iBAe/B,CAAC;AAEH,MAAM,MAAM,cAAc,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,oBAAoB,CAAC,CAAC;AAElE,eAAO,MAAM,mBAAmB;;;;;;;;;;;;;;;;;;;;iBA6B9B,CAAC;AAEH,MAAM,MAAM,aAAa,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,mBAAmB,CAAC,CAAC"}
|
|
@@ -4,10 +4,12 @@
|
|
|
4
4
|
* Purpose: Export all helper functions for db apply actors
|
|
5
5
|
*/
|
|
6
6
|
export { acquireAdvisoryLock, MIGRATION_LOCK_ID, releaseAdvisoryLock, } from './advisory-lock.js';
|
|
7
|
+
export type { PlanHazard, PlanStatement, ValidatedPlan } from './plan-validator.js';
|
|
8
|
+
export { parsePlanOutput, validatePlanForExecution } from './plan-validator.js';
|
|
7
9
|
export type { ParsedHazard, PgSchemaDiffPlanOptions } from './pg-schema-diff-helpers.js';
|
|
8
10
|
export type { ShadowDbConfig, ShadowDbResult } from './shadow-db-manager.js';
|
|
9
11
|
export { buildAllowedHazards, displayCheckModeResults, displayHazards, displayHazardsWithContext, executePgSchemaDiffPlan, filterFalsePositiveHazards, getIdempotentRoles, handleHazards, handleHazardsWithContext, handleProductionAuthzProtection, handleProductionDataProtection, isIdempotentRoleHazard, parseHazardsFromOutput, parseHazardsWithContext, resetIdempotentRolesCache, verifyDatabaseConnection, verifyPgSchemaDiffBinary, } from './pg-schema-diff-helpers.js';
|
|
10
|
-
export type { RetryConfig, RetryResult } from './retry-logic.js';
|
|
11
|
-
export { BASE_DELAY_MS, calculateBackoffDelay, DEFAULT_MAX_DELAY_MS, executeApplyWithRetry, isLockTimeoutError, MAX_RETRIES, sleep, } from './retry-logic.js';
|
|
12
|
+
export type { PlanSqlRetryConfig, RetryConfig, RetryResult } from './retry-logic.js';
|
|
13
|
+
export { BASE_DELAY_MS, calculateBackoffDelay, DEFAULT_MAX_DELAY_MS, executeApplyWithRetry, executePlanSqlWithRetry, isLockTimeoutError, MAX_RETRIES, sleep, } from './retry-logic.js';
|
|
12
14
|
export { createShadowDbWithExtensions, needsShadowDb } from './shadow-db-manager.js';
|
|
13
15
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/commands/db/apply/helpers/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAGH,OAAO,EACL,mBAAmB,EACnB,iBAAiB,EACjB,mBAAmB,GACpB,MAAM,oBAAoB,CAAC;AAE5B,YAAY,EAAE,YAAY,EAAE,uBAAuB,EAAE,MAAM,6BAA6B,CAAC;AACzF,YAAY,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AAE7E,OAAO,EACL,mBAAmB,EACnB,uBAAuB,EACvB,cAAc,EACd,yBAAyB,EACzB,uBAAuB,EACvB,0BAA0B,EAC1B,kBAAkB,EAClB,aAAa,EACb,wBAAwB,EACxB,+BAA+B,EAC/B,8BAA8B,EAC9B,sBAAsB,EACtB,sBAAsB,EACtB,uBAAuB,EACvB,yBAAyB,EACzB,wBAAwB,EACxB,wBAAwB,GACzB,MAAM,6BAA6B,CAAC;AACrC,YAAY,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/commands/db/apply/helpers/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAGH,OAAO,EACL,mBAAmB,EACnB,iBAAiB,EACjB,mBAAmB,GACpB,MAAM,oBAAoB,CAAC;AAE5B,YAAY,EAAE,UAAU,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACpF,OAAO,EAAE,eAAe,EAAE,wBAAwB,EAAE,MAAM,qBAAqB,CAAC;AAEhF,YAAY,EAAE,YAAY,EAAE,uBAAuB,EAAE,MAAM,6BAA6B,CAAC;AACzF,YAAY,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AAE7E,OAAO,EACL,mBAAmB,EACnB,uBAAuB,EACvB,cAAc,EACd,yBAAyB,EACzB,uBAAuB,EACvB,0BAA0B,EAC1B,kBAAkB,EAClB,aAAa,EACb,wBAAwB,EACxB,+BAA+B,EAC/B,8BAA8B,EAC9B,sBAAsB,EACtB,sBAAsB,EACtB,uBAAuB,EACvB,yBAAyB,EACzB,wBAAwB,EACxB,wBAAwB,GACzB,MAAM,6BAA6B,CAAC;AACrC,YAAY,EAAE,kBAAkB,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAErF,OAAO,EACL,aAAa,EACb,qBAAqB,EACrB,oBAAoB,EACpB,qBAAqB,EACrB,uBAAuB,EACvB,kBAAkB,EAClB,WAAW,EACX,KAAK,GACN,MAAM,kBAAkB,CAAC;AAE1B,OAAO,EAAE,4BAA4B,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAC"}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* AI HINT: pg-schema-diff Plan Output Validator
|
|
3
|
+
*
|
|
4
|
+
* Purpose: Parse pg-schema-diff plan output into structured statements and
|
|
5
|
+
* validate with Zod before psql execution (safety gate).
|
|
6
|
+
*
|
|
7
|
+
* Pattern: plan → Zod validation → psql execution
|
|
8
|
+
*
|
|
9
|
+
* Safety layers:
|
|
10
|
+
* 1. handleHazards() in actors.ts → blocks unapproved hazards (primary)
|
|
11
|
+
* 2. validatePlanForExecution() → defense-in-depth hazard check
|
|
12
|
+
* 3. Zod structural validation → ensures plan is well-formed
|
|
13
|
+
*/
|
|
14
|
+
import { z } from 'zod';
|
|
15
|
+
export declare const PlanHazardSchema: z.ZodObject<{
|
|
16
|
+
type: z.ZodString;
|
|
17
|
+
message: z.ZodString;
|
|
18
|
+
}, z.core.$strip>;
|
|
19
|
+
export declare const PlanStatementSchema: z.ZodObject<{
|
|
20
|
+
index: z.ZodNumber;
|
|
21
|
+
sql: z.ZodString;
|
|
22
|
+
hazards: z.ZodArray<z.ZodObject<{
|
|
23
|
+
type: z.ZodString;
|
|
24
|
+
message: z.ZodString;
|
|
25
|
+
}, z.core.$strip>>;
|
|
26
|
+
}, z.core.$strip>;
|
|
27
|
+
export declare const ValidatedPlanSchema: z.ZodObject<{
|
|
28
|
+
statements: z.ZodArray<z.ZodObject<{
|
|
29
|
+
index: z.ZodNumber;
|
|
30
|
+
sql: z.ZodString;
|
|
31
|
+
hazards: z.ZodArray<z.ZodObject<{
|
|
32
|
+
type: z.ZodString;
|
|
33
|
+
message: z.ZodString;
|
|
34
|
+
}, z.core.$strip>>;
|
|
35
|
+
}, z.core.$strip>>;
|
|
36
|
+
totalStatements: z.ZodNumber;
|
|
37
|
+
rawSql: z.ZodString;
|
|
38
|
+
}, z.core.$strip>;
|
|
39
|
+
export type PlanHazard = z.infer<typeof PlanHazardSchema>;
|
|
40
|
+
export type PlanStatement = z.infer<typeof PlanStatementSchema>;
|
|
41
|
+
export type ValidatedPlan = z.infer<typeof ValidatedPlanSchema>;
|
|
42
|
+
/**
|
|
43
|
+
* Parse pg-schema-diff plan output into structured statements.
|
|
44
|
+
*
|
|
45
|
+
* Handles two formats:
|
|
46
|
+
* 1. With `-- Statement Idx. N` markers → split into individual statements
|
|
47
|
+
* 2. Without markers → treat as single statement
|
|
48
|
+
*
|
|
49
|
+
* @throws ZodError if parsed result fails structural validation
|
|
50
|
+
*/
|
|
51
|
+
export declare function parsePlanOutput(planOutput: string): ValidatedPlan;
|
|
52
|
+
/**
|
|
53
|
+
* Validate that all hazards in the plan are in the allowed list.
|
|
54
|
+
*
|
|
55
|
+
* Defense-in-depth: handleHazards() in actors.ts is the primary check.
|
|
56
|
+
* This is a secondary safety gate right before psql execution.
|
|
57
|
+
*
|
|
58
|
+
* @throws Error if unresolved hazards are found
|
|
59
|
+
*/
|
|
60
|
+
export declare function validatePlanForExecution(plan: ValidatedPlan, allowedHazardTypes: string[]): void;
|
|
61
|
+
//# sourceMappingURL=plan-validator.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"plan-validator.d.ts","sourceRoot":"","sources":["../../../../../src/commands/db/apply/helpers/plan-validator.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAMxB,eAAO,MAAM,gBAAgB;;;iBAG3B,CAAC;AAEH,eAAO,MAAM,mBAAmB;;;;;;;iBAI9B,CAAC;AAEH,eAAO,MAAM,mBAAmB;;;;;;;;;;;iBAI9B,CAAC;AAEH,MAAM,MAAM,UAAU,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,gBAAgB,CAAC,CAAC;AAC1D,MAAM,MAAM,aAAa,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,mBAAmB,CAAC,CAAC;AAChE,MAAM,MAAM,aAAa,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,mBAAmB,CAAC,CAAC;AAiFhE;;;;;;;;GAQG;AACH,wBAAgB,eAAe,CAAC,UAAU,EAAE,MAAM,GAAG,aAAa,CAgBjE;AAMD;;;;;;;GAOG;AACH,wBAAgB,wBAAwB,CAAC,IAAI,EAAE,aAAa,EAAE,kBAAkB,EAAE,MAAM,EAAE,GAAG,IAAI,CAehG"}
|
|
@@ -48,4 +48,29 @@ export declare function isLockTimeoutError(errorOutput: string): boolean;
|
|
|
48
48
|
* Enhanced with configurable options and metrics.
|
|
49
49
|
*/
|
|
50
50
|
export declare function executeApplyWithRetry(applyArgs: string[], verbose: boolean, config?: RetryConfig): Promise<RetryResult>;
|
|
51
|
+
/**
|
|
52
|
+
* Options for plan+psql execution with retry.
|
|
53
|
+
*/
|
|
54
|
+
export interface PlanSqlRetryConfig extends RetryConfig {
|
|
55
|
+
/**
|
|
56
|
+
* Re-generate plan SQL on retry to handle partial execution safely.
|
|
57
|
+
* Returns fresh plan SQL, or null if all changes are already applied.
|
|
58
|
+
*/
|
|
59
|
+
rePlanFn?: () => string | null;
|
|
60
|
+
/**
|
|
61
|
+
* Allowed hazard types for defense-in-depth validation.
|
|
62
|
+
* Plan SQL is validated against this list before each psql execution.
|
|
63
|
+
* If omitted, hazard validation is skipped (primary check in actors.ts handles it).
|
|
64
|
+
*/
|
|
65
|
+
allowedHazardTypes?: string[];
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Execute plan SQL via psql with retry logic.
|
|
69
|
+
*
|
|
70
|
+
* Replaces pg-schema-diff apply to avoid creating temp DB on the production server,
|
|
71
|
+
* which exhausts connections on Supabase (max_connections=60).
|
|
72
|
+
*
|
|
73
|
+
* On retry: calls rePlanFn() to regenerate plan (handles partial execution safely).
|
|
74
|
+
*/
|
|
75
|
+
export declare function executePlanSqlWithRetry(dbUrl: string, initialPlanSql: string, verbose: boolean, config?: PlanSqlRetryConfig): Promise<RetryResult>;
|
|
51
76
|
//# sourceMappingURL=retry-logic.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"retry-logic.d.ts","sourceRoot":"","sources":["../../../../../src/commands/db/apply/helpers/retry-logic.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;
|
|
1
|
+
{"version":3,"file":"retry-logic.d.ts","sourceRoot":"","sources":["../../../../../src/commands/db/apply/helpers/retry-logic.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAaH,eAAO,MAAM,WAAW,IAAI,CAAC;AAC7B,eAAO,MAAM,aAAa,OAAO,CAAC;AAClC,eAAO,MAAM,oBAAoB,QAAQ,CAAC;AAE1C;;GAEG;AACH,MAAM,WAAW,WAAW;IAC1B,uCAAuC;IACvC,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,yDAAyD;IACzD,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,kEAAkE;IAClE,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,oCAAoC;IACpC,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED;;GAEG;AACH,MAAM,WAAW,WAAW;IAC1B,OAAO,EAAE,OAAO,CAAC;IACjB,KAAK,CAAC,EAAE,KAAK,CAAC;IACd,iEAAiE;IACjE,QAAQ,EAAE,MAAM,CAAC;IACjB,oDAAoD;IACpD,WAAW,EAAE,MAAM,CAAC;CACrB;AAED;;GAEG;AACH,wBAAgB,KAAK,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAE/C;AAED;;GAEG;AACH,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,MAAM,EAAE,UAAU,SAAuB,GAAG,MAAM,CAIhG;AAED;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAM/D;AAED;;;GAGG;AACH,wBAAsB,qBAAqB,CACzC,SAAS,EAAE,MAAM,EAAE,EACnB,OAAO,EAAE,OAAO,EAChB,MAAM,CAAC,EAAE,WAAW,GACnB,OAAO,CAAC,WAAW,CAAC,CAqEtB;AAMD;;GAEG;AACH,MAAM,WAAW,kBAAmB,SAAQ,WAAW;IACrD;;;OAGG;IACH,QAAQ,CAAC,EAAE,MAAM,MAAM,GAAG,IAAI,CAAC;IAC/B;;;;OAIG;IACH,kBAAkB,CAAC,EAAE,MAAM,EAAE,CAAC;CAC/B;AAED;;;;;;;GAOG;AACH,wBAAsB,uBAAuB,CAC3C,KAAK,EAAE,MAAM,EACb,cAAc,EAAE,MAAM,EACtB,OAAO,EAAE,OAAO,EAChB,MAAM,CAAC,EAAE,kBAAkB,GAC1B,OAAO,CAAC,WAAW,CAAC,CA0FtB"}
|
|
@@ -35,7 +35,9 @@ interface DbApplyContext {
|
|
|
35
35
|
input: DbApplyInput;
|
|
36
36
|
targetDir: string;
|
|
37
37
|
idempotentPreApplied: number;
|
|
38
|
+
idempotentPreSkipped: number;
|
|
38
39
|
idempotentPostApplied: number;
|
|
40
|
+
idempotentPostSkipped: number;
|
|
39
41
|
rolePasswordsSet: number;
|
|
40
42
|
schemaChangesApplied: boolean;
|
|
41
43
|
hazards: string[];
|
|
@@ -67,6 +69,7 @@ export declare const dbApplyMachine: import("xstate").StateMachine<DbApplyContex
|
|
|
67
69
|
}, import("xstate").EventObject>> | import("xstate").ActorRefFromLogic<import("xstate").PromiseActorLogic<actors.ApplyResult, {
|
|
68
70
|
input: DbApplyInput;
|
|
69
71
|
targetDir: string;
|
|
72
|
+
pass: "pre" | "post";
|
|
70
73
|
}, import("xstate").EventObject>> | import("xstate").ActorRefFromLogic<import("xstate").PromiseActorLogic<actors.PgSchemaDiffResult, {
|
|
71
74
|
input: DbApplyInput;
|
|
72
75
|
targetDir: string;
|
|
@@ -85,6 +88,7 @@ export declare const dbApplyMachine: import("xstate").StateMachine<DbApplyContex
|
|
|
85
88
|
logic: import("xstate").PromiseActorLogic<actors.ApplyResult, {
|
|
86
89
|
input: DbApplyInput;
|
|
87
90
|
targetDir: string;
|
|
91
|
+
pass: "pre" | "post";
|
|
88
92
|
}, import("xstate").EventObject>;
|
|
89
93
|
id: string | undefined;
|
|
90
94
|
} | {
|
|
@@ -103,6 +107,7 @@ export declare const dbApplyMachine: import("xstate").StateMachine<DbApplyContex
|
|
|
103
107
|
schemaChangesApplied: boolean;
|
|
104
108
|
hazards: string[];
|
|
105
109
|
seedsApplied: boolean;
|
|
110
|
+
idempotentSchemasSkipped?: number | undefined;
|
|
106
111
|
rolePasswordsSet?: number | undefined;
|
|
107
112
|
error?: string | undefined;
|
|
108
113
|
planSql?: string | undefined;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"machine.d.ts","sourceRoot":"","sources":["../../../../src/commands/db/apply/machine.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH,OAAO,EAAU,KAAK,YAAY,EAAS,MAAM,QAAQ,CAAC;AAE1D,OAAO,KAAK,MAAM,MAAM,aAAa,CAAC;AACtC,OAAO,KAAK,EAAE,YAAY,EAAiC,MAAM,eAAe,CAAC;AA6FjF,UAAU,cAAc;IACtB,KAAK,EAAE,YAAY,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;IAElB,oBAAoB,EAAE,MAAM,CAAC;IAC7B,qBAAqB,EAAE,MAAM,CAAC;IAC9B,gBAAgB,EAAE,MAAM,CAAC;IACzB,oBAAoB,EAAE,OAAO,CAAC;IAC9B,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,YAAY,EAAE,OAAO,CAAC;IACtB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IAEvB,SAAS,EAAE,MAAM,CAAC;IAClB,sBAAsB,EAAE,MAAM,GAAG,IAAI,CAAC;IACtC,oBAAoB,EAAE,MAAM,GAAG,IAAI,CAAC;IACpC,qBAAqB,EAAE,MAAM,GAAG,IAAI,CAAC;IACrC,mBAAmB,EAAE,MAAM,GAAG,IAAI,CAAC;IACnC,uBAAuB,EAAE,MAAM,GAAG,IAAI,CAAC;IACvC,qBAAqB,EAAE,MAAM,GAAG,IAAI,CAAC;IACrC,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,aAAa,EAAE,MAAM,CAAC;CACvB;AAID,eAAO,MAAM,cAAc;UAFC,OAAO;;UAAa,gBAAgB;WAAS,KAAK
|
|
1
|
+
{"version":3,"file":"machine.d.ts","sourceRoot":"","sources":["../../../../src/commands/db/apply/machine.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH,OAAO,EAAU,KAAK,YAAY,EAAS,MAAM,QAAQ,CAAC;AAE1D,OAAO,KAAK,MAAM,MAAM,aAAa,CAAC;AACtC,OAAO,KAAK,EAAE,YAAY,EAAiC,MAAM,eAAe,CAAC;AA6FjF,UAAU,cAAc;IACtB,KAAK,EAAE,YAAY,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;IAElB,oBAAoB,EAAE,MAAM,CAAC;IAC7B,oBAAoB,EAAE,MAAM,CAAC;IAC7B,qBAAqB,EAAE,MAAM,CAAC;IAC9B,qBAAqB,EAAE,MAAM,CAAC;IAC9B,gBAAgB,EAAE,MAAM,CAAC;IACzB,oBAAoB,EAAE,OAAO,CAAC;IAC9B,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,YAAY,EAAE,OAAO,CAAC;IACtB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IAEvB,SAAS,EAAE,MAAM,CAAC;IAClB,sBAAsB,EAAE,MAAM,GAAG,IAAI,CAAC;IACtC,oBAAoB,EAAE,MAAM,GAAG,IAAI,CAAC;IACpC,qBAAqB,EAAE,MAAM,GAAG,IAAI,CAAC;IACrC,mBAAmB,EAAE,MAAM,GAAG,IAAI,CAAC;IACnC,uBAAuB,EAAE,MAAM,GAAG,IAAI,CAAC;IACvC,qBAAqB,EAAE,MAAM,GAAG,IAAI,CAAC;IACrC,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,aAAa,EAAE,MAAM,CAAC;CACvB;AAID,eAAO,MAAM,cAAc;UAFC,OAAO;;UAAa,gBAAgB;WAAS,KAAK;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;WAI1D,YAAY;eAAa,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EA2OjD,CAAC;AAGH,MAAM,MAAM,cAAc,GAAG,OAAO,cAAc,CAAC;AACnD,MAAM,MAAM,eAAe,GAAG,YAAY,CAAC,OAAO,cAAc,CAAC,CAAC;AAGlE,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,eAAe,GAAG,MAAM,CAErE;AAED,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,eAAe,GAAG,OAAO,CAEpE;AAED,wBAAgB,eAAe,CAAC,QAAQ,EAAE,eAAe,GAAG,MAAM,GAAG,IAAI,CAExE"}
|
|
@@ -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.60";
|
|
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
|
@@ -1161,7 +1161,7 @@ var CLI_VERSION, HAS_ADMIN_COMMAND;
|
|
|
1161
1161
|
var init_version = __esm({
|
|
1162
1162
|
"src/version.ts"() {
|
|
1163
1163
|
init_esm_shims();
|
|
1164
|
-
CLI_VERSION = "0.5.
|
|
1164
|
+
CLI_VERSION = "0.5.60";
|
|
1165
1165
|
HAS_ADMIN_COMMAND = false;
|
|
1166
1166
|
}
|
|
1167
1167
|
});
|
|
@@ -7599,13 +7599,13 @@ function printSummary(logger16, output3) {
|
|
|
7599
7599
|
}
|
|
7600
7600
|
function findRepoRoot(startDir) {
|
|
7601
7601
|
const { existsSync: existsSync53, readFileSync: readFileSync30 } = __require("fs");
|
|
7602
|
-
const { join:
|
|
7602
|
+
const { join: join24, dirname: dirname5 } = __require("path");
|
|
7603
7603
|
let current = startDir;
|
|
7604
7604
|
while (current !== dirname5(current)) {
|
|
7605
|
-
if (existsSync53(
|
|
7605
|
+
if (existsSync53(join24(current, "turbo.json"))) {
|
|
7606
7606
|
return current;
|
|
7607
7607
|
}
|
|
7608
|
-
const pkgPath =
|
|
7608
|
+
const pkgPath = join24(current, "package.json");
|
|
7609
7609
|
if (existsSync53(pkgPath)) {
|
|
7610
7610
|
try {
|
|
7611
7611
|
const pkg = JSON.parse(readFileSync30(pkgPath, "utf-8"));
|
|
@@ -8176,8 +8176,8 @@ function determineAppCommand(mode, isMonorepo2, rootScripts, appScripts, repoRoo
|
|
|
8176
8176
|
const nextCommand = mode === "dev" ? "dev" : "start";
|
|
8177
8177
|
const rootHasCiScript = Boolean(rootScripts?.[ciScriptName]);
|
|
8178
8178
|
const appHasCiScript = Boolean(appScripts?.[ciScriptName]);
|
|
8179
|
-
const rootHasDefaultScript = mode === "start" && Boolean(rootScripts?.
|
|
8180
|
-
const appHasDefaultScript = mode === "start" && Boolean(appScripts?.
|
|
8179
|
+
const rootHasDefaultScript = mode === "start" && Boolean(rootScripts?.start);
|
|
8180
|
+
const appHasDefaultScript = mode === "start" && Boolean(appScripts?.start);
|
|
8181
8181
|
if (isMonorepo2 && (rootHasCiScript || rootHasDefaultScript)) {
|
|
8182
8182
|
const scriptName = rootHasCiScript ? ciScriptName : "start";
|
|
8183
8183
|
return { command: ["pnpm", scriptName], useRootScript: true };
|
|
@@ -8429,7 +8429,7 @@ var processCheckActor = fromPromise(async ({ input: input3 }) => {
|
|
|
8429
8429
|
}
|
|
8430
8430
|
if (pidFileContent) {
|
|
8431
8431
|
const pid = parseInt(pidFileContent, 10);
|
|
8432
|
-
if (!isNaN(pid) && isProcessAlive(pid)) {
|
|
8432
|
+
if (!Number.isNaN(pid) && isProcessAlive(pid)) {
|
|
8433
8433
|
if (input3.replace) {
|
|
8434
8434
|
await terminateAppProcessByPid({
|
|
8435
8435
|
pid,
|
|
@@ -13099,7 +13099,7 @@ var syncSchemaActor = fromPromise(
|
|
|
13099
13099
|
"--auto-approve",
|
|
13100
13100
|
// Allow DELETES_DATA hazards in preview
|
|
13101
13101
|
"--no-seed",
|
|
13102
|
-
// Seeds applied separately by applySeedsActor
|
|
13102
|
+
// Seeds applied separately by applySeedsActor
|
|
13103
13103
|
"--verbose"
|
|
13104
13104
|
// Always verbose for full traceability
|
|
13105
13105
|
] : [
|
|
@@ -13109,6 +13109,8 @@ var syncSchemaActor = fromPromise(
|
|
|
13109
13109
|
"sync",
|
|
13110
13110
|
envArg,
|
|
13111
13111
|
"--auto-approve",
|
|
13112
|
+
"--no-seed",
|
|
13113
|
+
// Seeds applied separately by applySeedsActor
|
|
13112
13114
|
"--verbose",
|
|
13113
13115
|
// Always verbose for full traceability
|
|
13114
13116
|
...skipCodegen ? ["--skip-codegen"] : []
|
|
@@ -13245,7 +13247,67 @@ init_esm_shims();
|
|
|
13245
13247
|
// src/commands/ci/commands/ci-supabase-local.ts
|
|
13246
13248
|
init_esm_shims();
|
|
13247
13249
|
init_constants();
|
|
13250
|
+
function isPortAvailable(port) {
|
|
13251
|
+
return new Promise((resolve12) => {
|
|
13252
|
+
const server = net.createServer();
|
|
13253
|
+
server.once("error", () => resolve12(false));
|
|
13254
|
+
server.once("listening", () => {
|
|
13255
|
+
server.close(() => resolve12(true));
|
|
13256
|
+
});
|
|
13257
|
+
server.listen(port);
|
|
13258
|
+
});
|
|
13259
|
+
}
|
|
13260
|
+
function detectSupabaseContainers() {
|
|
13261
|
+
try {
|
|
13262
|
+
const result = spawnSync(
|
|
13263
|
+
"docker",
|
|
13264
|
+
["ps", "--format", "{{.Names}} {{.Ports}} {{.Status}}", "--filter", "name=supabase"],
|
|
13265
|
+
{ encoding: "utf-8", timeout: 5e3, stdio: ["pipe", "pipe", "pipe"] }
|
|
13266
|
+
);
|
|
13267
|
+
if (result.status !== 0 || !result.stdout?.trim()) return [];
|
|
13268
|
+
return result.stdout.trim().split("\n").filter(Boolean);
|
|
13269
|
+
} catch {
|
|
13270
|
+
return [];
|
|
13271
|
+
}
|
|
13272
|
+
}
|
|
13273
|
+
async function checkSupabasePortConflicts(repoRoot) {
|
|
13274
|
+
let dbPort = 54322;
|
|
13275
|
+
try {
|
|
13276
|
+
const { readFileSync: readFileSync30 } = await import('fs');
|
|
13277
|
+
const configPath = path11__default.join(repoRoot, "supabase", "config.toml");
|
|
13278
|
+
const content = readFileSync30(configPath, "utf-8");
|
|
13279
|
+
const match = /\[db\][^[]*?port\s*=\s*(\d+)/s.exec(content);
|
|
13280
|
+
if (match?.[1]) dbPort = Number.parseInt(match[1], 10);
|
|
13281
|
+
} catch {
|
|
13282
|
+
}
|
|
13283
|
+
const available = await isPortAvailable(dbPort);
|
|
13284
|
+
if (available) return;
|
|
13285
|
+
const containers = detectSupabaseContainers();
|
|
13286
|
+
const dbContainers = containers.filter((c) => c.includes(String(dbPort)) || c.includes("db-"));
|
|
13287
|
+
console.warn("");
|
|
13288
|
+
console.warn("\u26A0\uFE0F Port conflict detected: PostgreSQL port %d is already in use", dbPort);
|
|
13289
|
+
console.warn("");
|
|
13290
|
+
if (dbContainers.length > 0) {
|
|
13291
|
+
console.warn(" Running Supabase containers:");
|
|
13292
|
+
for (const c of dbContainers) {
|
|
13293
|
+
console.warn(` ${c}`);
|
|
13294
|
+
}
|
|
13295
|
+
} else if (containers.length > 0) {
|
|
13296
|
+
console.warn(" Running Supabase containers (no DB match):");
|
|
13297
|
+
for (const c of containers.slice(0, 5)) {
|
|
13298
|
+
console.warn(` ${c}`);
|
|
13299
|
+
}
|
|
13300
|
+
} else {
|
|
13301
|
+
console.warn(" No Supabase containers detected (port may be used by another process).");
|
|
13302
|
+
}
|
|
13303
|
+
console.warn("");
|
|
13304
|
+
console.warn(" To fix:");
|
|
13305
|
+
console.warn(" 1. Stop the other instance: supabase stop (in the other project)");
|
|
13306
|
+
console.warn(" 2. Or use a different port in supabase/config.toml: [db] port = 54323");
|
|
13307
|
+
console.warn("");
|
|
13308
|
+
}
|
|
13248
13309
|
async function startSupabaseLocal(params) {
|
|
13310
|
+
await checkSupabasePortConflicts(params.repoRoot);
|
|
13249
13311
|
const exclude = process.env.RUNA_CI_SUPABASE_EXCLUDE ?? "studio,edge-runtime,storage-api,realtime,imgproxy,mailpit,logflare,vector,supavisor";
|
|
13250
13312
|
await runLogged({
|
|
13251
13313
|
cwd: params.repoRoot,
|
|
@@ -13465,7 +13527,7 @@ AND table_schema NOT IN ('information_schema');
|
|
|
13465
13527
|
SELECT COUNT(*)::int
|
|
13466
13528
|
FROM pg_class c
|
|
13467
13529
|
JOIN pg_namespace n ON n.oid=c.relnamespace
|
|
13468
|
-
WHERE c.relkind
|
|
13530
|
+
WHERE c.relkind IN ('r','p')
|
|
13469
13531
|
AND n.nspname NOT LIKE 'pg_%'
|
|
13470
13532
|
AND n.nspname NOT IN ('information_schema')
|
|
13471
13533
|
AND c.relrowsecurity;
|
|
@@ -19404,6 +19466,93 @@ function releaseAdvisoryLock(dbUrl, verbose) {
|
|
|
19404
19466
|
}
|
|
19405
19467
|
}
|
|
19406
19468
|
|
|
19469
|
+
// src/commands/db/apply/helpers/plan-validator.ts
|
|
19470
|
+
init_esm_shims();
|
|
19471
|
+
var PlanHazardSchema = z.object({
|
|
19472
|
+
type: z.string().min(1),
|
|
19473
|
+
message: z.string()
|
|
19474
|
+
});
|
|
19475
|
+
var PlanStatementSchema = z.object({
|
|
19476
|
+
index: z.number().int().nonnegative(),
|
|
19477
|
+
sql: z.string().min(1),
|
|
19478
|
+
hazards: z.array(PlanHazardSchema)
|
|
19479
|
+
});
|
|
19480
|
+
var ValidatedPlanSchema = z.object({
|
|
19481
|
+
statements: z.array(PlanStatementSchema),
|
|
19482
|
+
totalStatements: z.number().int().nonnegative(),
|
|
19483
|
+
rawSql: z.string().min(1)
|
|
19484
|
+
});
|
|
19485
|
+
function flushStatement(current, results) {
|
|
19486
|
+
if (!current) return;
|
|
19487
|
+
const sql = current.sqlLines.join("\n").trim();
|
|
19488
|
+
if (!sql) return;
|
|
19489
|
+
results.push({ index: current.index, sql, hazards: current.hazards });
|
|
19490
|
+
}
|
|
19491
|
+
function parseWithStatementMarkers(lines) {
|
|
19492
|
+
const results = [];
|
|
19493
|
+
let current = null;
|
|
19494
|
+
for (const line of lines) {
|
|
19495
|
+
const trimmed = line.trim();
|
|
19496
|
+
const idxMatch = trimmed.match(/^-- Statement Idx\.\s*(\d+)/);
|
|
19497
|
+
if (idxMatch) {
|
|
19498
|
+
flushStatement(current, results);
|
|
19499
|
+
current = { index: parseInt(idxMatch[1], 10), sqlLines: [], hazards: [] };
|
|
19500
|
+
continue;
|
|
19501
|
+
}
|
|
19502
|
+
const hazardMatch = trimmed.match(/^-- Hazard (\w+): (.+)/);
|
|
19503
|
+
if (hazardMatch && current) {
|
|
19504
|
+
current.hazards.push({ type: hazardMatch[1], message: hazardMatch[2] });
|
|
19505
|
+
continue;
|
|
19506
|
+
}
|
|
19507
|
+
if (current && trimmed && !trimmed.startsWith("--")) {
|
|
19508
|
+
current.sqlLines.push(line);
|
|
19509
|
+
}
|
|
19510
|
+
}
|
|
19511
|
+
flushStatement(current, results);
|
|
19512
|
+
return results;
|
|
19513
|
+
}
|
|
19514
|
+
function parseAsSingleStatement(lines) {
|
|
19515
|
+
const sqlLines = [];
|
|
19516
|
+
const hazards = [];
|
|
19517
|
+
for (const line of lines) {
|
|
19518
|
+
const trimmed = line.trim();
|
|
19519
|
+
const hazardMatch = trimmed.match(/^-- Hazard (\w+): (.+)/);
|
|
19520
|
+
if (hazardMatch) {
|
|
19521
|
+
hazards.push({ type: hazardMatch[1], message: hazardMatch[2] });
|
|
19522
|
+
continue;
|
|
19523
|
+
}
|
|
19524
|
+
if (trimmed && !trimmed.startsWith("--")) {
|
|
19525
|
+
sqlLines.push(line);
|
|
19526
|
+
}
|
|
19527
|
+
}
|
|
19528
|
+
const sql = sqlLines.join("\n").trim();
|
|
19529
|
+
if (!sql) return [];
|
|
19530
|
+
return [{ index: 0, sql, hazards }];
|
|
19531
|
+
}
|
|
19532
|
+
function parsePlanOutput(planOutput) {
|
|
19533
|
+
const lines = planOutput.split("\n");
|
|
19534
|
+
const hasStatementMarkers = lines.some((l) => /^-- Statement Idx\.\s*\d+/.test(l.trim()));
|
|
19535
|
+
const statements = hasStatementMarkers ? parseWithStatementMarkers(lines) : parseAsSingleStatement(lines);
|
|
19536
|
+
const plan = {
|
|
19537
|
+
statements,
|
|
19538
|
+
totalStatements: statements.length,
|
|
19539
|
+
rawSql: planOutput
|
|
19540
|
+
};
|
|
19541
|
+
return ValidatedPlanSchema.parse(plan);
|
|
19542
|
+
}
|
|
19543
|
+
function validatePlanForExecution(plan, allowedHazardTypes) {
|
|
19544
|
+
if (plan.totalStatements === 0) return;
|
|
19545
|
+
const allHazards = plan.statements.flatMap((s) => s.hazards);
|
|
19546
|
+
const allowedSet = new Set(allowedHazardTypes);
|
|
19547
|
+
const unresolved = allHazards.filter((h) => !allowedSet.has(h.type));
|
|
19548
|
+
if (unresolved.length > 0) {
|
|
19549
|
+
const types = [...new Set(unresolved.map((h) => h.type))].join(", ");
|
|
19550
|
+
throw new Error(
|
|
19551
|
+
`Plan contains unapproved hazards: ${types}. Approved: [${allowedHazardTypes.join(", ")}]. This is a safety check \u2014 hazards must be approved before execution.`
|
|
19552
|
+
);
|
|
19553
|
+
}
|
|
19554
|
+
}
|
|
19555
|
+
|
|
19407
19556
|
// src/commands/db/apply/helpers/pg-schema-diff-helpers.ts
|
|
19408
19557
|
init_esm_shims();
|
|
19409
19558
|
var logger4 = createCLILogger("db:apply");
|
|
@@ -19749,49 +19898,70 @@ function calculateBackoffDelay(attempt, maxDelayMs = DEFAULT_MAX_DELAY_MS) {
|
|
|
19749
19898
|
function isLockTimeoutError(errorOutput) {
|
|
19750
19899
|
return errorOutput.includes("lock_timeout") || errorOutput.includes("canceling statement due to lock timeout") || errorOutput.includes("could not obtain lock");
|
|
19751
19900
|
}
|
|
19752
|
-
async function
|
|
19901
|
+
async function executePlanSqlWithRetry(dbUrl, initialPlanSql, verbose, config) {
|
|
19753
19902
|
const maxRetries = config?.maxRetries ?? MAX_RETRIES;
|
|
19754
19903
|
const maxDelayMs = config?.maxDelayMs ?? DEFAULT_MAX_DELAY_MS;
|
|
19755
19904
|
let lastError = null;
|
|
19756
19905
|
let totalWaitMs = 0;
|
|
19906
|
+
let currentPlanSql = initialPlanSql;
|
|
19757
19907
|
for (let attempt = 0; attempt < maxRetries; attempt++) {
|
|
19758
19908
|
if (attempt > 0) {
|
|
19759
19909
|
const delay = calculateBackoffDelay(attempt - 1, maxDelayMs);
|
|
19760
19910
|
totalWaitMs += delay;
|
|
19761
19911
|
logger5.info(`Retry ${attempt}/${maxRetries - 1} after ${Math.round(delay)}ms...`);
|
|
19762
19912
|
await sleep(delay);
|
|
19913
|
+
if (config?.rePlanFn) {
|
|
19914
|
+
const freshPlan = config.rePlanFn();
|
|
19915
|
+
if (freshPlan === null) {
|
|
19916
|
+
return { success: true, attempts: attempt, totalWaitMs };
|
|
19917
|
+
}
|
|
19918
|
+
currentPlanSql = freshPlan;
|
|
19919
|
+
}
|
|
19763
19920
|
}
|
|
19764
|
-
const
|
|
19765
|
-
|
|
19766
|
-
stdio: ["pipe", "pipe", "pipe"]
|
|
19767
|
-
});
|
|
19768
|
-
const stdout = applyResult.stdout || "";
|
|
19769
|
-
const stderr = applyResult.stderr || "";
|
|
19770
|
-
if (verbose) {
|
|
19771
|
-
if (stdout) process.stdout.write(stdout);
|
|
19772
|
-
if (stderr) process.stderr.write(stderr);
|
|
19773
|
-
}
|
|
19774
|
-
if (applyResult.status === 0) {
|
|
19921
|
+
const plan = parsePlanOutput(currentPlanSql);
|
|
19922
|
+
if (plan.totalStatements === 0) {
|
|
19775
19923
|
return { success: true, attempts: attempt, totalWaitMs };
|
|
19776
19924
|
}
|
|
19777
|
-
|
|
19778
|
-
|
|
19779
|
-
logger5.warn(`Lock timeout on attempt ${attempt + 1}/${maxRetries}`);
|
|
19780
|
-
lastError = new Error(`lock_timeout: ${errorOutput}`);
|
|
19781
|
-
continue;
|
|
19925
|
+
if (config?.allowedHazardTypes) {
|
|
19926
|
+
validatePlanForExecution(plan, config.allowedHazardTypes);
|
|
19782
19927
|
}
|
|
19783
|
-
|
|
19784
|
-
|
|
19785
|
-
|
|
19786
|
-
|
|
19787
|
-
|
|
19928
|
+
if (verbose) {
|
|
19929
|
+
logger5.debug(`Plan validated: ${plan.totalStatements} statement(s)`);
|
|
19930
|
+
}
|
|
19931
|
+
const planFile = join(tmpdir(), `runa-plan-${Date.now()}-${attempt}.sql`);
|
|
19932
|
+
const wrappedSql = `SET lock_timeout = '50ms';
|
|
19933
|
+
|
|
19934
|
+
${currentPlanSql}`;
|
|
19935
|
+
writeFileSync(planFile, wrappedSql, "utf-8");
|
|
19936
|
+
try {
|
|
19937
|
+
const result = psqlSyncFile({ databaseUrl: dbUrl, filePath: planFile, onErrorStop: true });
|
|
19938
|
+
if (verbose) {
|
|
19939
|
+
if (result.stdout) process.stdout.write(result.stdout);
|
|
19940
|
+
if (result.stderr) process.stderr.write(result.stderr);
|
|
19941
|
+
}
|
|
19942
|
+
if (result.status === 0) {
|
|
19943
|
+
return { success: true, attempts: attempt, totalWaitMs };
|
|
19944
|
+
}
|
|
19945
|
+
const errorOutput = result.stderr || result.stdout || "Unknown error";
|
|
19946
|
+
if (isLockTimeoutError(errorOutput)) {
|
|
19947
|
+
logger5.warn(`Lock timeout on attempt ${attempt + 1}/${maxRetries}`);
|
|
19948
|
+
lastError = new Error(`lock_timeout: ${errorOutput}`);
|
|
19949
|
+
continue;
|
|
19950
|
+
}
|
|
19951
|
+
logger5.error("Plan execution failed:");
|
|
19952
|
+
logger5.error(errorOutput);
|
|
19953
|
+
return {
|
|
19954
|
+
success: false,
|
|
19955
|
+
error: new Error(`Plan execution failed: ${errorOutput}`),
|
|
19956
|
+
attempts: attempt,
|
|
19957
|
+
totalWaitMs
|
|
19958
|
+
};
|
|
19959
|
+
} finally {
|
|
19960
|
+
try {
|
|
19961
|
+
unlinkSync(planFile);
|
|
19962
|
+
} catch {
|
|
19963
|
+
}
|
|
19788
19964
|
}
|
|
19789
|
-
return {
|
|
19790
|
-
success: false,
|
|
19791
|
-
error: new Error(`pg-schema-diff apply failed: ${errorOutput}`),
|
|
19792
|
-
attempts: attempt,
|
|
19793
|
-
totalWaitMs
|
|
19794
|
-
};
|
|
19795
19965
|
}
|
|
19796
19966
|
logger5.error(`Migration failed after ${maxRetries} attempts (total wait: ${totalWaitMs}ms)`);
|
|
19797
19967
|
return {
|
|
@@ -20087,48 +20257,58 @@ function handleFreshDbCase(input3, dbUrl, targetDir) {
|
|
|
20087
20257
|
return { sql: "", hazards: [], applied: true };
|
|
20088
20258
|
}
|
|
20089
20259
|
}
|
|
20090
|
-
async function applyWithLockAndRetry(dbUrl, schemasDir, includeSchemas, input3, planOutput, hazards) {
|
|
20091
|
-
logger6.step("Applying schema changes...");
|
|
20260
|
+
async function applyWithLockAndRetry(dbUrl, schemasDir, includeSchemas, input3, planOutput, hazards, tempDbDsn) {
|
|
20261
|
+
logger6.step("Applying schema changes (plan+psql)...");
|
|
20092
20262
|
const lockAcquired = acquireAdvisoryLock(dbUrl, input3.verbose);
|
|
20093
20263
|
if (!lockAcquired) {
|
|
20094
20264
|
throw new Error(
|
|
20095
20265
|
"Could not acquire migration lock. Another migration may be running. Wait for it to complete or manually release the lock."
|
|
20096
20266
|
);
|
|
20097
20267
|
}
|
|
20098
|
-
|
|
20099
|
-
|
|
20100
|
-
|
|
20101
|
-
|
|
20102
|
-
|
|
20103
|
-
|
|
20104
|
-
|
|
20105
|
-
|
|
20106
|
-
|
|
20107
|
-
|
|
20108
|
-
|
|
20109
|
-
|
|
20110
|
-
|
|
20111
|
-
|
|
20112
|
-
|
|
20113
|
-
|
|
20114
|
-
|
|
20115
|
-
|
|
20116
|
-
|
|
20117
|
-
|
|
20118
|
-
|
|
20119
|
-
|
|
20120
|
-
|
|
20121
|
-
|
|
20122
|
-
|
|
20268
|
+
try {
|
|
20269
|
+
const allowedHazardTypes = buildAllowedHazards(input3);
|
|
20270
|
+
const result = await executePlanSqlWithRetry(dbUrl, planOutput, input3.verbose, {
|
|
20271
|
+
maxDelayMs: input3.maxLockWaitMs,
|
|
20272
|
+
allowedHazardTypes,
|
|
20273
|
+
rePlanFn: () => {
|
|
20274
|
+
const { planOutput: freshPlan } = executePgSchemaDiffPlan(
|
|
20275
|
+
dbUrl,
|
|
20276
|
+
schemasDir,
|
|
20277
|
+
includeSchemas,
|
|
20278
|
+
input3.verbose,
|
|
20279
|
+
{ tempDbDsn }
|
|
20280
|
+
);
|
|
20281
|
+
if (!freshPlan.trim() || freshPlan.includes("No changes")) {
|
|
20282
|
+
return null;
|
|
20283
|
+
}
|
|
20284
|
+
return freshPlan;
|
|
20285
|
+
}
|
|
20286
|
+
});
|
|
20287
|
+
if (!result.success) {
|
|
20288
|
+
throw result.error || new Error("Migration failed");
|
|
20289
|
+
}
|
|
20290
|
+
if (input3.verbose && result.attempts > 0) {
|
|
20291
|
+
logger6.debug(
|
|
20292
|
+
`Retry metrics: ${result.attempts} attempts, ${result.totalWaitMs}ms total wait`
|
|
20293
|
+
);
|
|
20294
|
+
}
|
|
20295
|
+
logger6.success("Schema changes applied");
|
|
20296
|
+
return {
|
|
20297
|
+
sql: planOutput,
|
|
20298
|
+
hazards,
|
|
20299
|
+
applied: true,
|
|
20300
|
+
retryAttempts: result.attempts,
|
|
20301
|
+
retryWaitMs: result.totalWaitMs
|
|
20302
|
+
};
|
|
20303
|
+
} finally {
|
|
20304
|
+
try {
|
|
20305
|
+
releaseAdvisoryLock(dbUrl, input3.verbose);
|
|
20306
|
+
} catch (lockError) {
|
|
20307
|
+
logger6.warn(
|
|
20308
|
+
`Failed to release advisory lock: ${lockError instanceof Error ? lockError.message : "Unknown error"}`
|
|
20309
|
+
);
|
|
20310
|
+
}
|
|
20123
20311
|
}
|
|
20124
|
-
logger6.success("Schema changes applied");
|
|
20125
|
-
return {
|
|
20126
|
-
sql: planOutput,
|
|
20127
|
-
hazards,
|
|
20128
|
-
applied: true,
|
|
20129
|
-
retryAttempts: result.attempts,
|
|
20130
|
-
retryWaitMs: result.totalWaitMs
|
|
20131
|
-
};
|
|
20132
20312
|
}
|
|
20133
20313
|
var ROLE_PASSWORD_CONFIGS = [
|
|
20134
20314
|
{
|
|
@@ -20239,45 +20419,66 @@ function shouldSkipInProduction(file, env2, verbose) {
|
|
|
20239
20419
|
}
|
|
20240
20420
|
return true;
|
|
20241
20421
|
}
|
|
20242
|
-
function applySingleIdempotentFile(dbUrl, schemasDir, file, verbose) {
|
|
20422
|
+
function applySingleIdempotentFile(dbUrl, schemasDir, file, verbose, pass) {
|
|
20243
20423
|
const filePath = join(schemasDir, file);
|
|
20244
20424
|
if (verbose) logger6.debug(`Applying ${file}...`);
|
|
20245
20425
|
const result = psqlSyncFile({
|
|
20246
20426
|
databaseUrl: dbUrl,
|
|
20247
20427
|
filePath,
|
|
20248
20428
|
onErrorStop: true
|
|
20249
|
-
// Fail fast on idempotent schemas
|
|
20250
20429
|
});
|
|
20251
20430
|
if (verbose) {
|
|
20252
20431
|
if (result.stdout) process.stdout.write(result.stdout);
|
|
20253
20432
|
if (result.stderr) process.stderr.write(result.stderr);
|
|
20254
20433
|
}
|
|
20255
20434
|
if (result.status !== 0) {
|
|
20435
|
+
if (pass === "pre") {
|
|
20436
|
+
if (verbose) {
|
|
20437
|
+
logger6.warn(`Skipped ${file} (will retry in 2nd pass)`);
|
|
20438
|
+
}
|
|
20439
|
+
return false;
|
|
20440
|
+
}
|
|
20256
20441
|
const errorMsg = result.stderr ? maskDbCredentials(result.stderr) : "";
|
|
20257
20442
|
throw new Error(`Failed to apply idempotent schema: ${file}
|
|
20258
20443
|
${errorMsg}`);
|
|
20259
20444
|
}
|
|
20445
|
+
return true;
|
|
20260
20446
|
}
|
|
20261
|
-
var applyIdempotentSchemas = fromPromise(async ({ input: { input: input3, targetDir } }) => {
|
|
20447
|
+
var applyIdempotentSchemas = fromPromise(async ({ input: { input: input3, targetDir, pass } }) => {
|
|
20262
20448
|
checkPasswordSecurity();
|
|
20263
20449
|
const schemasDir = join(targetDir, "supabase/schemas/idempotent");
|
|
20264
20450
|
const dbUrl = getDbUrl(input3);
|
|
20265
20451
|
let filesApplied = 0;
|
|
20452
|
+
let filesSkipped = 0;
|
|
20266
20453
|
if (existsSync(schemasDir)) {
|
|
20267
20454
|
const files = readdirSync(schemasDir).filter((f) => f.endsWith(".sql")).sort();
|
|
20268
20455
|
if (files.length > 0) {
|
|
20269
20456
|
for (const file of files) {
|
|
20270
20457
|
if (shouldSkipInProduction(file, input3.env, input3.verbose)) continue;
|
|
20271
|
-
applySingleIdempotentFile(dbUrl, schemasDir, file, input3.verbose);
|
|
20458
|
+
const applied = applySingleIdempotentFile(dbUrl, schemasDir, file, input3.verbose, pass);
|
|
20459
|
+
if (applied) {
|
|
20460
|
+
filesApplied++;
|
|
20461
|
+
} else {
|
|
20462
|
+
filesSkipped++;
|
|
20463
|
+
}
|
|
20464
|
+
}
|
|
20465
|
+
if (filesSkipped > 0) {
|
|
20466
|
+
logger6.success(
|
|
20467
|
+
`Applied ${filesApplied}/${filesApplied + filesSkipped} idempotent schema(s) (${filesSkipped} deferred to 2nd pass)`
|
|
20468
|
+
);
|
|
20469
|
+
if (filesSkipped === files.length) {
|
|
20470
|
+
logger6.warn("ALL idempotent files skipped in 1st pass.");
|
|
20471
|
+
logger6.warn("This may indicate a syntax error. Check logs with --verbose.");
|
|
20472
|
+
}
|
|
20473
|
+
} else {
|
|
20474
|
+
logger6.success(`Applied ${filesApplied} idempotent schema(s)`);
|
|
20272
20475
|
}
|
|
20273
|
-
logger6.success(`Applied ${files.length} idempotent schema(s)`);
|
|
20274
|
-
filesApplied = files.length;
|
|
20275
20476
|
}
|
|
20276
20477
|
} else {
|
|
20277
20478
|
logger6.info("No idempotent schemas found");
|
|
20278
20479
|
}
|
|
20279
20480
|
const rolePasswordsSet = setRolePasswords(dbUrl, input3.verbose);
|
|
20280
|
-
return { filesApplied, rolePasswordsSet };
|
|
20481
|
+
return { filesApplied, filesSkipped, rolePasswordsSet };
|
|
20281
20482
|
});
|
|
20282
20483
|
var applyPgSchemaDiff = fromPromise(async ({ input: { input: input3, targetDir } }) => {
|
|
20283
20484
|
const schemasDir = join(targetDir, "supabase/schemas/declarative");
|
|
@@ -20346,7 +20547,15 @@ ${content}`;
|
|
|
20346
20547
|
displayCheckModeResults(planOutput);
|
|
20347
20548
|
return { sql: planOutput, hazards, applied: false };
|
|
20348
20549
|
}
|
|
20349
|
-
return applyWithLockAndRetry(
|
|
20550
|
+
return applyWithLockAndRetry(
|
|
20551
|
+
dbUrl,
|
|
20552
|
+
schemasDir,
|
|
20553
|
+
includeSchemas,
|
|
20554
|
+
input3,
|
|
20555
|
+
planOutput,
|
|
20556
|
+
hazards,
|
|
20557
|
+
shadowDb?.dsn
|
|
20558
|
+
);
|
|
20350
20559
|
} finally {
|
|
20351
20560
|
if (shadowDb) {
|
|
20352
20561
|
try {
|
|
@@ -20458,6 +20667,11 @@ var DbApplyMetricsSchema = z.object({
|
|
|
20458
20667
|
z.object({
|
|
20459
20668
|
success: z.boolean(),
|
|
20460
20669
|
idempotentSchemasApplied: z.number(),
|
|
20670
|
+
/**
|
|
20671
|
+
* Number of idempotent schemas deferred during 1st pass (graceful skip).
|
|
20672
|
+
* These are retried in 2nd pass after declarative tables exist.
|
|
20673
|
+
*/
|
|
20674
|
+
idempotentSchemasSkipped: z.number().optional(),
|
|
20461
20675
|
/**
|
|
20462
20676
|
* Number of RBAC role passwords set (drizzle_app, drizzle_service).
|
|
20463
20677
|
* Set when DRIZZLE_APP_PASSWORD or DRIZZLE_SERVICE_PASSWORD env vars are present.
|
|
@@ -20578,7 +20792,9 @@ var dbApplyMachine = setup({
|
|
|
20578
20792
|
targetDir: input3.targetDir,
|
|
20579
20793
|
// 2-pass idempotent
|
|
20580
20794
|
idempotentPreApplied: 0,
|
|
20795
|
+
idempotentPreSkipped: 0,
|
|
20581
20796
|
idempotentPostApplied: 0,
|
|
20797
|
+
idempotentPostSkipped: 0,
|
|
20582
20798
|
rolePasswordsSet: 0,
|
|
20583
20799
|
schemaChangesApplied: false,
|
|
20584
20800
|
hazards: [],
|
|
@@ -20609,11 +20825,16 @@ var dbApplyMachine = setup({
|
|
|
20609
20825
|
entry: assign({ idempotentPreStartTime: () => Date.now() }),
|
|
20610
20826
|
invoke: {
|
|
20611
20827
|
src: "applyIdempotentSchemas",
|
|
20612
|
-
input: ({ context }) => ({
|
|
20828
|
+
input: ({ context }) => ({
|
|
20829
|
+
input: context.input,
|
|
20830
|
+
targetDir: context.targetDir,
|
|
20831
|
+
pass: "pre"
|
|
20832
|
+
}),
|
|
20613
20833
|
onDone: {
|
|
20614
20834
|
target: "applyingPgSchemaDiff",
|
|
20615
20835
|
actions: assign({
|
|
20616
20836
|
idempotentPreApplied: ({ event }) => event.output.filesApplied,
|
|
20837
|
+
idempotentPreSkipped: ({ event }) => event.output.filesSkipped,
|
|
20617
20838
|
rolePasswordsSet: ({ event }) => event.output.rolePasswordsSet,
|
|
20618
20839
|
idempotentPreEndTime: () => Date.now()
|
|
20619
20840
|
})
|
|
@@ -20672,11 +20893,16 @@ var dbApplyMachine = setup({
|
|
|
20672
20893
|
entry: assign({ idempotentPostStartTime: () => Date.now() }),
|
|
20673
20894
|
invoke: {
|
|
20674
20895
|
src: "applyIdempotentSchemas",
|
|
20675
|
-
input: ({ context }) => ({
|
|
20896
|
+
input: ({ context }) => ({
|
|
20897
|
+
input: context.input,
|
|
20898
|
+
targetDir: context.targetDir,
|
|
20899
|
+
pass: "post"
|
|
20900
|
+
}),
|
|
20676
20901
|
onDone: {
|
|
20677
20902
|
target: "applyingSeeds",
|
|
20678
20903
|
actions: assign({
|
|
20679
20904
|
idempotentPostApplied: ({ event }) => event.output.filesApplied,
|
|
20905
|
+
idempotentPostSkipped: ({ event }) => event.output.filesSkipped,
|
|
20680
20906
|
idempotentPostEndTime: () => Date.now()
|
|
20681
20907
|
})
|
|
20682
20908
|
},
|
|
@@ -20734,9 +20960,11 @@ var dbApplyMachine = setup({
|
|
|
20734
20960
|
retryAttempts: context.retryAttempts > 0 ? context.retryAttempts : void 0
|
|
20735
20961
|
};
|
|
20736
20962
|
const totalIdempotentApplied = context.idempotentPreApplied + context.idempotentPostApplied;
|
|
20963
|
+
const totalIdempotentSkipped = context.idempotentPreSkipped + context.idempotentPostSkipped;
|
|
20737
20964
|
return {
|
|
20738
20965
|
success: !context.error,
|
|
20739
20966
|
idempotentSchemasApplied: totalIdempotentApplied,
|
|
20967
|
+
idempotentSchemasSkipped: totalIdempotentSkipped > 0 ? totalIdempotentSkipped : void 0,
|
|
20740
20968
|
rolePasswordsSet: context.rolePasswordsSet > 0 ? context.rolePasswordsSet : void 0,
|
|
20741
20969
|
schemaChangesApplied: context.schemaChangesApplied,
|
|
20742
20970
|
hazards: context.hazards,
|
|
@@ -26393,7 +26621,7 @@ function getTablesWithTimestamps(dbUrl, schemas) {
|
|
|
26393
26621
|
JOIN pg_attribute a1 ON c.oid = a1.attrelid AND a1.attname = 'updated_at'
|
|
26394
26622
|
JOIN pg_attribute a2 ON c.oid = a2.attrelid AND a2.attname = 'created_at'
|
|
26395
26623
|
WHERE n.nspname IN (${schemasFilter})
|
|
26396
|
-
AND c.relkind
|
|
26624
|
+
AND c.relkind IN ('r', 'p')
|
|
26397
26625
|
ORDER BY n.nspname, c.relname;
|
|
26398
26626
|
`;
|
|
26399
26627
|
const conn = parsePostgresUrl(dbUrl);
|
|
@@ -31209,7 +31437,7 @@ init_esm_shims();
|
|
|
31209
31437
|
|
|
31210
31438
|
// src/constants/versions.ts
|
|
31211
31439
|
init_esm_shims();
|
|
31212
|
-
var COMPATIBLE_TEMPLATES_VERSION = "0.5.
|
|
31440
|
+
var COMPATIBLE_TEMPLATES_VERSION = "0.5.60";
|
|
31213
31441
|
var TEMPLATES_PACKAGE_NAME = "@r06-dev/runa-templates";
|
|
31214
31442
|
var GITHUB_PACKAGES_REGISTRY = "https://npm.pkg.github.com";
|
|
31215
31443
|
|
|
@@ -35800,13 +36028,13 @@ function printActionsNeeded(logger16, actions) {
|
|
|
35800
36028
|
}
|
|
35801
36029
|
function findRepoRoot3(startDir) {
|
|
35802
36030
|
const { existsSync: existsSync53, readFileSync: readFileSync30 } = __require("fs");
|
|
35803
|
-
const { join:
|
|
36031
|
+
const { join: join24, dirname: dirname5 } = __require("path");
|
|
35804
36032
|
let current = startDir;
|
|
35805
36033
|
while (current !== dirname5(current)) {
|
|
35806
|
-
if (existsSync53(
|
|
36034
|
+
if (existsSync53(join24(current, "turbo.json"))) {
|
|
35807
36035
|
return current;
|
|
35808
36036
|
}
|
|
35809
|
-
const pkgPath =
|
|
36037
|
+
const pkgPath = join24(current, "package.json");
|
|
35810
36038
|
if (existsSync53(pkgPath)) {
|
|
35811
36039
|
try {
|
|
35812
36040
|
const pkg = JSON.parse(readFileSync30(pkgPath, "utf-8"));
|
|
@@ -35876,8 +36104,8 @@ function generateReportOutput(output3, isJsonMode) {
|
|
|
35876
36104
|
}
|
|
35877
36105
|
function validateRunaRepo(repoRoot) {
|
|
35878
36106
|
const { existsSync: existsSync53 } = __require("fs");
|
|
35879
|
-
const { join:
|
|
35880
|
-
const templateDir =
|
|
36107
|
+
const { join: join24 } = __require("path");
|
|
36108
|
+
const templateDir = join24(repoRoot, "packages/runa-templates/templates");
|
|
35881
36109
|
if (!existsSync53(templateDir)) {
|
|
35882
36110
|
throw new CLIError("template-check is a runa-repo only command", "NOT_RUNA_REPO", [
|
|
35883
36111
|
"This command compares runa-repo with pj-repo templates",
|
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.60",
|
|
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.28.0",
|
|
55
55
|
"zod": "4.3.6",
|
|
56
|
-
"@runa-ai/runa": "0.5.
|
|
56
|
+
"@runa-ai/runa": "0.5.60",
|
|
57
57
|
"@runa-ai/runa-xstate-test-plugin": "0.5.58"
|
|
58
58
|
},
|
|
59
59
|
"engines": {
|