목공책 하나 들이셔요~

2014년 2월 12일 수요일

Mercurial 리비전 출력과 hgweb UI 변경

이번 포스팅은 Mercurial에 대한 소소한 팁으로 Mercurial의 리비전 번호를 ant에서 추출하는 방법과 Mercurial 서버인 hgweb의 유저인터페이스를 튜닝하는 법에 대해서 알아봅니다. 

저의 개인적인 코딩 스타일이 반영된 것으로 범용적인 솔루션은 아님을 양해 바랍니다.

프로그램에 버전 정보 부여하기

개발팀이 만든 프로그램이 Lab을 떠나서 현장에 설치되고 나면 개발자의 보살핌을 받지 못하게 됩니다. 현장에 설치된 소프트웨어의 버전은 변하지 않지만 개발자들의 PC에는 새로운 버전으로 계속 업데이트되고 있습니다.

이런 상황에서 현장에 설치된 소프트웨어에 어떤 문제가 발생했을 때 난감한 상황이 됩니다.

개발자의 PC에 있는 소스코드는 현장에 설치된 것에 비해 많이 업그레이드된 것입니다. 그 문제가 이미 인지되어 수정된 것이라면 현장의 소프트웨어를 최신의 것으로 업데이트를 하면 되지만 이것만으로 해결되지 않는 경우도 있습니다. 이럴때는 현장에 설치된 소프트웨어의 버전 정보가 매우 중요합니다.

개발팀에서는 보통 1.0 alpha, 1.0 beta, 1.0, 1.1, 1.1.1 이런식으로 어떤 규칙에 의해서 버전을 매기는데 어떤 상황에서 버전 번호를 변경할 것인가에 대해서는 모호한 면이 없지 않습니다. 실제로 같은 버전 1.1이라고 해서 완전한 동일한 코드가 아닌 경우가 빈번합니다.

그래서 대부분의 상용 프로그램들은 더 복잡한 버전 번호를 매깁니다. 아래 구글 크롬의 버전 정보를 보면 32.0.1700.107 과 같은 식으로 버전 번호가 부여되는데 아마도 1700.107은 브랜치나 리비전 번호와 맵핑된 걸로 추측됩니다. 단순하게 두자리의 버전 번호보다는 더 구체적으로 어떤 소스코드의 스냅샷을 지정할 수 있지요.

똑같지는 않지만 저는 개발한 소프트웨어의 버전 번호를 버전 컨트롤 시스템의 리비전 번호를 사용합니다. Mercurial의 경우는 로컬 리포지토리에서 부여되는 리비전 번호와 각 체인지셋 마다 고유하게 부여되는 체이지셋 ID를 조합하여 사용합니다. 체인지셋 ID는 원래 40자리의 16진수로 구성되는데 너무 길어서 12자리로 줄이고 리비전 번호와 결합하여 사용합니다.

예를 들어 커맨드라인에서 "hg tip"이라고 입력하면 가장 최신의 체인지셋에 대한 정보가 출력됩니다. 이때 체인지셋 ID는 30:7b2b3c1a9772와 같은 식으로 출력되지만 "hg tip --debug"를 입력하여 더 자세한 정보를 출력할 경우 아래 그림과 같이 40자리의 긴 체인지셋 번호가 출력됩니다.


리비전 번호는 로컬 리포지토리에서만 유일성이 보장되기 때문에 체인지셋을 특정하는데 어려움이 있습니다. 그래서 체인지셋의 앞부분과 리비전 번호를 결합한 형태가 주로 사용됩니다. 저는 이 짧은 체인지셋 ID를 제가 만드는 소프트웨어의 버전 정보로 기록합니다.


Java로 개발하는 경우 Ant 스크립트를 이용하여 프로젝트를 빌드하고 관리합니다. 프로젝트의 버전 즉 체인지셋 ID를 자동으로 생성하고 소프트웨어에 내재시키기 위해서 저는 Ant 스크립트에서 Mercurial을 실행하여 version.properties 파일을 만들고 소프트웨어에서 이를 읽어들여 버전 정보를 표시하게 합니다.

Mercurial은 가장 최근에 커밋된 체인지셋을 tip이라고 하는데, 이 tip의 체인지셋 ID와 커밋 시각을 추출하는 명령이 필요합니다. "hg help tip"을 입력하면 tip에 대한 도움말이 나오는데 --template이라는 옵션이 보입니다. Mercurial의 장점 중 하나가 명령의 출력이 고정되지 않고 template이나 style을 지정하여 커스터마이징 할 수 있다는 것입니다.

template옵션에 대해 더 자세히 알아보려면 "hg help template"이라고 입력하면 됩니다. 이 설명을 자세히 보면 어떻게 해야할지 감이 잡히실 겁니다.

template은 키워드와 필터로 구성됩니다. 문법은 {keyword | filter} 와 같습니다. 키워드는 어떤 항목을 출력할 것인가를 지정하고 필터는 어떤 형식으로 출력할 것인가를 지정합니다.

예를 들어 hg tip --template '{date|isodate}' 명령을 실행하면 잡스러운 정보들은 모두 생략되고 다음과 같이 ISO규격에 의한 날짜만 출력됩니다.

$ hg tip --template '{date | isodate}'
2014-02-11 04:40 +0900

만일 XML에서 사용되는 RFC3339 규격으로 출력하려면 다음과 같이 하면 됩니다.

$ hg tip --template '{date | rfc3339date}'
2014-02-11T04:40:18+09:00

비슷한 방법으로 리비전 번호와 짧은 체인지셋 ID를 조합하여 버전을 출력하려면 다음과 같이 하면 됩니다.

$ hg tip --template '{rev}:{node | short}'
30:7b2b3c1a9772

이같은 팁을 이용하여 Ant에서 자동으로 version.properties 파일을 생성할 수 있습니다. Netbeans는 프로젝트 빌드를 위한 Ant 스크립트를 자동으로 만들어주어 편리한데, build.xml에서 "-pre-dist" 타겟은 배포판을 만들기 전에 자동으로 실행되어 version.properties 파일을 만들기 좋습니다. 대략 다음과 같은 식으로 타겟을 정의합니다.

<target name="-pre-dist">
    <antcall target="versioning">
</target>

<target name="versioning">
    <property name="hg.exec" value="/usr/bin/hg" />
    <property name="product.version" value="1.1" />
    <echo message="Making version.properties"/>
    <exec executable="${hg.exec}" outputproperty="reldate">
        <arg line="tip --template '{date|rfc3339date}'" />
    </exec>
    <exec executable="${hg.exec} outputproperty="revision">
        <arg line="tip --template '{rev}:{node|short}'" />
    </exec>
    <propertyfile file="${build.classes.dir}/version.properties">
        <entry key="build.version" value="${product.version}" />
        <entry key="build.time" value="${reldate}" />
        <entry key="build.revision" value="${revision}" />   
    </propertyfile>
</target>

이렇게 해두면 프로젝트를 빌드하여 배포판을 만들때 마다 version.properties가 생성이 되며, 이 파일이 jar나 war에 같이 묶이면 소스코드에서 읽어들여 버전 정보를 보여줄 수 있습니다. 아래는 이렇게 생성된 version.properties 파일입니다.


hgweb UI에서 체인지셋 ID 보이게 수정하기

hgweb은 중앙 레포지토리의 웹 유저인터페이스를 제공하는 모듈입니다. UI는 단순하기 그지 없습니다. 하지만 Mercurial의 철학은 모든 유저 인터페이스 (커맨드라인을 포함)는 커스터마이징 가능하게 한다는 겁니다. 그래서 당연히 hgweb의 UI도 수정할 수 있습니다.

그런데 저는 UI를 예쁘게 한다기 보다는 꼭 필요한 정보인데 빠진 것이 있어 추가를 하려는 겁니다. 기본적은 hgweb UI는 다음과 같습니다. 모자이크가 좀 과도한 건 양해 바랍니다. ㅡ,,ㅡ


중요한 건 개별 레포지토리 뷰의 필드가 시간, 커밋한 사람, 그리고 커밋 메시지 세개 뿐이라는 겁니다. 현장에 설치된 프로그램의 리비전 정보를 알아낸 다음 이것이 어떤 상태의 소스코드인지 알려면 각각의 체인지셋 상세 화면에 들어가야만 볼 수 있습니다.


저는 단지 레포지토리의 체인지셋 리스트에서 리비전 정보도 같이 뿌려지길 원했습니다. 그래서 hgweb의 UI를 어떻게 변경할 수 있는지 알아보기 시작했습니다. 다소 빈약한 문서이지만 방법은 있더군요.

hgweb의 UI를 변경할 수 있는 방법에 대한 문서는 여기를 참조하시기 바랍니다. 대략 정리해보면 원하는 템플릿을 찾아서 데이타 타입과 필터로 출력할 내용을 정의하면 되며, 룩앤필과 관련된 내용은 Fragments들을 수정하면 됩니다. 만들어진 스타일은 config파일에서 style로 지정하면 됩니다.

제가 했던 최소한의 수정 작업은 다음과 같습니다.

일단 hgweb이 기본적으로 제공하는 UI는 paper라는 테마입니다. 그래서 저는 paper 테마를 구성하는 파일들을 복사한 뒤에 mypaper라는 새로운 스타일을 만들고 일부분만 수정할 것입니다. hgweb의 테마는 Mercurial의 설치방법에 따라 다르지만 소스 배포판으로 설치한 경우 다음의 디렉토리에 있습니다. (Python의 버전이 아주 옛날거네요. 서버에 있는거라...)

/usr/lib/python2.4/site-packages/mercurial/templates

이 디렉토리에 paper라는 디렉토리가 있음을 알 수 있습니다. 이것을 mypaper라는 이름으로 복사를 합니다. 디렉토리 통째로 복사하는 거라 -R 옵션을 사용합니다.

cp  -R  paper  mypaper

이제 mypaper 디렉토리로 들어가서 shortlog.tmpl 템플릿 파일을 편집합니다. 체인지셋의 리스트가 나오는 부분은 bigtable이는 스타일을 검색하면 쉽게 찾을 수 있습니다. 여기에 rev라는 테이블 헤더를 하나 추가합니다. 스타일은 원래 author로 되어 있으나 이건 폭이 너무 넓으므로 그냥 age 스타일로 변경합니다.


테이블의 바디 부분은 shortlogentry를 참조하게 되어 있음을 볼 수 있습니다. 그러므로 shortlogentry.tmpl을 열어 다음과 같이 편집합니다. td 태그에 {rev}:{node | short}을 추가하는 겁니다. 스타일도 조정하구요.


이제 hgweb.config 파일을 수정할 차례입니다. 이전 글에서 작성한 hgweb.config 파일에 아래 두 줄을 추가합니다. 첫번째는 style을 mypaper로 지정하는 것이고 두번째는 캐쉬 설정을 끄는 것입니다. hgweb의 경우 성능을 위해 캐쉬를 적극적으로 사용합니다. 그러다보니 테마 파일을 수정하는 것이 캐쉬 때문에 즉각 반영되지 않아 불편합니다. 캐쉬를 끄면 다소 성능을 희생하더라도 수정한 것이 바로 반영되어 쾌적하게 UI를 개발할 수 있습니다.


이렇게 하면 다음과 같이 hgweb의 UI가 변경됩니다. 테이블의 두번째 필드로 rev 즉 체인지셋 ID가 추가되었음을 볼 수 있습니다. 현장에 설치된 프로그램의 체인지셋 ID를 알아내면 hgweb을 통해 어떤 내용이 추가된 소스코드인지 쉽게 파악할 수 있습니다.


이외에 Mercurial은 몇가지 기본적인 테마를 제공하며 별도로 테마를 다운로드 받아 설치할 수 있습니다. hgweb이 기본으로 제공하는 UI가 맘에 들지 않는 다면 이 링크를 확인해 보세요.

관련글 :
  - Mercurial 서버 hgweb 설치하기

참고 URL :
  - hgweb UI를 수정하는 방법 : http://mercurial.selenic.com/wiki/Theming
  - hgweb에서 사용 가능한 테마들 : http://mercurial.selenic.com/wiki/AvailableThemes

댓글 없음:

댓글 쓰기