Gitolite and hook
- Gitolite에 대한 후킹 시스템
서문
sitaramc/gitolite 는 Git 저장소를 편하게 사용할 수 있도록 호스팅해주는 좋은 도구입니다. Gitolite 는 사용자 정의 후킹을 추가할 수 있습니다만, 몇가지 이유로 인해 내가 원하는것을 찾아야할 필요가 있었습니다.(저장소별로 구분되는 post-receive)
이런 필요성때문에 나는 다양한 작업을 자동화하기 위해 저장소에 대한 post-receive 후킹을 사용자 정의로 설정하는 가이드를 작성했습니다. 이제 시작해 보도록 하겠습니다.
Gitolite의 준비
주의: 이것을 진행하는데는 Gitolite v3 를 쓴다고 가정하고 있습니다만 몇가지를 조정하면 v2 에서도 사용할 수 있습니다
지금부터 작업할 후킹 시스템은 Gitolite 관리 저장소 안에 거의 존재하는 경우가 없을겁니다만, 원하는 작동을 보장받기 위해 먼서 서버측(git를 운영하고있는곳의 home directory)의 .gitolite.rc 파일의 일부를 변경해야 작동됩니다.
- $LOCAL_CODE 의 값을 "$ENV{HOME}/.gitolite/local-code" 로 설정해줍니다(이렇게 하면 hook 이 작동될 수 있습니다)
- $GIT_CONFIG_KEYS 를 ".*" 으로 변경합니다. (이렇게 하면 나중에 hooks.run 을 사용할 수 있습니다.)
파일을 저장한뒤 이러한 변경 내용을 적용하려면 쉘에서 gitolite setup(gentoo에서는 /usr/bin/gl-setup 이 됩니다) 을 실행합니다. 파일을 저장하고 이러한 변경 내용을 적용하려면 쉘에서 gitolite setup 을 실행합니다. 이제 다음의 내용을 진행해서 로컬컴퓨터에 대한 관리자 저장소 아래쪽에 local-code 디렉토리 구조를 설정하면 됩니다:
- gitolite-admin
- conf
- keydir
- local-code
- hooks
- common
- hooks.d
- common
- hooks
후킹시스템(The hooking system)
이 시스템은 2개의 컴포넌트를 가지고 있는데, hooks.d 디렉토리와 후크 자체에서 적절한 후크를 실행하는 post-receive 후크가 그것입니다.
아래에 있는 코드는 post-receive 를 위한 파일의 내용으로서 저장소에 정의되며 후킹된경우 호출을 처리하는 코드입니다. (local-code/hooks/common/ 아래쪽에 코드를 위치시키면 됩니다):
post-receive
#!/bin/bash
run_hook () {
echo -en "\e[1;33m$4..\e[00m "
echo $1 $2 $3 | $GIT_DIR/hooks/hooks.d/$4
}
echo -en "\e[1;33mRunning hooks..\e[00m "
while read oldrev newrev refname; do
if [ "$refname" = "refs/heads/master" ]; then
hooks=$(git cat-file blob $newrev:.hooks 2>/dev/null)
if [ -n "$hooks" ]; then
# Repo-local hooks defined in .hooks.
for hook in $hooks; do
run_hook $oldrev $newrev $refname $hook
done
fi
# Global hooks for this repo (ie. set in Gitolite config).
hooks=$(git config --get hooks.run)
[ -z "$hooks" ] && continue
for hook in $hooks; do
run_hook $oldrev $newrev $refname $hook
done
fi
done
echo -e "\e[1;32mDone.\e[00m"
나는 이 방법이 따로 설명이 필요없는 깔끔한 방법이라고 믿습니다만, 이것에 대한 설명이 필요한경우 의견을 주시기 바랍니다 :)
저장소에있는 hook를 사용가능하게 만들기
이것들은 gitolite.conf 에서 직접 정의되어 있으므로 앞에서 언급한대로 이렇게 저장소에서 후킹되도록 설계되어 있습니다. 예를 들자면 내 홈페이지에 있대로 따라해보시면 됩니다(코딩라인에 주의해주세요):
repo web/..*
C = @zanea
RW+ = @zanea
config hooks.run = nginx-deploy notify <- hook 파일을 지정해주는 LINE
'
또한 사용자는 저장소 자체 내부의 .hook 파일에서 한 줄로 후크를 지정할 수 있습니다.(wild repos 에 유용합니다)
간단하게 hook 배포하기
이 코드는 내가 셋업한 환경을 위해 동작하는 코드입니다(nginx에서 사용되죠). 하지만 수정하는것이 어렵지는 않습니다. (그리고 local-code/hooks/common/hooks.d/ 에 위치하게 됩니다):
nginx-deploy
#!/bin/bash
read oldrev newrev refname
# Get project name from current directory (without .git)
PROJECT=$(basename "$PWD")
PROJECT=${PROJECT%.git}
# Where the checkout should reside
WWW_DIR=/www/${PROJECT}
# Where logs should reside
LOG_DIR=/www/logs/${PROJECT}
mkdir -p $LOG_DIR ; chmod 770 $LOG_DIR/../ ; chmod 770 $LOG_DIR
# Checkout the website
mkdir -p $WWW_DIR
GIT_WORK_TREE=$WWW_DIR git checkout -q -f $newrev
chmod -R 770 $WWW_DIR
# Replace some template values in our nginx.conf
sed -i -e "s_{{WEBDIR}}_${WWW_DIR}_g" -e "s_{{LOGDIR}}_${LOG_DIR}_g" -e "s_{{PROJECT}}_${PROJECT}_g" ${WWW_DIR}/nginxkkk.conf
# Symlink the nginx config to the nginx directory and tell nginx to reload
sudo ln -sf ${WWW_DIR}/nginx.conf /etc/nginx/sites-enabled/${PROJECT}.conf && sudo nginx -s reload
echo -e "\e[1;32mSuccessfully deployed ${PROJECT}.\e[00m"
그리고 해당되는 Gitolite config 에 맞는 자리에 위치시키면 되는데, 당신은 바로 git commit -a; git push 를 실행함으로서 당신의 admin repository 에 세팅한 후크가 동작하는걸 확인할 수 있습니다! 이 아래쪽에 제공되는 barebones 후크를 사용해보세요:
test-print
#!/bin/bash
read oldrev newrev refname
echo "$(pwd), $GIT_DIR, $oldrev $newrev $refname"
이 코드가 잘 동작한다면, 사용자가 정의한 저장소에 정의된 test-print hook 에 git push 된 결과가 사용자의 터미널에 출력되는것 확인할 수 있습니다.
추가로 할 수 있는것들
처음 hook 를 사용하고 싶었던 이유중 가장 큰것은 코드스타일 검사를 자동화해서 CodeSnack 에서 프로젝트를 위한 문서를 생성하게 하려는 것이었습니다. 우리는 운좋게도 PHP를 많이 사용하는 유틸리티는 PHPMD, sebastianbergmann/phpcpd(PHP Copy/Paste Detector), PHP_CodeSniffer 등과 같이 PHP 코드의 스타일 검사를 바깥쪽에서 수행하는 방법이 있습니다. 비슷한 도구로 sebastianbergmann/phpunit 그리고 unit 의 테스트와 문서화를 위한 phpDocumentor 2 등이 있습니다.
git hook 을 사용하면 Redmine 아래에서 관리되고 있는 프로젝트에 대한 어떤 나쁜 결과 또는 버그에 대한 레포트를 전부 모니터링을 할 수 있으며, 뿐만아니라 항상 프로젝트 문서를 최신으로 유지할 수 있고, 새로운 코드가 저장소에 들어가는 것에 대한 결과를 알 수 있습니다 :)
example code
file :: ~/.gitolite.rc
$GL_BINDIR = "/usr/bin";
$LOCAL_CODE = "$ENV{HOME}/.gitolite/local-code";
$GIT_CONFIG_KEYS = ".*";
참고문서
- 번역원문 :: http://demonastery.org/2012/09/a-hooking-system-for-gitolite/
- http://gitolite.com/gitolite/rc.html < search LOCAL_CODE and GIT_CONFIG_KEYS
- http://gitolite.com/gitolite/git-config.html < search GIT_CONFIG_KEYS
- http://askubuntu.com/questions/227577/how-to-set-local-code-in-gitolite