aiwf
Version:
AI Workflow Framework for Claude Code with multi-language support (Korean/English)
220 lines (194 loc) • 5.98 kB
JavaScript
const { exec } = require('child_process');
const { promisify } = require('util');
const execAsync = promisify(exec);
const path = require('path');
const { extractFeatureIds, extractFeatureIdFromBranch } = require('../config/commit-patterns');
/**
* Git 커밋 정보를 추출하는 유틸리티 모듈
*/
/**
* Git 명령어 실행
* @param {string} command - 실행할 git 명령어
* @param {string} cwd - 작업 디렉토리 (선택사항)
* @returns {Promise<string>} - 명령어 출력
*/
async function runGitCommand(command, cwd = process.cwd()) {
try {
const { stdout, stderr } = await execAsync(`git ${command}`, { cwd });
if (stderr && !stderr.includes('warning:')) {
throw new Error(`Git command failed: ${stderr}`);
}
return stdout.trim();
} catch (error) {
throw new Error(`Git command execution failed: ${error.message}`);
}
}
/**
* 현재 브랜치 이름 가져오기
* @returns {Promise<string>} - 브랜치 이름
*/
async function getCurrentBranch() {
return await runGitCommand('rev-parse --abbrev-ref HEAD');
}
/**
* 커밋 정보 객체 구조
* @typedef {Object} CommitInfo
* @property {string} hash - 커밋 해시
* @property {string} author - 작성자
* @property {string} date - 커밋 날짜
* @property {string} message - 커밋 메시지
* @property {Array<string>} featureIds - 관련 Feature ID 목록
*/
/**
* 특정 커밋의 상세 정보 가져오기
* @param {string} commitHash - 커밋 해시
* @returns {Promise<CommitInfo>} - 커밋 정보
*/
async function getCommitInfo(commitHash) {
const format = '%H%n%an%n%aI%n%s%n%b';
const output = await runGitCommand(`show -s --format="${format}" ${commitHash}`);
const lines = output.split('\n');
const message = lines.slice(3).join('\n').trim();
const featureIds = extractFeatureIds(message);
return {
hash: lines[0],
author: lines[1],
date: lines[2],
message: message,
featureIds: featureIds
};
}
/**
* 커밋 범위에서 Feature 관련 커밋 찾기
* @param {string} since - 시작 날짜 (예: '2025-01-01')
* @param {string} until - 종료 날짜 (선택사항)
* @returns {Promise<Array<CommitInfo>>} - Feature 관련 커밋 목록
*/
async function getFeatureRelatedCommits(since, until = 'HEAD') {
try {
// git log 명령어 구성
let command = `log --since="${since}"`;
if (until !== 'HEAD') {
command += ` --until="${until}"`;
}
command += ' --pretty=format:"%H" --grep="FL[0-9]\\{3\\}"';
const output = await runGitCommand(command);
if (!output) {
return [];
}
const commitHashes = output.split('\n').filter(Boolean);
const commits = [];
for (const hash of commitHashes) {
const commitInfo = await getCommitInfo(hash);
if (commitInfo.featureIds.length > 0) {
commits.push(commitInfo);
}
}
return commits;
} catch (error) {
console.error('Error getting feature related commits:', error);
return [];
}
}
/**
* 특정 Feature ID와 관련된 모든 커밋 찾기
* @param {string} featureId - Feature ID (예: 'FL001')
* @returns {Promise<Array<CommitInfo>>} - 해당 Feature의 커밋 목록
*/
async function getCommitsByFeatureId(featureId) {
try {
const command = `log --pretty=format:"%H" --grep="${featureId}"`;
const output = await runGitCommand(command);
if (!output) {
return [];
}
const commitHashes = output.split('\n').filter(Boolean);
const commits = [];
for (const hash of commitHashes) {
const commitInfo = await getCommitInfo(hash);
if (commitInfo.featureIds.includes(featureId)) {
commits.push(commitInfo);
}
}
return commits;
} catch (error) {
console.error(`Error getting commits for feature ${featureId}:`, error);
return [];
}
}
/**
* 현재 브랜치의 Feature ID 추출
* @returns {Promise<string|null>} - Feature ID 또는 null
*/
async function getCurrentFeatureId() {
try {
const branch = await getCurrentBranch();
return extractFeatureIdFromBranch(branch);
} catch (error) {
console.error('Error getting current feature ID:', error);
return null;
}
}
/**
* 최근 N개의 커밋에서 Feature 관련 커밋 찾기
* @param {number} count - 확인할 커밋 수
* @returns {Promise<Array<CommitInfo>>} - Feature 관련 커밋 목록
*/
async function getRecentFeatureCommits(count = 10) {
try {
const command = `log -${count} --pretty=format:"%H"`;
const output = await runGitCommand(command);
if (!output) {
return [];
}
const commitHashes = output.split('\n').filter(Boolean);
const featureCommits = [];
for (const hash of commitHashes) {
const commitInfo = await getCommitInfo(hash);
if (commitInfo.featureIds.length > 0) {
featureCommits.push(commitInfo);
}
}
return featureCommits;
} catch (error) {
console.error('Error getting recent feature commits:', error);
return [];
}
}
/**
* Git 저장소인지 확인
* @param {string} dir - 확인할 디렉토리
* @returns {Promise<boolean>}
*/
async function isGitRepository(dir = process.cwd()) {
try {
await runGitCommand('rev-parse --git-dir', dir);
return true;
} catch (error) {
return false;
}
}
/**
* 변경된 파일 목록 가져오기
* @returns {Promise<Array<string>>} - 변경된 파일 경로 목록
*/
async function getChangedFiles() {
try {
const output = await runGitCommand('diff --name-only HEAD');
return output ? output.split('\n').filter(Boolean) : [];
} catch (error) {
console.error('Error getting changed files:', error);
return [];
}
}
module.exports = {
runGitCommand,
getCurrentBranch,
getCommitInfo,
getFeatureRelatedCommits,
getCommitsByFeatureId,
getCurrentFeatureId,
getRecentFeatureCommits,
isGitRepository,
getChangedFiles
};