배경 및 목적
- 몰라도, 없어도 큰 상관은 없지만 막상 없으면 생각보다 귀찮은 그것, 게으른 개발자를 위한 Git Hooks입니다.
- 프로젝트에 Spotless 등을 통해 Lint를 정하게 되면 아래와 같은 반복 작업이 발생하게 됩니다.
- 매번 Gradle Task Spotless Apply / Spotless Check를 통해 Lint가 맞는지 확인
- 또한 커밋 컨벤션 등으로 Jira 티켓 번호 등을 연동하기 위해서는, 아래와 같은 반복 작업 또한 발생하게 됩니다.
- 커밋 시 xxx-000 으로 지라 이슈번호를 앞에 기재하기
- 이런 과정을 자동화해 매 커밋마다 1~2초를 줄이고, 팀원 전부의 1~2초가 1년이 되면 꽤 의미있는 시간이 되지 않을까하여 Git Hooks을 통해 이것을 자동화하는 작업을 해보았습니다.
기대 결과
- 프로젝트 개발자들이 프로젝트에 적용된 Lint 규칙에 대해 전혀 모르고, 심지어 Spotless를 신경쓰지 않아도 Repo 에 올라가는 모든 코드가 Lint 규칙이 자동으로 적용되어 올라가는 효과를 얻을 수 있습니다.
- 또한 모든 (xxx- 로 시작하는 feature 브랜치)의 커밋에 지라 이슈번호를 자동으로 붙임으로써, 미아 커밋을 방지할 수 있습니다.
구현 내역
구현 사항은 크게 아래의 두가지로 나뉩니다.
PreCommit 시에 자동으로 Spotless Apply를 진행하고, 변경 사항을 Git add 하기
scripts/pre-commit
# 변경된 파일들 이름만 추출하여 저장
stagedFiles=$(git diff --staged --name-only)
# SpotlessApply 실행
echo "Running spotlessApply. Formatting code..."
./gradlew spotlessApply
# 변경사항이 발생한 파일들 다시 git add
for file in $stagedFiles; do
if test -f "$file"; then
git add "$file"
fi
done
Prepare Commit Msg 로 Commit Message 에 지라 이슈번호 붙이기
# 브랜치 이름 받아오기
BRANCH_NAME=$(git symbolic-ref --short HEAD)
echo BRANCH_NAME : "$BRANCH_NAME"
# 브랜치 이름에서 cmc-숫자 에 해당하는 문자열을 추출하여 Jira id 에 저장
JIRA_ID=$(echo "${BRANCH_NAME}" | grep -o '^cmc-[0-9]*')
echo Jira id : "$JIRA_ID"
# 추출한 문자열 반영
BRANCH_IN_COMMIT=$(grep -c "$JIRA_ID" $1)
if [[ -n $JIRA_ID ]] && ! [[ $BRANCH_IN_COMMIT -ge 1 ]]; then
sed -i.bak -e "1s/^/$JIRA_ID /" $1
fi
(공통) Git Hooks 관련 권한설정이 되어있지 않은 신규 인원들을 위한 설정도 추가하였습니다.
gradle/install-git-hooks
tasks.create(name: 'gitExecutableHooks') {
Runtime.getRuntime().exec("chmod -R +x .git/hooks/");
}
task installGitHooks(type: Copy) {
from new File(rootProject.rootDir, 'pre-commit'), new File(rootProject.rootDir, 'prepare-commit-msg')
into { new File(rootProject.rootDir, '.git/hooks') }
}
gitExecutableHooks.dependsOn installGitHooks
clean.dependsOn gitExecutableHooks
Commit전에 SpotlessApply를 진행하고, 변경된 파일을 자동으로 add합니다.
Commit 전에 자동으로 브랜치에서 Jira 이슈 번호를 추출하여 커밋 메시지에 Prefix로 반영합니다.
해당 파일들을 작성하고, build.gradle에
// PreCommit Script Copy to .git
tasks.register('updateGitHooks', Copy) {
from './scripts/pre-commit', './scripts/prepare-commit-msg'
into './.git/hooks'
}
compileJava.dependsOn updateGitHooks
해당 task를 등록해주면 성공적으로 동작합니다.
발생할 수 있는 에러
- Mac OS 사용시 fatal: cannot exec '.git/hooks/pre-commit': Operation not permitted 에러 발생
pre-commit 파일에는 이와 관련된 파일 메타 데이터가있을 수 있으며 (@ 출력의 ls에서 이를 확인할 수 있습니다.) 해당 파일 메타 데이터에 com.apple.quarantine 속성이 포함될 수 있습니다.
- 프로젝트 루트의 .git/hooks 로 이동합니다.
- ls -l@ pre-commit || xattr -l pre-commit 가 정상 동작하는지 확인합니다.
- xattr -d com.apple.quarantine pre-commit 로 해당 속성을 제거합니다.