import { Injectable, Logger } from "@nestjs/common"; import { ConfigService } from "@nestjs/config"; import { simpleGit, SimpleGit } from "simple-git"; import { GitOperationError } from "./types"; /** * Service for managing git operations */ @Injectable() export class GitOperationsService { private readonly logger = new Logger(GitOperationsService.name); private readonly gitUserName: string; private readonly gitUserEmail: string; constructor(private readonly configService: ConfigService) { this.gitUserName = this.configService.get("orchestrator.git.userName") ?? "Mosaic Orchestrator"; this.gitUserEmail = this.configService.get("orchestrator.git.userEmail") ?? "orchestrator@mosaicstack.dev"; } /** * Get a simple-git instance for a local path */ private getGit(localPath: string): SimpleGit { return simpleGit(localPath); } /** * Clone a repository */ async cloneRepository(url: string, localPath: string, branch?: string): Promise { try { this.logger.log(`Cloning repository ${url} to ${localPath}`); const git = simpleGit(); if (branch) { await git.clone(url, localPath, ["--branch", branch]); } else { await git.clone(url, localPath); } this.logger.log(`Successfully cloned repository to ${localPath}`); } catch (error) { this.logger.error(`Failed to clone repository: ${String(error)}`); throw new GitOperationError( `Failed to clone repository from ${url}`, "clone", error as Error ); } } /** * Create a new branch */ async createBranch(localPath: string, branchName: string): Promise { try { this.logger.log(`Creating branch ${branchName} at ${localPath}`); const git = this.getGit(localPath); await git.checkoutLocalBranch(branchName); this.logger.log(`Successfully created branch ${branchName}`); } catch (error) { this.logger.error(`Failed to create branch: ${String(error)}`); throw new GitOperationError( `Failed to create branch ${branchName}`, "createBranch", error as Error ); } } /** * Commit changes */ async commit(localPath: string, message: string, files?: string[]): Promise { try { this.logger.log(`Committing changes at ${localPath}`); const git = this.getGit(localPath); // Configure git user await git.addConfig("user.name", this.gitUserName); await git.addConfig("user.email", this.gitUserEmail); // Stage files if (files && files.length > 0) { await git.add(files); } else { await git.add("."); } // Commit await git.commit(message); this.logger.log(`Successfully committed changes: ${message}`); } catch (error) { this.logger.error(`Failed to commit: ${String(error)}`); throw new GitOperationError(`Failed to commit changes`, "commit", error as Error); } } /** * Push changes to remote */ async push(localPath: string, remote = "origin", branch?: string, force = false): Promise { try { this.logger.log(`Pushing changes from ${localPath} to ${remote}`); const git = this.getGit(localPath); if (force) { await git.push(remote, branch, { "--force": null }); } else { await git.push(remote, branch); } this.logger.log(`Successfully pushed changes to ${remote}`); } catch (error) { this.logger.error(`Failed to push: ${String(error)}`); throw new GitOperationError(`Failed to push changes to ${remote}`, "push", error as Error); } } }