UNPKG

sync-worktrees

Version:

Automatically synchronize Git worktrees with remote branches - perfect for multi-branch development workflows

231 lines • 10.1 kB
#!/usr/bin/env node "use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || (function () { var ownKeys = function(o) { ownKeys = Object.getOwnPropertyNames || function (o) { var ar = []; for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; return ar; }; return ownKeys(o); }; return function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); __setModuleDefault(result, mod); return result; }; })(); Object.defineProperty(exports, "__esModule", { value: true }); const path = __importStar(require("path")); const prompts_1 = require("@inquirer/prompts"); const cron = __importStar(require("node-cron")); const config_loader_service_1 = require("./services/config-loader.service"); const worktree_sync_service_1 = require("./services/worktree-sync.service"); const cli_1 = require("./utils/cli"); const interactive_1 = require("./utils/interactive"); async function runSingleRepository(config) { console.log("\nšŸ“‹ CLI Command (for future reference):"); console.log(` ${(0, cli_1.reconstructCliCommand)(config)}`); console.log(""); const syncService = new worktree_sync_service_1.WorktreeSyncService(config); try { await syncService.initialize(); if (config.runOnce) { console.log("Running the sync process once as requested by --runOnce flag."); await syncService.sync(); } else { console.log("Git Worktree Sync script started as a scheduled job."); console.log(`Job is scheduled with cron pattern: "${config.cronSchedule}"`); console.log(`To see options, run: node ${path.basename(process.argv[1])} --help`); console.log("Running initial sync..."); await syncService.sync(); console.log("Waiting for the next scheduled run..."); cron.schedule(config.cronSchedule, async () => { try { await syncService.sync(); } catch (error) { console.error("Error during scheduled sync:", error); } }); } } catch (error) { console.error("āŒ Fatal Error during initialization:", error.message); process.exit(1); } } async function runMultipleRepositories(repositories, runOnce) { const services = new Map(); console.log(`\nšŸ”„ Syncing ${repositories.length} repositories...`); for (const repoConfig of repositories) { console.log(`\nšŸ“¦ Repository: ${repoConfig.name}`); console.log(` URL: ${repoConfig.repoUrl}`); console.log(` Worktrees: ${repoConfig.worktreeDir}`); if (repoConfig.bareRepoDir) { console.log(` Bare repo: ${repoConfig.bareRepoDir}`); } const syncService = new worktree_sync_service_1.WorktreeSyncService(repoConfig); services.set(repoConfig.name, syncService); try { await syncService.initialize(); await syncService.sync(); } catch (error) { console.error(`āŒ Error syncing repository '${repoConfig.name}':`, error.message); } } if (!runOnce) { console.log("\nā° Scheduling cron jobs for all repositories..."); const cronJobs = new Map(); for (const repoConfig of repositories) { const syncService = services.get(repoConfig.name); if (!syncService) continue; if (!cronJobs.has(repoConfig.cronSchedule)) { cronJobs.set(repoConfig.cronSchedule, repoConfig.cronSchedule); cron.schedule(repoConfig.cronSchedule, async () => { const reposToSync = repositories.filter((r) => r.cronSchedule === repoConfig.cronSchedule); for (const repo of reposToSync) { const service = services.get(repo.name); if (!service) continue; console.log(`\nšŸ”„ Running scheduled sync for: ${repo.name}`); try { await service.sync(); } catch (error) { console.error(`Error during scheduled sync for '${repo.name}':`, error); } } }); } } console.log("\nāœ… All repositories scheduled. Waiting for next runs..."); for (const [schedule] of cronJobs) { const repoCount = repositories.filter((r) => r.cronSchedule === schedule).length; console.log(` ${schedule}: ${repoCount} repository(ies)`); } } } async function listRepositories(configPath) { const configLoader = new config_loader_service_1.ConfigLoaderService(); try { const configFile = await configLoader.loadConfigFile(configPath); const configDir = path.dirname(path.resolve(configPath)); console.log("\nšŸ“‹ Configured repositories:\n"); configFile.repositories.forEach((repo, index) => { const resolved = configLoader.resolveRepositoryConfig(repo, configFile.defaults, configDir); console.log(`${index + 1}. ${resolved.name}`); console.log(` URL: ${resolved.repoUrl}`); console.log(` Worktrees: ${resolved.worktreeDir}`); console.log(` Schedule: ${resolved.cronSchedule}`); console.log(` Run Once: ${resolved.runOnce}`); if (resolved.bareRepoDir) { console.log(` Bare repo: ${resolved.bareRepoDir}`); } if (resolved.skipLfs) { console.log(` Skip LFS: ${resolved.skipLfs}`); } console.log(""); }); } catch (error) { console.error("āŒ Error loading config file:", error.message); process.exit(1); } } async function main() { const options = (0, cli_1.parseArguments)(); if (options.config) { const configLoader = new config_loader_service_1.ConfigLoaderService(); if (options.list) { await listRepositories(options.config); return; } try { const configFile = await configLoader.loadConfigFile(options.config); const configDir = path.dirname(path.resolve(options.config)); let repositories = configFile.repositories.map((repo) => configLoader.resolveRepositoryConfig(repo, configFile.defaults, configDir, configFile.retry)); if (options.filter) { repositories = configLoader.filterRepositories(repositories, options.filter); if (repositories.length === 0) { console.error(`āŒ No repositories match filter: ${options.filter}`); process.exit(1); } } const globalRunOnce = options.runOnce ?? configFile.defaults?.runOnce ?? false; // Apply CLI override for updateExistingWorktrees if (options.noUpdateExisting) { repositories = repositories.map((repo) => ({ ...repo, updateExistingWorktrees: false, })); } await runMultipleRepositories(repositories, globalRunOnce); } catch (error) { if (error instanceof Error && error.message.includes("Config file not found")) { console.error(`\nāŒ Config file not found: ${options.config}`); const createConfig = await (0, prompts_1.confirm)({ message: "Would you like to run interactive setup to create a config file?", default: true, }); if (createConfig) { // Run interactive mode which will offer to save config const config = await (0, interactive_1.promptForConfig)({}); await runSingleRepository(config); } else { console.log("\nšŸ’” You can create a config file manually or run without --config for interactive setup."); process.exit(1); } } else { console.error("āŒ Error loading config file:", error.message); process.exit(1); } } } else { let config; if ((0, cli_1.isInteractiveMode)(options)) { config = await (0, interactive_1.promptForConfig)(options); } else { config = options; } // Convert noUpdateExisting CLI flag to updateExistingWorktrees config if (options.noUpdateExisting) { config.updateExistingWorktrees = false; } else if (config.updateExistingWorktrees === undefined) { config.updateExistingWorktrees = true; // Default to true } await runSingleRepository(config); } } main().catch((error) => { console.error("āŒ Unhandled error:", error); process.exit(1); }); //# sourceMappingURL=index.js.map