-
Notifications
You must be signed in to change notification settings - Fork 8
Expand file tree
/
Copy pathapply.ts
More file actions
105 lines (93 loc) · 4.27 KB
/
Copy pathapply.ts
File metadata and controls
105 lines (93 loc) · 4.27 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
import { execSync } from "child_process";
import { dirname, join, resolve } from "path";
import { fileURLToPath } from "url";
// ─────────────────────────────────────────────────────────────────────────────
// Apply: Pull → Merge → Push (safe bidirectional sync)
//
// 1. Pull latest platform state, merge with local changes (git stash/pop)
// 2. If merge is clean, push the result to the platform
// 3. If conflicts, stop — user resolves, then runs push manually
// ─────────────────────────────────────────────────────────────────────────────
const __dirname = dirname(fileURLToPath(import.meta.url));
const BASE_DIR = join(__dirname, "..");
const SLUG_RE = /^[a-z0-9]([a-z0-9-]*[a-z0-9])?$/;
function runPassthrough(cmd: string): number {
try {
execSync(cmd, { cwd: BASE_DIR, stdio: "inherit" });
return 0;
} catch (error: unknown) {
return (error as { status?: number }).status ?? 1;
}
}
export async function runApply(): Promise<void> {
const env = process.argv[2];
const allArgs = process.argv.slice(3);
const hasForce = allArgs.includes("--force");
const pullArgs = allArgs.filter((a) => a !== "--force").join(" ");
const pushArgs = allArgs.join(" ");
if (!env || !SLUG_RE.test(env)) {
console.error("Usage: npm run apply <org> [--force] [--allow-new-files]");
console.error("");
console.error(" Pull → Merge → Push (safe bidirectional sync)");
console.error("");
console.error(" Pulls latest platform state (preserving local changes),");
console.error(" then pushes the result back to the platform.");
console.error("");
console.error(
" --force Enable deletions: resources you deleted locally",
);
console.error(
" will also be deleted from the platform.",
);
console.error(
" --allow-new-files Bypass the orphan-YAML pre-flight gate (push stage).",
);
console.error(
" Use only after confirming every local file without a",
);
console.error(
" state entry is genuinely new — see src/new-file-gate.ts.",
);
process.exit(1);
}
console.log(
"═══════════════════════════════════════════════════════════════",
);
console.log(`🔄 Vapi GitOps Apply - Environment: ${env}`);
console.log(" Pull → Merge → Push");
if (hasForce) {
console.log(" ⚠️ Deletions enabled (--force)");
}
console.log(
"═══════════════════════════════════════════════════════════════\n",
);
const pullCmd = `npx tsx src/pull.ts ${env} ${pullArgs}`.trim();
const pullExit = runPassthrough(pullCmd);
if (pullExit !== 0) {
console.error("\n❌ Pull had issues. Resolve conflicts before pushing.");
process.exit(1);
}
console.log("\n🚀 Pushing merged state to platform...\n");
const pushCmd = `npx tsx src/push.ts ${env} ${pushArgs}`.trim();
const pushExit = runPassthrough(pushCmd);
if (pushExit !== 0) {
console.error("\n❌ Push failed!");
process.exit(1);
}
console.log(
"\n═══════════════════════════════════════════════════════════════",
);
console.log("✅ Apply complete! (Pull → Merge → Push)");
console.log(
"═══════════════════════════════════════════════════════════════\n",
);
}
// Run when executed directly
const isMainModule =
resolve(process.argv[1] ?? "") === resolve(fileURLToPath(import.meta.url));
if (isMainModule) {
runApply().catch((error) => {
console.error("\n❌ Apply failed:", error);
process.exit(1);
});
}