Skip to content

Commit 2ac59be

Browse files
committed
feat: add Git merge support for local changes during pull
- Implemented functionality to stash local changes before pulling platform state and reapply them afterward. - Added utility functions to check if the current directory is a Git repository and to determine the presence of local changes. - Enhanced logging to inform users about the stashing and merging process, including conflict resolution guidance. - Updated comments for clarity on the new Git integration within the resource pulling workflow.
1 parent 79536aa commit 2ac59be

1 file changed

Lines changed: 67 additions & 14 deletions

File tree

src/pull.ts

Lines changed: 67 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
1+
import { execSync } from "child_process";
12
import { mkdir, writeFile } from "fs/promises";
23
import { join, dirname } from "path";
34
import { stringify } from "yaml";
4-
import { VAPI_ENV, VAPI_BASE_URL, VAPI_TOKEN, RESOURCES_DIR } from "./config.ts";
5+
import { VAPI_ENV, VAPI_BASE_URL, VAPI_TOKEN, RESOURCES_DIR, BASE_DIR } from "./config.ts";
56
import { loadState, saveState } from "./state.ts";
67
import type { StateFile, ResourceType } from "./types.ts";
78

@@ -28,10 +29,6 @@ const EXCLUDED_FIELDS = [
2829
"workflowIds", // Server-managed: workflows are a separate resource type
2930
];
3031

31-
// ─────────────────────────────────────────────────────────────────────────────
32-
// API Functions
33-
// ─────────────────────────────────────────────────────────────────────────────
34-
3532
// Map resource types to their API endpoints
3633
const ENDPOINT_MAP: Record<ResourceType, string> = {
3734
tools: "/tool",
@@ -56,6 +53,34 @@ const FOLDER_MAP: Record<ResourceType, string> = {
5653
simulationSuites: "simulations/suites",
5754
};
5855

56+
// ─────────────────────────────────────────────────────────────────────────────
57+
// Git Helpers (merge support — stash local changes before pull, reapply after)
58+
// ─────────────────────────────────────────────────────────────────────────────
59+
60+
function gitCmd(args: string): string {
61+
return execSync(`git ${args}`, {
62+
cwd: BASE_DIR,
63+
encoding: "utf-8",
64+
stdio: ["pipe", "pipe", "pipe"],
65+
}).trim();
66+
}
67+
68+
function isGitRepo(): boolean {
69+
try { gitCmd("rev-parse --is-inside-work-tree"); return true; } catch { return false; }
70+
}
71+
72+
function gitHasCommits(): boolean {
73+
try { gitCmd("rev-parse HEAD"); return true; } catch { return false; }
74+
}
75+
76+
function gitHasChanges(): boolean {
77+
return gitCmd("status --porcelain").length > 0;
78+
}
79+
80+
// ─────────────────────────────────────────────────────────────────────────────
81+
// API Functions
82+
// ─────────────────────────────────────────────────────────────────────────────
83+
5984
export async function fetchAllResources(resourceType: ResourceType): Promise<VapiResource[]> {
6085
const endpoint = ENDPOINT_MAP[resourceType];
6186
const url = `${VAPI_BASE_URL}${endpoint}`;
@@ -406,6 +431,18 @@ async function main(): Promise<void> {
406431
console.log(` API: ${VAPI_BASE_URL}`);
407432
console.log("═══════════════════════════════════════════════════════════════");
408433

434+
// Git merge support: stash local changes before overwriting with platform state
435+
const gitEnabled = isGitRepo() && gitHasCommits();
436+
const hadLocalChanges = gitEnabled && gitHasChanges();
437+
438+
if (hadLocalChanges) {
439+
console.log("\n📦 Stashing local changes before pull...");
440+
gitCmd('stash push -m "gitops: stash before pull"');
441+
console.log(" ✅ Local changes stashed\n");
442+
} else if (gitEnabled) {
443+
console.log("\n📦 No local changes to stash\n");
444+
}
445+
409446
const state = loadState();
410447

411448
const stats: Record<string, PullStats> = {
@@ -419,14 +456,7 @@ async function main(): Promise<void> {
419456
simulationSuites: { created: 0, updated: 0 },
420457
};
421458

422-
// Pull in dependency order:
423-
// 1. Base resources (tools, structuredOutputs)
424-
// 2. Assistants (references tools, structuredOutputs)
425-
// 3. Squads (references assistants)
426-
// 4. Simulation building blocks (personalities, scenarios - no cross-dependencies)
427-
// 5. Simulations (references personalities, scenarios)
428-
// 6. Simulation suites (references simulations)
429-
459+
// Pull in dependency order
430460
stats.tools = await pullResourceType("tools", state);
431461
stats.structuredOutputs = await pullResourceType("structuredOutputs", state);
432462
stats.assistants = await pullResourceType("assistants", state);
@@ -436,9 +466,32 @@ async function main(): Promise<void> {
436466
stats.simulations = await pullResourceType("simulations", state);
437467
stats.simulationSuites = await pullResourceType("simulationSuites", state);
438468

439-
// Save updated state
469+
// Reapply local changes on top of pulled platform state
470+
let hasConflicts = false;
471+
if (hadLocalChanges) {
472+
console.log("\n📦 Reapplying local changes...");
473+
try {
474+
gitCmd("stash pop");
475+
console.log(" ✅ Local changes merged cleanly\n");
476+
} catch {
477+
hasConflicts = true;
478+
}
479+
}
480+
481+
// Always save state last — overwrites any stash-induced changes to the state file
440482
await saveState(state);
441483

484+
if (hasConflicts) {
485+
console.error("\n⚠️ Merge conflicts detected!");
486+
console.error(" Platform changes conflict with your local changes.\n");
487+
console.error(" To see conflicted files:");
488+
console.error(" git diff --name-only --diff-filter=U\n");
489+
console.error(" After resolving conflicts:");
490+
console.error(` npm run push:${VAPI_ENV} # push to platform`);
491+
console.error(" git stash drop # clean up the stash\n");
492+
process.exit(1);
493+
}
494+
442495
// Summary
443496
console.log("\n═══════════════════════════════════════════════════════════════");
444497
console.log("✅ Pull complete!");

0 commit comments

Comments
 (0)