sync-worktrees
Version:
Automatically synchronize Git worktrees with remote branches - perfect for multi-branch development workflows
231 lines ⢠10.1 kB
JavaScript
;
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