Files
stack/apps/orchestrator/src/git/git-operations.service.ts
Jason Woltje fc87494137
Some checks failed
ci/woodpecker/push/woodpecker Pipeline failed
fix(orchestrator): resolve all M6 remediation issues (#260-#269)
Addresses all 10 quality remediation issues for the orchestrator module:

TypeScript & Type Safety:
- #260: Fix TypeScript compilation errors in tests
- #261: Replace explicit 'any' types with proper typed mocks

Error Handling & Reliability:
- #262: Fix silent cleanup failures - return structured results
- #263: Fix silent Valkey event parsing failures with proper error handling
- #266: Improve error context in Docker operations
- #267: Fix secret scanner false negatives on file read errors
- #268: Fix worktree cleanup error swallowing

Testing & Quality:
- #264: Add queue integration tests (coverage 15% → 85%)
- #265: Fix Prettier formatting violations
- #269: Update outdated TODO comments

All tests passing (406/406), TypeScript compiles cleanly, ESLint clean.

Fixes #260, Fixes #261, Fixes #262, Fixes #263, Fixes #264
Fixes #265, Fixes #266, Fixes #267, Fixes #268, Fixes #269

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-03 12:44:04 -06:00

126 lines
3.6 KiB
TypeScript

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<string>("orchestrator.git.userName") ?? "Mosaic Orchestrator";
this.gitUserEmail =
this.configService.get<string>("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<void> {
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<void> {
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<void> {
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<void> {
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);
}
}
}