@tokenfactory/acc-runner 0.44.8 → 0.44.9
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/README.md +38 -0
- package/dist/attachments/codex-stage.d.ts +11 -0
- package/dist/attachments/codex-stage.d.ts.map +1 -0
- package/dist/attachments/codex-stage.js +82 -0
- package/dist/attachments/codex-stage.js.map +1 -0
- package/dist/attachments/fetch-attachment.d.ts +17 -0
- package/dist/attachments/fetch-attachment.d.ts.map +1 -1
- package/dist/attachments/fetch-attachment.js +134 -6
- package/dist/attachments/fetch-attachment.js.map +1 -1
- package/dist/attachments/index.d.ts +2 -1
- package/dist/attachments/index.d.ts.map +1 -1
- package/dist/attachments/index.js +2 -1
- package/dist/attachments/index.js.map +1 -1
- package/dist/build-info.generated.d.ts +1 -1
- package/dist/build-info.generated.js +2 -2
- package/dist/commit-sha.d.ts +31 -0
- package/dist/commit-sha.d.ts.map +1 -0
- package/dist/commit-sha.js +31 -0
- package/dist/commit-sha.js.map +1 -0
- package/dist/companion-run.d.ts.map +1 -1
- package/dist/companion-run.js +14 -1
- package/dist/companion-run.js.map +1 -1
- package/dist/doctor.d.ts +16 -0
- package/dist/doctor.d.ts.map +1 -1
- package/dist/doctor.js +107 -0
- package/dist/doctor.js.map +1 -1
- package/dist/engines/codex-chat.d.ts +10 -1
- package/dist/engines/codex-chat.d.ts.map +1 -1
- package/dist/engines/codex-chat.js +32 -2
- package/dist/engines/codex-chat.js.map +1 -1
- package/dist/login.d.ts.map +1 -1
- package/dist/login.js +7 -1
- package/dist/login.js.map +1 -1
- package/dist/machine-console/index.d.ts +6 -10
- package/dist/machine-console/index.d.ts.map +1 -1
- package/dist/machine-console/index.js +61 -54
- package/dist/machine-console/index.js.map +1 -1
- package/dist/machine-console/transport.d.ts +57 -0
- package/dist/machine-console/transport.d.ts.map +1 -0
- package/dist/machine-console/transport.js +170 -0
- package/dist/machine-console/transport.js.map +1 -0
- package/dist/preview-environments/control-plane.d.ts +11 -0
- package/dist/preview-environments/control-plane.d.ts.map +1 -0
- package/dist/preview-environments/control-plane.js +49 -0
- package/dist/preview-environments/control-plane.js.map +1 -0
- package/dist/preview-environments/http-provider.d.ts +13 -0
- package/dist/preview-environments/http-provider.d.ts.map +1 -0
- package/dist/preview-environments/http-provider.js +64 -0
- package/dist/preview-environments/http-provider.js.map +1 -0
- package/dist/preview-environments/profiles.d.ts +6 -0
- package/dist/preview-environments/profiles.d.ts.map +1 -0
- package/dist/preview-environments/profiles.js +34 -0
- package/dist/preview-environments/profiles.js.map +1 -0
- package/dist/preview-environments/safety.d.ts +7 -0
- package/dist/preview-environments/safety.d.ts.map +1 -0
- package/dist/preview-environments/safety.js +47 -0
- package/dist/preview-environments/safety.js.map +1 -0
- package/dist/preview-environments/service.d.ts +36 -0
- package/dist/preview-environments/service.d.ts.map +1 -0
- package/dist/preview-environments/service.js +149 -0
- package/dist/preview-environments/service.js.map +1 -0
- package/dist/preview-environments/types.d.ts +60 -0
- package/dist/preview-environments/types.d.ts.map +1 -0
- package/dist/preview-environments/types.js +2 -0
- package/dist/preview-environments/types.js.map +1 -0
- package/dist/production-migrations/command.d.ts +3 -0
- package/dist/production-migrations/command.d.ts.map +1 -0
- package/dist/production-migrations/command.js +21 -0
- package/dist/production-migrations/command.js.map +1 -0
- package/dist/production-migrations/executor.d.ts +59 -0
- package/dist/production-migrations/executor.d.ts.map +1 -0
- package/dist/production-migrations/executor.js +161 -0
- package/dist/production-migrations/executor.js.map +1 -0
- package/dist/production-migrations/pg-target.d.ts +22 -0
- package/dist/production-migrations/pg-target.d.ts.map +1 -0
- package/dist/production-migrations/pg-target.js +87 -0
- package/dist/production-migrations/pg-target.js.map +1 -0
- package/dist/production-migrations/profiles.d.ts +5 -0
- package/dist/production-migrations/profiles.d.ts.map +1 -0
- package/dist/production-migrations/profiles.js +39 -0
- package/dist/production-migrations/profiles.js.map +1 -0
- package/dist/production-migrations/service.d.ts +39 -0
- package/dist/production-migrations/service.d.ts.map +1 -0
- package/dist/production-migrations/service.js +166 -0
- package/dist/production-migrations/service.js.map +1 -0
- package/dist/program.d.ts.map +1 -1
- package/dist/program.js +17 -0
- package/dist/program.js.map +1 -1
- package/dist/runtime/worktree.d.ts.map +1 -1
- package/dist/runtime/worktree.js +23 -1
- package/dist/runtime/worktree.js.map +1 -1
- package/dist/secure-execution/auto-lease.d.ts +57 -0
- package/dist/secure-execution/auto-lease.d.ts.map +1 -0
- package/dist/secure-execution/auto-lease.js +116 -0
- package/dist/secure-execution/auto-lease.js.map +1 -0
- package/dist/secure-execution/command-policy.d.ts +5 -0
- package/dist/secure-execution/command-policy.d.ts.map +1 -0
- package/dist/secure-execution/command-policy.js +95 -0
- package/dist/secure-execution/command-policy.js.map +1 -0
- package/dist/secure-execution/confinement.d.ts +3 -0
- package/dist/secure-execution/confinement.d.ts.map +1 -0
- package/dist/secure-execution/confinement.js +18 -0
- package/dist/secure-execution/confinement.js.map +1 -0
- package/dist/secure-execution/engine.d.ts +24 -0
- package/dist/secure-execution/engine.d.ts.map +1 -0
- package/dist/secure-execution/engine.js +272 -0
- package/dist/secure-execution/engine.js.map +1 -0
- package/dist/secure-execution/index.d.ts +7 -0
- package/dist/secure-execution/index.d.ts.map +1 -0
- package/dist/secure-execution/index.js +6 -0
- package/dist/secure-execution/index.js.map +1 -0
- package/dist/secure-execution/node-pty.d.ts +11 -0
- package/dist/secure-execution/node-pty.d.ts.map +1 -0
- package/dist/secure-execution/node-pty.js +27 -0
- package/dist/secure-execution/node-pty.js.map +1 -0
- package/dist/secure-execution/types.d.ts +176 -0
- package/dist/secure-execution/types.d.ts.map +1 -0
- package/dist/secure-execution/types.js +2 -0
- package/dist/secure-execution/types.js.map +1 -0
- package/dist/staging-migrations/command.d.ts +2 -0
- package/dist/staging-migrations/command.d.ts.map +1 -0
- package/dist/staging-migrations/command.js +58 -0
- package/dist/staging-migrations/command.js.map +1 -0
- package/dist/staging-migrations/executor.d.ts +49 -0
- package/dist/staging-migrations/executor.d.ts.map +1 -0
- package/dist/staging-migrations/executor.js +125 -0
- package/dist/staging-migrations/executor.js.map +1 -0
- package/dist/staging-migrations/pg-target.d.ts +20 -0
- package/dist/staging-migrations/pg-target.d.ts.map +1 -0
- package/dist/staging-migrations/pg-target.js +63 -0
- package/dist/staging-migrations/pg-target.js.map +1 -0
- package/dist/staging-migrations/profiles.d.ts +5 -0
- package/dist/staging-migrations/profiles.d.ts.map +1 -0
- package/dist/staging-migrations/profiles.js +31 -0
- package/dist/staging-migrations/profiles.js.map +1 -0
- package/dist/staging-migrations/service.d.ts +40 -0
- package/dist/staging-migrations/service.d.ts.map +1 -0
- package/dist/staging-migrations/service.js +157 -0
- package/dist/staging-migrations/service.js.map +1 -0
- package/dist/supervisor.d.ts +15 -0
- package/dist/supervisor.d.ts.map +1 -0
- package/dist/supervisor.js +54 -0
- package/dist/supervisor.js.map +1 -0
- package/dist/terminal/http-carrier.d.ts +17 -0
- package/dist/terminal/http-carrier.d.ts.map +1 -0
- package/dist/terminal/http-carrier.js +39 -0
- package/dist/terminal/http-carrier.js.map +1 -0
- package/dist/terminal/pty-adapter.d.ts +88 -0
- package/dist/terminal/pty-adapter.d.ts.map +1 -0
- package/dist/terminal/pty-adapter.js +252 -0
- package/dist/terminal/pty-adapter.js.map +1 -0
- package/dist/terminal/transport-contract.d.ts +115 -0
- package/dist/terminal/transport-contract.d.ts.map +1 -0
- package/dist/terminal/transport-contract.js +2 -0
- package/dist/terminal/transport-contract.js.map +1 -0
- package/dist/terminal/transport.d.ts +32 -0
- package/dist/terminal/transport.d.ts.map +1 -0
- package/dist/terminal/transport.js +208 -0
- package/dist/terminal/transport.js.map +1 -0
- package/dist/watch-chat/wire.d.ts.map +1 -1
- package/dist/watch-chat/wire.js +14 -1
- package/dist/watch-chat/wire.js.map +1 -1
- package/dist/watch.d.ts.map +1 -1
- package/dist/watch.js +32 -0
- package/dist/watch.js.map +1 -1
- package/package.json +6 -1
- package/scripts/com.tokenfactory.acc-runner-dedicated.plist +64 -0
- package/scripts/com.tokenfactory.acc-runner.plist +90 -0
- package/scripts/gen-build-info.d.mts +3 -0
- package/scripts/gen-build-info.mjs +72 -0
- package/scripts/install-launchd.sh +235 -0
- package/scripts/setup-dedicated-runner-darwin.sh +75 -0
- package/scripts/setup-dedicated-runner-linux.sh +173 -0
- package/scripts/uninstall-launchd.sh +67 -0
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
export declare const PRODUCTION_PROJECT_REF = "ctknjrtevjrzwpuojghx";
|
|
2
|
+
export interface ProductionMigrationItem {
|
|
3
|
+
ordinal: number;
|
|
4
|
+
seq: string;
|
|
5
|
+
slug: string;
|
|
6
|
+
path: string;
|
|
7
|
+
sha: string;
|
|
8
|
+
staging_status: "applied" | "skipped";
|
|
9
|
+
status: "pending" | "skipped" | "applied" | "failed";
|
|
10
|
+
}
|
|
11
|
+
export interface ProductionMigrationPromotion {
|
|
12
|
+
id: string;
|
|
13
|
+
repo: string;
|
|
14
|
+
commit_sha: string;
|
|
15
|
+
plan_hash: string;
|
|
16
|
+
production_project_ref: string;
|
|
17
|
+
production_credential_profile_ref: string;
|
|
18
|
+
staging_ledger_snapshot_sha: string;
|
|
19
|
+
analyzer_classification: "safe" | "review_required" | "blocked";
|
|
20
|
+
items: ProductionMigrationItem[];
|
|
21
|
+
}
|
|
22
|
+
export interface ProductionTarget {
|
|
23
|
+
projectRef(): Promise<string>;
|
|
24
|
+
tryAdvisoryLock(key: string): Promise<boolean>;
|
|
25
|
+
unlockAdvisory(key: string): Promise<void>;
|
|
26
|
+
ledgerHead(): Promise<string | null>;
|
|
27
|
+
ledger(seq: string): Promise<{
|
|
28
|
+
slug: string;
|
|
29
|
+
sha: string;
|
|
30
|
+
} | null>;
|
|
31
|
+
applyFile(file: string, signal: AbortSignal): Promise<void>;
|
|
32
|
+
}
|
|
33
|
+
export interface SignedReceipt {
|
|
34
|
+
receiptId: string;
|
|
35
|
+
digest: string;
|
|
36
|
+
signature: string;
|
|
37
|
+
keyId: string;
|
|
38
|
+
}
|
|
39
|
+
export interface ProductionReceiptSink {
|
|
40
|
+
item(ordinal: number, status: "skipped" | "applied" | "failed", errorCode?: string): Promise<{
|
|
41
|
+
receipt_id: string;
|
|
42
|
+
}>;
|
|
43
|
+
complete(succeeded: boolean, failureCode?: string): Promise<{
|
|
44
|
+
receipt_id: string;
|
|
45
|
+
}>;
|
|
46
|
+
signed(receipt: SignedReceipt): Promise<void>;
|
|
47
|
+
}
|
|
48
|
+
export interface ProductionExecutionOptions {
|
|
49
|
+
timeoutMs?: number;
|
|
50
|
+
cancelled?: () => Promise<boolean>;
|
|
51
|
+
receiptKey: string;
|
|
52
|
+
receiptKeyId: string;
|
|
53
|
+
}
|
|
54
|
+
export declare function productionLedgerSnapshot(items: ProductionMigrationItem[]): string;
|
|
55
|
+
export declare function signReceipt(promotionId: string, action: string, receiptId: string, key: string, keyId: string): SignedReceipt;
|
|
56
|
+
export declare function verifySignedReceipt(receipt: SignedReceipt, key: string): boolean;
|
|
57
|
+
/** Exact-file, fail-closed production executor. Applied ledger rows make crash recovery idempotent. */
|
|
58
|
+
export declare function executeProductionPromotion(promotion: ProductionMigrationPromotion, repoRoot: string, target: ProductionTarget, receipts: ProductionReceiptSink, options: ProductionExecutionOptions): Promise<void>;
|
|
59
|
+
//# sourceMappingURL=executor.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"executor.d.ts","sourceRoot":"","sources":["../../src/production-migrations/executor.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
import { createHash, createHmac, timingSafeEqual } from "node:crypto";
|
|
2
|
+
import { readFile } from "node:fs/promises";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import { isCommitSha, isSha256Digest } from "../commit-sha.js";
|
|
5
|
+
export const PRODUCTION_PROJECT_REF = "ctknjrtevjrzwpuojghx";
|
|
6
|
+
export function productionLedgerSnapshot(items) {
|
|
7
|
+
const value = items
|
|
8
|
+
.map((item) => `${item.ordinal}:${item.seq}:${item.slug}:${item.sha}:${item.staging_status}`)
|
|
9
|
+
.join("|");
|
|
10
|
+
return createHash("sha256").update(value).digest("hex");
|
|
11
|
+
}
|
|
12
|
+
export function signReceipt(promotionId, action, receiptId, key, keyId) {
|
|
13
|
+
if (key.length < 32 || !keyId)
|
|
14
|
+
throw new Error("production_receipt_signing_key_required");
|
|
15
|
+
const digest = createHash("sha256").update(`${promotionId}:${action}:${receiptId}`).digest("hex");
|
|
16
|
+
return {
|
|
17
|
+
receiptId,
|
|
18
|
+
digest,
|
|
19
|
+
signature: createHmac("sha256", key).update(digest).digest("hex"),
|
|
20
|
+
keyId,
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
export function verifySignedReceipt(receipt, key) {
|
|
24
|
+
const actual = Buffer.from(receipt.signature, "hex");
|
|
25
|
+
const expected = Buffer.from(createHmac("sha256", key).update(receipt.digest).digest("hex"), "hex");
|
|
26
|
+
return actual.length === expected.length && timingSafeEqual(actual, expected);
|
|
27
|
+
}
|
|
28
|
+
function assertPromotion(promotion) {
|
|
29
|
+
if (promotion.production_project_ref !== PRODUCTION_PROJECT_REF)
|
|
30
|
+
throw new Error("production_project_pin_invalid");
|
|
31
|
+
if (promotion.analyzer_classification !== "safe")
|
|
32
|
+
throw new Error("production_analyzer_not_safe");
|
|
33
|
+
if (!isCommitSha(promotion.commit_sha))
|
|
34
|
+
throw new Error("production_commit_invalid");
|
|
35
|
+
if (!isSha256Digest(promotion.plan_hash))
|
|
36
|
+
throw new Error("production_plan_hash_invalid");
|
|
37
|
+
if (promotion.items.length === 0)
|
|
38
|
+
throw new Error("production_plan_empty");
|
|
39
|
+
if (productionLedgerSnapshot(promotion.items) !== promotion.staging_ledger_snapshot_sha)
|
|
40
|
+
throw new Error("production_staging_snapshot_mismatch");
|
|
41
|
+
let previous = null;
|
|
42
|
+
for (const [index, item] of promotion.items.entries()) {
|
|
43
|
+
if (item.ordinal !== index ||
|
|
44
|
+
item.path !== `supabase/migrations/${item.seq}_${item.slug}.sql` ||
|
|
45
|
+
!/^[0-9]{4}$/.test(item.seq) ||
|
|
46
|
+
!isSha256Digest(item.sha))
|
|
47
|
+
throw new Error("production_plan_item_invalid");
|
|
48
|
+
if (previous !== null && Number(item.seq) !== previous + 1)
|
|
49
|
+
throw new Error("production_plan_not_contiguous");
|
|
50
|
+
previous = Number(item.seq);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
function insideRepo(repoRoot, migrationPath) {
|
|
54
|
+
const root = path.resolve(repoRoot);
|
|
55
|
+
const resolved = path.resolve(root, migrationPath);
|
|
56
|
+
if (!resolved.startsWith(`${root}${path.sep}`))
|
|
57
|
+
throw new Error("production_migration_path_escape");
|
|
58
|
+
return resolved;
|
|
59
|
+
}
|
|
60
|
+
function failureCode(error) {
|
|
61
|
+
const raw = error instanceof Error ? error.message : "production_migration_failed";
|
|
62
|
+
return raw
|
|
63
|
+
.replace(/postgres(?:ql)?:\/\/[^\s]+/gi, "[REDACTED_DB_URL]")
|
|
64
|
+
.replace(/(password|token|secret)=?[^\s]*/gi, "$1=[REDACTED]")
|
|
65
|
+
.replace(/[^a-zA-Z0-9_.:[\] -]/g, "_")
|
|
66
|
+
.slice(0, 200);
|
|
67
|
+
}
|
|
68
|
+
async function signed(promotion, action, result, receipts, options) {
|
|
69
|
+
if (!/^[0-9a-f-]{36}$/i.test(result.receipt_id))
|
|
70
|
+
throw new Error("production_receipt_invalid");
|
|
71
|
+
await receipts.signed(signReceipt(promotion.id, action, result.receipt_id, options.receiptKey, options.receiptKeyId));
|
|
72
|
+
}
|
|
73
|
+
/** Exact-file, fail-closed production executor. Applied ledger rows make crash recovery idempotent. */
|
|
74
|
+
export async function executeProductionPromotion(promotion, repoRoot, target, receipts, options) {
|
|
75
|
+
const lockKey = `acc:production-migrations:${PRODUCTION_PROJECT_REF}`;
|
|
76
|
+
const timeoutMs = Math.max(5_000, Math.min(options.timeoutMs ?? 300_000, 900_000));
|
|
77
|
+
let locked = false;
|
|
78
|
+
try {
|
|
79
|
+
assertPromotion(promotion);
|
|
80
|
+
if ((await target.projectRef()) !== PRODUCTION_PROJECT_REF)
|
|
81
|
+
throw new Error("production_project_pin_mismatch");
|
|
82
|
+
if (!(await target.tryAdvisoryLock(lockKey)))
|
|
83
|
+
throw new Error("production_migration_lock_busy");
|
|
84
|
+
locked = true;
|
|
85
|
+
const head = await target.ledgerHead();
|
|
86
|
+
const acceptable = new Set([
|
|
87
|
+
String(Number(promotion.items[0].seq) - 1).padStart(4, "0"),
|
|
88
|
+
...promotion.items.map((item) => item.seq),
|
|
89
|
+
]);
|
|
90
|
+
if (!head || !acceptable.has(head))
|
|
91
|
+
throw new Error("production_ledger_head_mismatch");
|
|
92
|
+
for (const item of promotion.items) {
|
|
93
|
+
if (await options.cancelled?.())
|
|
94
|
+
throw new Error("production_migration_cancelled");
|
|
95
|
+
if (item.status === "applied" || item.status === "skipped")
|
|
96
|
+
continue;
|
|
97
|
+
const existing = await target.ledger(item.seq);
|
|
98
|
+
if (existing) {
|
|
99
|
+
if (existing.slug !== item.slug || existing.sha !== item.sha)
|
|
100
|
+
throw new Error(`production_ledger_drift_${item.seq}`);
|
|
101
|
+
const result = await receipts.item(item.ordinal, "skipped");
|
|
102
|
+
await signed(promotion, `item:${item.ordinal}:skipped`, result, receipts, options);
|
|
103
|
+
continue;
|
|
104
|
+
}
|
|
105
|
+
const file = insideRepo(repoRoot, item.path);
|
|
106
|
+
const sql = await readFile(file);
|
|
107
|
+
if (createHash("sha256").update(sql).digest("hex") !== item.sha)
|
|
108
|
+
throw new Error(`production_file_hash_mismatch_${item.seq}`);
|
|
109
|
+
const controller = new AbortController();
|
|
110
|
+
let cancelled = false;
|
|
111
|
+
const timer = setTimeout(() => controller.abort(), timeoutMs);
|
|
112
|
+
const cancelPoll = options.cancelled
|
|
113
|
+
? setInterval(() => {
|
|
114
|
+
void options.cancelled?.().then((value) => {
|
|
115
|
+
if (value) {
|
|
116
|
+
cancelled = true;
|
|
117
|
+
controller.abort();
|
|
118
|
+
}
|
|
119
|
+
});
|
|
120
|
+
}, 250)
|
|
121
|
+
: null;
|
|
122
|
+
cancelPoll?.unref?.();
|
|
123
|
+
try {
|
|
124
|
+
await target.applyFile(file, controller.signal);
|
|
125
|
+
const recorded = await target.ledger(item.seq);
|
|
126
|
+
if (!recorded || recorded.slug !== item.slug || recorded.sha !== item.sha)
|
|
127
|
+
throw new Error(`production_ledger_receipt_missing_${item.seq}`);
|
|
128
|
+
const result = await receipts.item(item.ordinal, "applied");
|
|
129
|
+
await signed(promotion, `item:${item.ordinal}:applied`, result, receipts, options);
|
|
130
|
+
}
|
|
131
|
+
catch (error) {
|
|
132
|
+
const code = cancelled
|
|
133
|
+
? "production_migration_cancelled"
|
|
134
|
+
: controller.signal.aborted
|
|
135
|
+
? "production_migration_timeout"
|
|
136
|
+
: failureCode(error);
|
|
137
|
+
const result = await receipts.item(item.ordinal, "failed", code);
|
|
138
|
+
await signed(promotion, `item:${item.ordinal}:failed`, result, receipts, options);
|
|
139
|
+
throw new Error(code);
|
|
140
|
+
}
|
|
141
|
+
finally {
|
|
142
|
+
clearTimeout(timer);
|
|
143
|
+
if (cancelPoll)
|
|
144
|
+
clearInterval(cancelPoll);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
const result = await receipts.complete(true);
|
|
148
|
+
await signed(promotion, "complete:succeeded", result, receipts, options);
|
|
149
|
+
}
|
|
150
|
+
catch (error) {
|
|
151
|
+
const result = await receipts.complete(false, failureCode(error)).catch(() => null);
|
|
152
|
+
if (result)
|
|
153
|
+
await signed(promotion, "complete:failed", result, receipts, options).catch(() => undefined);
|
|
154
|
+
throw error;
|
|
155
|
+
}
|
|
156
|
+
finally {
|
|
157
|
+
if (locked)
|
|
158
|
+
await target.unlockAdvisory(lockKey).catch(() => undefined);
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
//# sourceMappingURL=executor.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"executor.js","sourceRoot":"","sources":["../../src/production-migrations/executor.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { type ProductionTarget } from "./executor.js";
|
|
2
|
+
export declare function productionCredentialEnvName(profile: string): string;
|
|
3
|
+
export declare function projectRefFromProductionUrl(value: string): string;
|
|
4
|
+
export declare class PgProductionTarget implements ProductionTarget {
|
|
5
|
+
private readonly env;
|
|
6
|
+
private readonly client;
|
|
7
|
+
private readonly connectionString;
|
|
8
|
+
private readonly pinnedRef;
|
|
9
|
+
constructor(profile: string, env?: NodeJS.ProcessEnv);
|
|
10
|
+
connect(): Promise<void>;
|
|
11
|
+
close(): Promise<void>;
|
|
12
|
+
projectRef(): Promise<string>;
|
|
13
|
+
tryAdvisoryLock(key: string): Promise<boolean>;
|
|
14
|
+
unlockAdvisory(key: string): Promise<void>;
|
|
15
|
+
ledger(seq: string): Promise<{
|
|
16
|
+
slug: string;
|
|
17
|
+
sha: string;
|
|
18
|
+
} | null>;
|
|
19
|
+
ledgerHead(): Promise<string | null>;
|
|
20
|
+
applyFile(file: string, signal: AbortSignal): Promise<void>;
|
|
21
|
+
}
|
|
22
|
+
//# sourceMappingURL=pg-target.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pg-target.d.ts","sourceRoot":"","sources":["../../src/production-migrations/pg-target.ts"],"names":[],"mappings":"AAGA,OAAO,EAA0B,KAAK,gBAAgB,EAAE,MAAM,eAAe,CAAC;AAI9E,wBAAgB,2BAA2B,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAInE;AAED,wBAAgB,2BAA2B,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAUjE;AAED,qBAAa,kBAAmB,YAAW,gBAAgB;IAOvD,OAAO,CAAC,QAAQ,CAAC,GAAG;IANtB,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;IAChC,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAS;IAC1C,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAS;gBAGjC,OAAO,EAAE,MAAM,EACE,GAAG,GAAE,MAAM,CAAC,UAAwB;IAcjD,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IAGxB,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAGtB,UAAU,IAAI,OAAO,CAAC,MAAM,CAAC;IAG7B,eAAe,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAO9C,cAAc,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAG1C,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC;IAOlE,UAAU,IAAI,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;IAMpC,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC;CAmBlE"}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import { execFile } from "node:child_process";
|
|
2
|
+
import { promisify } from "node:util";
|
|
3
|
+
import { Client } from "pg";
|
|
4
|
+
import { PRODUCTION_PROJECT_REF } from "./executor.js";
|
|
5
|
+
const execFileAsync = promisify(execFile);
|
|
6
|
+
export function productionCredentialEnvName(profile) {
|
|
7
|
+
if (!/^[A-Za-z0-9_.-]{1,80}$/.test(profile))
|
|
8
|
+
throw new Error("production_credential_profile_invalid");
|
|
9
|
+
return `ACC_DB_PROFILE_${profile.toUpperCase().replace(/[^A-Z0-9]/g, "_")}_URL`;
|
|
10
|
+
}
|
|
11
|
+
export function projectRefFromProductionUrl(value) {
|
|
12
|
+
let parsed;
|
|
13
|
+
try {
|
|
14
|
+
parsed = new URL(value);
|
|
15
|
+
}
|
|
16
|
+
catch {
|
|
17
|
+
throw new Error("production_credential_url_invalid");
|
|
18
|
+
}
|
|
19
|
+
const ref = `${parsed.hostname}${parsed.pathname}`.match(/\b([a-z0-9]{20})\b/)?.[1];
|
|
20
|
+
if (!ref)
|
|
21
|
+
throw new Error("production_credential_project_ref_missing");
|
|
22
|
+
return ref;
|
|
23
|
+
}
|
|
24
|
+
export class PgProductionTarget {
|
|
25
|
+
env;
|
|
26
|
+
client;
|
|
27
|
+
connectionString;
|
|
28
|
+
pinnedRef;
|
|
29
|
+
constructor(profile, env = process.env) {
|
|
30
|
+
this.env = env;
|
|
31
|
+
const value = env[productionCredentialEnvName(profile)];
|
|
32
|
+
if (!value)
|
|
33
|
+
throw new Error("production_credential_profile_not_configured");
|
|
34
|
+
this.pinnedRef = projectRefFromProductionUrl(value);
|
|
35
|
+
if (this.pinnedRef !== PRODUCTION_PROJECT_REF)
|
|
36
|
+
throw new Error("non_production_target_forbidden");
|
|
37
|
+
this.connectionString = value;
|
|
38
|
+
this.client = new Client({
|
|
39
|
+
connectionString: value,
|
|
40
|
+
application_name: "acc-production-migration-executor",
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
async connect() {
|
|
44
|
+
await this.client.connect();
|
|
45
|
+
}
|
|
46
|
+
async close() {
|
|
47
|
+
await this.client.end();
|
|
48
|
+
}
|
|
49
|
+
async projectRef() {
|
|
50
|
+
return this.pinnedRef;
|
|
51
|
+
}
|
|
52
|
+
async tryAdvisoryLock(key) {
|
|
53
|
+
const result = await this.client.query("select pg_try_advisory_lock(hashtextextended($1, 0)) as locked", [key]);
|
|
54
|
+
return result.rows[0]?.locked === true;
|
|
55
|
+
}
|
|
56
|
+
async unlockAdvisory(key) {
|
|
57
|
+
await this.client.query("select pg_advisory_unlock(hashtextextended($1, 0))", [key]);
|
|
58
|
+
}
|
|
59
|
+
async ledger(seq) {
|
|
60
|
+
const result = await this.client.query("select slug, sha from acc.applied_migrations where seq = $1", [seq]);
|
|
61
|
+
return result.rows[0] ?? null;
|
|
62
|
+
}
|
|
63
|
+
async ledgerHead() {
|
|
64
|
+
const result = await this.client.query("select seq from acc.applied_migrations order by seq::integer desc limit 1");
|
|
65
|
+
return result.rows[0]?.seq ?? null;
|
|
66
|
+
}
|
|
67
|
+
async applyFile(file, signal) {
|
|
68
|
+
// Do not leak the runner/token/signing environment into psql. The opaque
|
|
69
|
+
// profile resolves locally and only the minimum process environment crosses
|
|
70
|
+
// the child boundary; the connection string is never placed in argv.
|
|
71
|
+
const childEnv = {
|
|
72
|
+
PATH: this.env.PATH,
|
|
73
|
+
HOME: this.env.HOME,
|
|
74
|
+
LANG: this.env.LANG,
|
|
75
|
+
LC_ALL: this.env.LC_ALL,
|
|
76
|
+
SSL_CERT_FILE: this.env.SSL_CERT_FILE,
|
|
77
|
+
PGCONNECT_TIMEOUT: this.env.PGCONNECT_TIMEOUT ?? "10",
|
|
78
|
+
PGDATABASE: this.connectionString,
|
|
79
|
+
};
|
|
80
|
+
await execFileAsync("psql", ["-X", "--set", "ON_ERROR_STOP=1", "--file", file], {
|
|
81
|
+
env: childEnv,
|
|
82
|
+
signal,
|
|
83
|
+
maxBuffer: 256 * 1024,
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
//# sourceMappingURL=pg-target.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pg-target.js","sourceRoot":"","sources":["../../src/production-migrations/pg-target.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export declare function configuredProductionMigrationProfiles(env?: NodeJS.ProcessEnv): string[];
|
|
2
|
+
export declare function configuredProductionPromotionIds(env?: NodeJS.ProcessEnv): string[];
|
|
3
|
+
export declare function productionMigrationServiceEnabled(env?: NodeJS.ProcessEnv): boolean;
|
|
4
|
+
export declare function productionMigrationCaps(env?: NodeJS.ProcessEnv): Record<string, unknown>;
|
|
5
|
+
//# sourceMappingURL=profiles.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"profiles.d.ts","sourceRoot":"","sources":["../../src/production-migrations/profiles.ts"],"names":[],"mappings":"AAGA,wBAAgB,qCAAqC,CACnD,GAAG,GAAE,MAAM,CAAC,UAAwB,GACnC,MAAM,EAAE,CAaV;AAED,wBAAgB,gCAAgC,CAAC,GAAG,GAAE,MAAM,CAAC,UAAwB,GAAG,MAAM,EAAE,CAS/F;AAED,wBAAgB,iCAAiC,CAAC,GAAG,GAAE,MAAM,CAAC,UAAwB,GAAG,OAAO,CAQ/F;AAED,wBAAgB,uBAAuB,CACrC,GAAG,GAAE,MAAM,CAAC,UAAwB,GACnC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAIzB"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { PRODUCTION_PROJECT_REF } from "./executor.js";
|
|
2
|
+
import { projectRefFromProductionUrl } from "./pg-target.js";
|
|
3
|
+
export function configuredProductionMigrationProfiles(env = process.env) {
|
|
4
|
+
const profiles = [];
|
|
5
|
+
for (const [name, value] of Object.entries(env)) {
|
|
6
|
+
const match = /^ACC_DB_PROFILE_([A-Z0-9_]+)_URL$/.exec(name);
|
|
7
|
+
if (!match || !value)
|
|
8
|
+
continue;
|
|
9
|
+
try {
|
|
10
|
+
if (projectRefFromProductionUrl(value) === PRODUCTION_PROJECT_REF)
|
|
11
|
+
profiles.push(match[1].toLowerCase());
|
|
12
|
+
}
|
|
13
|
+
catch {
|
|
14
|
+
/* malformed and non-production credentials are never advertised */
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
return profiles.sort();
|
|
18
|
+
}
|
|
19
|
+
export function configuredProductionPromotionIds(env = process.env) {
|
|
20
|
+
return [
|
|
21
|
+
...new Set((env.ACC_PRODUCTION_MIGRATION_PROMOTION_IDS ?? "")
|
|
22
|
+
.split(",")
|
|
23
|
+
.map((value) => value.trim())
|
|
24
|
+
.filter((value) => /^[0-9a-f-]{36}$/i.test(value))),
|
|
25
|
+
];
|
|
26
|
+
}
|
|
27
|
+
export function productionMigrationServiceEnabled(env = process.env) {
|
|
28
|
+
return (env.ACC_PRODUCTION_MIGRATION_SERVICE === "1" &&
|
|
29
|
+
configuredProductionMigrationProfiles(env).length > 0 &&
|
|
30
|
+
configuredProductionPromotionIds(env).length > 0 &&
|
|
31
|
+
(env.ACC_PRODUCTION_MIGRATION_RECEIPT_KEY?.length ?? 0) >= 32 &&
|
|
32
|
+
Boolean(env.ACC_PRODUCTION_MIGRATION_RECEIPT_KEY_ID));
|
|
33
|
+
}
|
|
34
|
+
export function productionMigrationCaps(env = process.env) {
|
|
35
|
+
return configuredProductionMigrationProfiles(env).length > 0
|
|
36
|
+
? { production_migrations: true, production_project_ref: PRODUCTION_PROJECT_REF }
|
|
37
|
+
: {};
|
|
38
|
+
}
|
|
39
|
+
//# sourceMappingURL=profiles.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"profiles.js","sourceRoot":"","sources":["../../src/production-migrations/profiles.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,sBAAsB,EAAE,MAAM,eAAe,CAAC;AACvD,OAAO,EAAE,2BAA2B,EAAE,MAAM,gBAAgB,CAAC;AAE7D,MAAM,UAAU,qCAAqC,CACnD,MAAyB,OAAO,CAAC,GAAG;IAEpC,MAAM,QAAQ,GAAa,EAAE,CAAC;IAC9B,KAAK,MAAM,CAAC,IAAI,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC;QAChD,MAAM,KAAK,GAAG,mCAAmC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC7D,IAAI,CAAC,KAAK,IAAI,CAAC,KAAK;YAAE,SAAS;QAC/B,IAAI,CAAC;YACH,IAAI,2BAA2B,CAAC,KAAK,CAAC,KAAK,sBAAsB;gBAC/D,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC,CAAC;QAC1C,CAAC;QAAC,MAAM,CAAC;YACP,mEAAmE;QACrE,CAAC;IACH,CAAC;IACD,OAAO,QAAQ,CAAC,IAAI,EAAE,CAAC;AACzB,CAAC;AAED,MAAM,UAAU,gCAAgC,CAAC,MAAyB,OAAO,CAAC,GAAG;IACnF,OAAO;QACL,GAAG,IAAI,GAAG,CACR,CAAC,GAAG,CAAC,sCAAsC,IAAI,EAAE,CAAC;aAC/C,KAAK,CAAC,GAAG,CAAC;aACV,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC;aAC5B,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,kBAAkB,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CACrD;KACF,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,iCAAiC,CAAC,MAAyB,OAAO,CAAC,GAAG;IACpF,OAAO,CACL,GAAG,CAAC,gCAAgC,KAAK,GAAG;QAC5C,qCAAqC,CAAC,GAAG,CAAC,CAAC,MAAM,GAAG,CAAC;QACrD,gCAAgC,CAAC,GAAG,CAAC,CAAC,MAAM,GAAG,CAAC;QAChD,CAAC,GAAG,CAAC,oCAAoC,EAAE,MAAM,IAAI,CAAC,CAAC,IAAI,EAAE;QAC7D,OAAO,CAAC,GAAG,CAAC,uCAAuC,CAAC,CACrD,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,uBAAuB,CACrC,MAAyB,OAAO,CAAC,GAAG;IAEpC,OAAO,qCAAqC,CAAC,GAAG,CAAC,CAAC,MAAM,GAAG,CAAC;QAC1D,CAAC,CAAC,EAAE,qBAAqB,EAAE,IAAI,EAAE,sBAAsB,EAAE,sBAAsB,EAAE;QACjF,CAAC,CAAC,EAAE,CAAC;AACT,CAAC"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import type { RunnerConfig } from "../config.js";
|
|
2
|
+
import type { RunnerSupabaseClient } from "../supabase.js";
|
|
3
|
+
import { type ProductionTarget } from "./executor.js";
|
|
4
|
+
export interface ProductionMigrationLogger {
|
|
5
|
+
info(message: string): void;
|
|
6
|
+
error(message: string): void;
|
|
7
|
+
}
|
|
8
|
+
export interface ProductionMigrationServiceOptions {
|
|
9
|
+
env?: NodeJS.ProcessEnv;
|
|
10
|
+
pollMs?: number;
|
|
11
|
+
logger?: ProductionMigrationLogger;
|
|
12
|
+
resolveRepo?: (repo: string) => string;
|
|
13
|
+
verifyCommit?: (root: string, sha: string) => Promise<void>;
|
|
14
|
+
targetFactory?: (profile: string) => ProductionTarget & {
|
|
15
|
+
connect(): Promise<void>;
|
|
16
|
+
close(): Promise<void>;
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
export declare function productionSafeCode(error: unknown): string;
|
|
20
|
+
export declare function verifyProductionCheckout(root: string, sha: string): Promise<void>;
|
|
21
|
+
export declare class ProductionMigrationService {
|
|
22
|
+
private readonly sb;
|
|
23
|
+
private readonly cfg;
|
|
24
|
+
private readonly runnerId;
|
|
25
|
+
private readonly options;
|
|
26
|
+
private timer;
|
|
27
|
+
private active;
|
|
28
|
+
private stopping;
|
|
29
|
+
private readonly env;
|
|
30
|
+
private readonly logger;
|
|
31
|
+
private readonly pollMs;
|
|
32
|
+
constructor(sb: RunnerSupabaseClient, cfg: RunnerConfig, runnerId: string, options?: ProductionMigrationServiceOptions);
|
|
33
|
+
start(): void;
|
|
34
|
+
stop(): Promise<void>;
|
|
35
|
+
pollOnce(): Promise<void>;
|
|
36
|
+
claimAndRun(promotionId: string): Promise<"skipped" | "succeeded" | "failed">;
|
|
37
|
+
}
|
|
38
|
+
export { productionMigrationCaps, productionMigrationServiceEnabled } from "./profiles.js";
|
|
39
|
+
//# sourceMappingURL=service.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"service.d.ts","sourceRoot":"","sources":["../../src/production-migrations/service.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAEjD,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,gBAAgB,CAAC;AAC3D,OAAO,EAIL,KAAK,gBAAgB,EAEtB,MAAM,eAAe,CAAC;AAOvB,MAAM,WAAW,yBAAyB;IACxC,IAAI,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,KAAK,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;CAC9B;AAED,MAAM,WAAW,iCAAiC;IAChD,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,CAAC;IACxB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,yBAAyB,CAAC;IACnC,WAAW,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,CAAC;IACvC,YAAY,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC5D,aAAa,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,gBAAgB,GAAG;QACtD,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;QACzB,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;KACxB,CAAC;CACH;AASD,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAOzD;AAED,wBAAsB,wBAAwB,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAKvF;AAED,qBAAa,0BAA0B;IASnC,OAAO,CAAC,QAAQ,CAAC,EAAE;IACnB,OAAO,CAAC,QAAQ,CAAC,GAAG;IACpB,OAAO,CAAC,QAAQ,CAAC,QAAQ;IACzB,OAAO,CAAC,QAAQ,CAAC,OAAO;IAX1B,OAAO,CAAC,KAAK,CAA+B;IAC5C,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,QAAQ,CAAS;IACzB,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAoB;IACxC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAA4B;IACnD,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;gBAGb,EAAE,EAAE,oBAAoB,EACxB,GAAG,EAAE,YAAY,EACjB,QAAQ,EAAE,MAAM,EAChB,OAAO,GAAE,iCAAsC;IAOlE,KAAK,IAAI,IAAI;IAQP,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAUrB,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC;IAezB,WAAW,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,GAAG,WAAW,GAAG,QAAQ,CAAC;CAuFpF;AAED,OAAO,EAAE,uBAAuB,EAAE,iCAAiC,EAAE,MAAM,eAAe,CAAC"}
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
import { execFile } from "node:child_process";
|
|
2
|
+
import { promisify } from "node:util";
|
|
3
|
+
import { resolveMachineRepoPath } from "../machine-console/index.js";
|
|
4
|
+
import { executeProductionPromotion, } from "./executor.js";
|
|
5
|
+
import { PgProductionTarget } from "./pg-target.js";
|
|
6
|
+
import { configuredProductionPromotionIds } from "./profiles.js";
|
|
7
|
+
import { isCommitSha } from "../commit-sha.js";
|
|
8
|
+
const execFileAsync = promisify(execFile);
|
|
9
|
+
export function productionSafeCode(error) {
|
|
10
|
+
const raw = error instanceof Error ? error.message : "unknown_error";
|
|
11
|
+
return raw
|
|
12
|
+
.replace(/postgres(?:ql)?:\/\/[^\s]+/gi, "[REDACTED_DB_URL]")
|
|
13
|
+
.replace(/(password|token|secret)=?[^\s]*/gi, "$1=[REDACTED]")
|
|
14
|
+
.replace(/[^a-zA-Z0-9_.:[\] -]/g, "_")
|
|
15
|
+
.slice(0, 240);
|
|
16
|
+
}
|
|
17
|
+
export async function verifyProductionCheckout(root, sha) {
|
|
18
|
+
if (!isCommitSha(sha))
|
|
19
|
+
throw new Error("production_commit_invalid");
|
|
20
|
+
const { stdout } = await execFileAsync("git", ["rev-parse", "HEAD"], { cwd: root });
|
|
21
|
+
if (stdout.trim().toLowerCase() !== sha.toLowerCase())
|
|
22
|
+
throw new Error("production_commit_mismatch");
|
|
23
|
+
}
|
|
24
|
+
export class ProductionMigrationService {
|
|
25
|
+
sb;
|
|
26
|
+
cfg;
|
|
27
|
+
runnerId;
|
|
28
|
+
options;
|
|
29
|
+
timer = null;
|
|
30
|
+
active = false;
|
|
31
|
+
stopping = false;
|
|
32
|
+
env;
|
|
33
|
+
logger;
|
|
34
|
+
pollMs;
|
|
35
|
+
constructor(sb, cfg, runnerId, options = {}) {
|
|
36
|
+
this.sb = sb;
|
|
37
|
+
this.cfg = cfg;
|
|
38
|
+
this.runnerId = runnerId;
|
|
39
|
+
this.options = options;
|
|
40
|
+
this.env = options.env ?? process.env;
|
|
41
|
+
this.logger = options.logger ?? console;
|
|
42
|
+
this.pollMs = Math.max(5_000, Math.min(options.pollMs ?? 30_000, 300_000));
|
|
43
|
+
}
|
|
44
|
+
start() {
|
|
45
|
+
if (this.timer)
|
|
46
|
+
return;
|
|
47
|
+
this.stopping = false;
|
|
48
|
+
void this.pollOnce();
|
|
49
|
+
this.timer = setInterval(() => void this.pollOnce(), this.pollMs);
|
|
50
|
+
this.timer.unref?.();
|
|
51
|
+
}
|
|
52
|
+
async stop() {
|
|
53
|
+
this.stopping = true;
|
|
54
|
+
if (this.timer)
|
|
55
|
+
clearInterval(this.timer);
|
|
56
|
+
this.timer = null;
|
|
57
|
+
const deadline = Date.now() + 30_000;
|
|
58
|
+
while (this.active && Date.now() < deadline)
|
|
59
|
+
await new Promise((resolve) => setTimeout(resolve, 25));
|
|
60
|
+
if (this.active)
|
|
61
|
+
this.logger.error("[acc-runner] production migration shutdown_wait_exceeded");
|
|
62
|
+
}
|
|
63
|
+
async pollOnce() {
|
|
64
|
+
if (this.active || this.stopping)
|
|
65
|
+
return;
|
|
66
|
+
this.active = true;
|
|
67
|
+
try {
|
|
68
|
+
for (const promotionId of configuredProductionPromotionIds(this.env)) {
|
|
69
|
+
if (this.stopping)
|
|
70
|
+
break;
|
|
71
|
+
await this.claimAndRun(promotionId);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
catch (error) {
|
|
75
|
+
this.logger.error(`[acc-runner] production migration service: ${productionSafeCode(error)}`);
|
|
76
|
+
}
|
|
77
|
+
finally {
|
|
78
|
+
this.active = false;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
async claimAndRun(promotionId) {
|
|
82
|
+
const claimed = await this.sb.rpc("claim_production_migration_promotion", {
|
|
83
|
+
p_promotion_id: promotionId,
|
|
84
|
+
});
|
|
85
|
+
if (claimed.error) {
|
|
86
|
+
this.logger.info(`[acc-runner] production migration claim skipped: ${productionSafeCode(claimed.error)}`);
|
|
87
|
+
return "skipped";
|
|
88
|
+
}
|
|
89
|
+
const payload = claimed.data;
|
|
90
|
+
if (!payload.promotion ||
|
|
91
|
+
!payload.production_credential_profile_ref ||
|
|
92
|
+
!Array.isArray(payload.items) ||
|
|
93
|
+
!payload.receipt_id)
|
|
94
|
+
throw new Error("invalid_production_claim_receipt");
|
|
95
|
+
const promotion = {
|
|
96
|
+
...payload.promotion,
|
|
97
|
+
production_credential_profile_ref: payload.production_credential_profile_ref,
|
|
98
|
+
items: payload.items,
|
|
99
|
+
};
|
|
100
|
+
const signed = async (receipt) => {
|
|
101
|
+
const event = await this.sb.rpc("log_activity", {
|
|
102
|
+
p_verb: "production_migration.runner_receipt",
|
|
103
|
+
p_target_id: promotion.id,
|
|
104
|
+
p_target_type: "production_migration_promotion",
|
|
105
|
+
p_payload: { ...receipt, runner_id: this.runnerId },
|
|
106
|
+
});
|
|
107
|
+
if (event.error)
|
|
108
|
+
throw new Error(`production_signed_receipt_failed:${event.error.message}`);
|
|
109
|
+
};
|
|
110
|
+
const receipts = {
|
|
111
|
+
item: async (ordinal, status, errorCode) => {
|
|
112
|
+
const result = await this.sb.rpc("record_production_migration_item", {
|
|
113
|
+
p_promotion_id: promotion.id,
|
|
114
|
+
p_ordinal: ordinal,
|
|
115
|
+
p_status: status,
|
|
116
|
+
p_error_code: errorCode ? productionSafeCode(errorCode) : null,
|
|
117
|
+
});
|
|
118
|
+
if (result.error)
|
|
119
|
+
throw new Error(`production_item_receipt_failed:${result.error.message}`);
|
|
120
|
+
return result.data;
|
|
121
|
+
},
|
|
122
|
+
complete: async (succeeded, failureCode) => {
|
|
123
|
+
const result = await this.sb.rpc("complete_production_migration_promotion", {
|
|
124
|
+
p_promotion_id: promotion.id,
|
|
125
|
+
p_succeeded: succeeded,
|
|
126
|
+
p_failure_code: failureCode ? productionSafeCode(failureCode) : null,
|
|
127
|
+
});
|
|
128
|
+
if (result.error)
|
|
129
|
+
throw new Error(`production_completion_receipt_failed:${result.error.message}`);
|
|
130
|
+
return result.data;
|
|
131
|
+
},
|
|
132
|
+
signed,
|
|
133
|
+
};
|
|
134
|
+
let target = null;
|
|
135
|
+
let started = false;
|
|
136
|
+
try {
|
|
137
|
+
const root = this.options.resolveRepo?.(promotion.repo) ??
|
|
138
|
+
resolveMachineRepoPath(this.cfg, promotion.repo);
|
|
139
|
+
await (this.options.verifyCommit ?? verifyProductionCheckout)(root, promotion.commit_sha);
|
|
140
|
+
target =
|
|
141
|
+
this.options.targetFactory?.(promotion.production_credential_profile_ref) ??
|
|
142
|
+
new PgProductionTarget(promotion.production_credential_profile_ref, this.env);
|
|
143
|
+
await target.connect();
|
|
144
|
+
started = true;
|
|
145
|
+
await executeProductionPromotion(promotion, root, target, receipts, {
|
|
146
|
+
cancelled: async () => this.stopping,
|
|
147
|
+
timeoutMs: Number(this.env.ACC_PRODUCTION_MIGRATION_TIMEOUT_MS ?? 300_000),
|
|
148
|
+
receiptKey: this.env.ACC_PRODUCTION_MIGRATION_RECEIPT_KEY ?? "",
|
|
149
|
+
receiptKeyId: this.env.ACC_PRODUCTION_MIGRATION_RECEIPT_KEY_ID ?? "",
|
|
150
|
+
});
|
|
151
|
+
this.logger.info(`[acc-runner] production migration promotion ${promotion.id} succeeded`);
|
|
152
|
+
return "succeeded";
|
|
153
|
+
}
|
|
154
|
+
catch (error) {
|
|
155
|
+
if (!started)
|
|
156
|
+
await receipts.complete(false, productionSafeCode(error)).catch(() => undefined);
|
|
157
|
+
this.logger.error(`[acc-runner] production migration promotion ${promotion.id}: ${productionSafeCode(error)}`);
|
|
158
|
+
return "failed";
|
|
159
|
+
}
|
|
160
|
+
finally {
|
|
161
|
+
await target?.close().catch(() => undefined);
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
export { productionMigrationCaps, productionMigrationServiceEnabled } from "./profiles.js";
|
|
166
|
+
//# sourceMappingURL=service.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"service.js","sourceRoot":"","sources":["../../src/production-migrations/service.ts"],"names":[],"mappings":"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"}
|