UNPKG

cfn-forge

Version:

CloudFormation deployment automation tool with git-based workflows

228 lines (204 loc) 6 kB
/** * Git Manager Implementation * Cross-platform git operations using simple-git */ const simpleGit = require('simple-git'); const path = require('path'); const IGitManager = require('../interfaces/IGitManager'); class GitManager extends IGitManager { constructor(workingDir = process.cwd()) { super(); this.git = simpleGit(workingDir); this.workingDir = workingDir; } /** * Get the current commit hash (short version) * @returns {Promise<string>} Short commit hash (8 chars) */ async getCommitHash() { try { const log = await this.git.log(['-1', '--format=%H']); return log.latest.hash.substring(0, 8); } catch (error) { throw new Error(`Failed to get commit hash: ${error.message}`); } } /** * Get the current branch name * @returns {Promise<string>} Current branch name */ async getCurrentBranch() { try { const branch = await this.git.branch(); return branch.current || 'main'; } catch (error) { throw new Error(`Failed to get current branch: ${error.message}`); } } /** * Get the latest tag * @returns {Promise<string|null>} Latest tag or null if none */ async getLatestTag() { try { const tags = await this.git.tags(); return tags.latest || null; } catch (error) { // Return null if no tags exist instead of throwing return null; } } /** * Check if current commit is tagged * @returns {Promise<boolean>} True if on a tagged commit */ async isOnTaggedCommit() { try { const latestTag = await this.getLatestTag(); if (!latestTag) { return false; } const tagCommit = await this.git.revparse([latestTag]); const currentCommit = await this.git.revparse(['HEAD']); return tagCommit === currentCommit; } catch (error) { return false; } } /** * Check if there are uncommitted changes * @returns {Promise<boolean>} True if working directory has changes */ async hasUncommittedChanges() { try { const status = await this.git.status(); return !status.isClean(); } catch (error) { throw new Error(`Failed to check for uncommitted changes: ${error.message}`); } } /** * Get list of changed files * @returns {Promise<string[]>} Array of changed file paths */ async getChangedFiles() { try { const status = await this.git.status(); const changed = [ ...status.modified, ...status.not_added, ...status.created, ...status.deleted, ...status.renamed.map((r) => r.to), ]; return changed; } catch (error) { throw new Error(`Failed to get changed files: ${error.message}`); } } /** * Get repository root directory * @returns {Promise<string>} Absolute path to repo root */ async getRepoRoot() { try { const root = await this.git.revparse(['--show-toplevel']); return path.resolve(root); } catch (error) { throw new Error(`Failed to get repository root: ${error.message}`); } } /** * Check if path is inside a git repository * @returns {Promise<boolean>} True if inside a git repo */ async isGitRepository() { try { await this.git.revparse(['--git-dir']); return true; } catch (error) { return false; } } /** * Get commit message for a specific commit * @param {string} commitHash - Commit hash * @returns {Promise<string>} Commit message */ async getCommitMessage(commitHash) { try { const log = await this.git.show([commitHash, '--format=%B', '--no-patch']); return log.trim(); } catch (error) { throw new Error(`Failed to get commit message: ${error.message}`); } } /** * Get list of commits between two refs * @param {string} fromRef - Starting reference * @param {string} toRef - Ending reference * @returns {Promise<Array>} Array of commit objects */ async getCommitsBetween(fromRef, toRef) { try { const log = await this.git.log({ from: fromRef, to: toRef, }); return log.all.map((commit) => ({ hash: commit.hash, shortHash: commit.hash.substring(0, 8), message: commit.message, author: commit.author_name, email: commit.author_email, date: commit.date, })); } catch (error) { throw new Error(`Failed to get commits between refs: ${error.message}`); } } /** * Check if deployment should be triggered based on configuration * @param {Object} config - Deployment configuration * @returns {Promise<boolean>} True if deployment should proceed */ async shouldDeploy(config = {}) { const deployOnTags = config.deployOnTags || false; if (deployOnTags) { const isTagged = await this.isOnTaggedCommit(); return isTagged; } // Default: deploy on any commit return true; } /** * Get deployment trigger information * @returns {Promise<Object>} Trigger information */ async getDeploymentTrigger() { const isTagged = await this.isOnTaggedCommit(); const branch = await this.getCurrentBranch(); const commitHash = await this.getCommitHash(); const latestTag = await this.getLatestTag(); return { type: isTagged ? 'tag' : 'commit', branch, commitHash, tag: isTagged ? latestTag : null, timestamp: new Date().toISOString(), }; } /** * Create a deployment hash for state tracking * @returns {Promise<string>} Deployment hash */ async createDeploymentHash() { const commitHash = await this.getCommitHash(); const branch = await this.getCurrentBranch(); const timestamp = Date.now(); // Create a simple hash for deployment state tracking const deploymentId = `${branch}-${commitHash}-${timestamp}`; return Buffer.from(deploymentId).toString('base64').substring(0, 16); } } module.exports = GitManager;