훅(hook) 은 특정 이벤트가 발생하면 동작하는 프로그램을 의미한다. 예로 저장소에 커밋이 요청이 들어오거나 혹은 커밋이 될 경우, 또는 저장소에 배타적 잠금(Exclusive Lock)이 생성되거나 해제될 경우등이다.

훅 은 동작 방식에 따라 크게 2가지로 나눌 수 있다. 첫 번째는 이벤트가 처리되기전에 수행되는 pre-훅 이다. pre-훅 을 이용하면 커밋전에 커밋 메시지가 정책에 맞는지, 파일이 추가될 경우 파일형식이 저장소 관리 규칙에 맞는지 등을 검사하여 규칙에 어긋나면 커밋을 거부하게 동작할 수 있다.

다른 하나는 이벤트가 완전히 끝난후에 수행되는 post 훅 이다. 이것을 이용하면 커밋이 완료되면 프로젝트 팀원들에게 메일을 보내거나(커밋시마다 메일을 보내는 건 과다한 메일이 발생할 수 있으므로 실제로는 유용하지 않다) 지속적인 통합 서버에 통보하여 커밋된 내용을 업데이트하여 자동으로 빌드를 수행하거나 이슈 관리에서 커밋 내역을 인덱싱하는 등의 작업을 수행할 수 있다.

post 훅 은 이벤트가 끝난후에 수행되므로 이벤트를 수정할 수 없는 점을 주의해야 한다. 커밋시 로그메시지를 반드시 입력해야 하는 게 내부 정책이라면 post 훅 에서 로그 메시지 여부를 검사해서 커밋을 취소하려고 해도 이미 커밋이 끝났으므로 커밋을 거부할 수 없다.

 

훅 디렉터리 위치

훅 프로그램은 저장소의 훅s 디렉터리에 위치한다. 기본적으로 svnadmin 명령어로 저장소를 생성하면 훅 스크립트 템플릿이 같이 생성된다.

ls -l /var/www/svn/myrepos/훅s/

total 36
-rwxr-xr-x. 1 lesstif rnd 2062 2014-03-18 23:12 post-commit.tmpl
-rwxr-xr-x. 1 lesstif rnd 1638 2014-03-18 23:12 post-lock.tmpl
-rwxr-xr-x. 1 lesstif rnd 2289 2014-03-18 23:12 post-revprop-change.tmpl
-rwxr-xr-x. 1 lesstif rnd 1567 2014-03-18 23:12 post-unlock.tmpl
-rwxr-xr-x. 1 lesstif rnd 3426 2014-03-18 23:12 pre-commit.tmpl
-rwxr-xr-x. 1 lesstif rnd 2434 2014-03-18 23:12 pre-lock.tmpl
-rwxr-xr-x. 1 lesstif rnd 2786 2014-03-18 23:12 pre-revprop-change.tmpl
-rwxr-xr-x. 1 lesstif rnd 2122 2014-03-18 23:12 pre-unlock.tmpl
-rwxr-xr-x. 1 lesstif rnd 3163 2014-03-18 23:12 start-commit.tmpl

위와 같이 9개의 훅 스크립트 템플릿이 저장소의 훅s 디렉터리 내에 생성된다. 훅 파일은 확장자인 .tmpl 을 없애고 훅의 이름은 이벤트가 완료되기 전에 실행되는지 후에 실행되는지 여부에 따라 pre 나 post 접두사를 붙이고 - 뒤에 이벤트 이름을 지정하면 된다.

훅은 실행 속성이 있어야 하므로 작성은 쉘 스크립트, PERL, PHP, Python, Ruby, 자바등 언어의 제한 없이 선호하는 개발 언어를 사용하여 작성하고 chmod +x 명령어로 실행 속성을 부여해 주면 된다.

 

훅 실행 권한

훅 프로그램을 실행시 저장소에 연결하는 프로세스의 권한으로 실행된다. 예로 svnserve 로 저장소에 연결시 svnserve 를 구동한 사용자의 권한을 갖게되며 apache httpd 를 사용하여 저장소에 연결시 apache httpd 의 권한을 갖게 된다.

한가지 주의할 점은 보안 이슈때문에 훅 을 실행할 경우 환경변수를 상속받지 않고 모두 초기화하고 실행한다는 점이다. 훅 스크립트에서 $PATH 나 $EDITOR 같은 환경 변수에 접근해도 설정된 값이 없으므로 의도대로 동작하지 않을 소지가 크다.그러므로 훅 스크립트 구동에 필요한 환경 변수는 .profile 이나 .bash_profile 같이 쉘의 초기화 파일에 넣어도 적용되지 않으므로 훅 스크립트 자체에 넣어야 한다.

훅 에서 사용할 환경 변수를 설정하는 또 다른 방법은 svn 1.8 부터 추가된 기능인 hooks-env 파일을 사용하는 것이다. 저장소의 conf 디렉터리 밑에 hooks-env 파일이 있을경우 svn 은 해당 파일을 파싱하여 처리한다. hooks-env 파일은 Windows의 INI 파일처럼 [section] 에 name = value 형식으로 설정할 수 있다. 다음 예제를 보자

# 모든 스크립트는 UTF-8 인코딩 사용
[default]
LANG = ko_KR.UTF-8
PATH = /usr/local/bin:/usr/bin


## 이벤트 마다 PATH 환경 변수를 다르게 설정할 수 있다.
[post-commit]
PATH = /usr/local/synctools-1.1/bin:%(PATH)s


[post-revprop-change]
PATH = /usr/local/synctools-1.1/bin:%(PATH)s

default 에 지정한 변수를 다른 훅마다 사용하려면 %(변수명)s 와 같이 사용하면 된다. s는 치환(substitue) 의 의미로 %(PATH)s 는 PATH 변수의 값으로 치환된다.   

 

훅 스크립트의 범주

훅 스크립트를 통해 다양한 기능을 구현할 수 있지만 크게 notification, validation, and replication 세 가지 범주로 나눠볼 수 있다. 

 

먼저 통지 스크립트는 어떤 이벤트가 발생했음을 알리는 용도로 사용된다. post-commit 과 post-revprop-change 훅을 통해 사용되며 커밋후 커밋 메시지와 변경 내역을 통보하거나 저장소의 property 가 변경되었을 경우에 사용될 수 있다. 

validation 스크립트는 주로 start-commit 이나 pre-commit 을 통해 구현되며 버전관리 규칙에 맞는지 여부를 커밋이 완료되기 전에 검증할 수 있다.  

마지막으로 replication 스크립트는 저장소를 복제한다거나 할 경우에 사용할 수 있다. 다음 항목에서 다룰 "svn 저장소 백업"에서는 svn 에 내장된 svnsync 유틸리티와 pre-revprop-change 훅 을 이용하여 저장소를 백업하는 예제를 다룰 예정이다.

 

훅 스크립트 사용예

pre-commit 훅 을 이용하여 커밋 log 메시지가 규칙을 따랐는지와 Issue 관리 시스템과 연동하기 위한 내용이 들어 있는지 확인하는 예제를 통해 훅 스크립트의 활용 방법에 대해 습득해 보자.

먼저 프로젝트의 커밋 규칙이 다음과 같다고 가정하자. 

  1. 커밋 메시지는 10자 이상이어야 한다.
  2. 커밋 메시지안에는 이슈 관리 시스템과 연동하기 위한 식별 가능한 유일한 이슈번호(12장에서 기술)가 들어 있어야 한다.

 

이슈 번호는 redmine 의 경우 숫자로만 이루어져 있고(Ex: 123) JIRA 의 경우 PROJECT 식별자뒤에 - 와 일련번호가 들어간다.(PROJ-123).

더 자세한 훅 스크립트는 이슈 관리 시스템 장의 버전 관리 연동 부분에서 작성하고 일단은 redmine 과 연동할 경우 커밋 로그 메시지에 레드마인 이슈 번호를 포함했는지 여부를 확인하는 훅을 php 로 만들어 보자.

  1. 선호하는 에디터로 pre-commit 파일을 연다.

    vi myrepos/hooks/pre-commit

  2. 다음 훅 스크립트를 작성한다.

    #!/usr/bin/php<?php
    
    # comment 가 10자 미만이면 커밋 거부
    $minchars = 10;
    $svnlook = 'svnlook';
    
    
    #--------------------------------------------
    $repos = $argv[1];
    $txn = $argv[2];
    $comment = `$svnlook log -t "$txn" "$repos"`;
    
    $comment = chop($comment);
    
    if ( strlen($comment) == 0 ) {
      fwrite(STDERR, "---------------------------------------------------------------------------\n");
      fwrite(STDERR, "Your commit has been blocked because it didn't include a log message.!\n");
      fwrite(STDERR, "Do the commit again, this time with a log message that describes your changes.!\n");
      fwrite(STDERR, "---------------------------------------------------------------------------\n");
      exit(1);
    }
    else if ( strlen($comment) < $minchars ) {
      fwrite(STDERR, "---------------------------------------------------------------------------\n");
      fwrite(STDERR, "Comment must be at least $minchars characters.\n");
      fwrite(STDERR, "---------------------------------------------------------------------------\n");
      exit(1);
    }
    
    ## 커밋 메시지에 #123 처럼 redmine 이슈 번호가 들어 있는지 확인한다.
    if (preg_match('/[^\d\n\r]#(?:[0-9]+)+.+/', $comment)) {
    	# Successful match
    } else {
      # Match attempt failed
      fwrite(STDERR, "---------------------------------------------------------------------------\n");
      fwrite(STDERR, "Comment must match REDMINE-ISSUE-NUMBER!!! \"$comment\"\n");
      fwrite(STDERR, "---------------------------------------------------------------------------\n");
      exit(1);
    }
    
    exit(0);
    ?>
    CODE
  3. chmod 로 실행 속성을 부여한다.

    chmod +x myrepos/hooks/pre-commit


훅 스크립트 동작 확인

  1. 임의의 파일을 편집한 후에 로그 메시지 없이 커밋을 시도해서 아래처럼 실패가 나는걸 확인해 보자.

    svn ci -m " "

  2. 로그 메시지는 있지만 redmine 의 이슈 번호가 없는 커밋을 해서 훅 스크립트의 정상 동작 여부를 확인할 수 있다.

    svn ci -m "pom.xml changed" 

  3. 정상적으로 커밋을 하려면 다음과 같이 커밋 로그 메시지에 레드마인의 이슈 번호를 포함해야 한다.

    svn ci -m "issue 379 fixed"