svnserve를 흉내내는 git 저장소를 만들 수 있을까?

git 저장소 (혹은 git 호환 구현체)에서 svn 서버인척하려면 필요한 걸 리스팅 하는 중.

최근에 알게된 dulwich 프로젝트 덕분에 svn 프로토콜 일부를 구현하면 아마 기존 저장소를 git으로 옮기고, svn 클라이언트 쓰는 사람들에겐 ‘여전히 svn 저장소로 보이도록’ 만드는게 가능하지 않을까 싶다.

일단 떠오르는 것만 간단히. 잘못된 부분이나 추가할 사항 있으면 댓글로 좀(…).

svn은 revision 번호가 존재함. git에서 (내부적으로) 적당히 tag달아서 그걸 기준으로 해석해서 보내주면 되려나? (클라이언트 동작이긴하지만 git svn을 생각하면 어렵지 않을듯)

svn은 몇 가지 (미리 정해놓은) svn:… 형식의 프로퍼티를 지원한다. 이걸 어떻게 흉내낼까? link 가 없다고 가정(…)하면 상당히 쉬울 것 같긴한데…
(물론 여기서도 svn:externals 문제가 생길 거 같긴 하다)
executable mode야 별도로 처리하니까 (파일 퍼미션은 별도로 저장하긴하니까) svn:executable은 처리해줄 수 있고, svn:ignore 도 .gitignore나 .git/info/exclude 생각하면 어찌 되겠지. (.gitignore에 가깝군…)

svn:keywords 같이 서버 수준에서 처리하는 기능은 GG. 이건 어찌 해볼 도리가 없다.

svn branch (copy?)를 어떻게 처리할까? git 브랜치 처럼 처리하면 망하지 않을까?

MIME 타입 처리?
http://stackoverflow.com/questions/3537575/can-git-store-the-mime-type-of-a-file-like-svn-does-for-browsing-html
로 보아서는 잘 안될듯하다.

svn은 (대부분의 응답으로) unified diff를 보낸다. git에서 diff를 딸 때 unified diff로 따는 게 있었던 것 같으니 이건 큰 문제가 안될 것 같다.

svn은 저장소를 재귀적으로 정의한다. git은 저장소를 `하나로’ 본다. svn의 서브 디렉터리 요청이 오면 이걸 잘 처리할 방법이 필요한 듯. git에 이런 명령어(혹은 object 접근 방법)이 있던가?; log 랑 diff는 적당히 제한할 수 있었던 것 같으니 큰 문제 아닌가?

SVN 에서 서버 접근할 때 일반적으로 쓰는 명령이 뭐가 있을까?

update, commit, ls, copy, move, rm, … 정도인가?

팀 릴리즈 서버 준비

지지난 주에 적당한 CPU 파워의 잉여 머신이 하나 생겨서, 시험 삼아(?) 릴리즈 서버를 하나 구축해봤다. 근데 이게 OS 새로 올리고 프로그램 좀 설치하다 보면 부팅도 안되더라고 Orz

그래서 지난 주에 머신을 교체하고 (E8400이 Q8300이 되었다?), 이번 주 목요일에 설정을 끝냈다. 대략 다음의 툴을 깔아야 했다.

개발 도구 위주로 쓰면,

  • VS 2005/2008/2010 (…) 및 여기에 해당하는 Service Pack과 hotfix
  • Collabnet Subversion Server (Client 모듈만)
  • Debugging Tools For Windows (x64)
  • ActivePerl[1]
  • CruiseControl.NET 1.5[2]

…이 대부분은 Azyu 군이 수고해줬음. Azyu ㄳㄳ(…)

릴리즈 서버라고 거창하게 써놓긴 했지만 하는 일은 매우 단순하다. 모든 작업은 CCNet에 forced-build trigger로만 일어나게 하고, 다음 작업을 한다.

  1. 변경 없는 저장소 – 팀 내 기준으로는 주로 subersion – 코드를 가지고 release 할 설정으로 빌드하고 (msbuild 혹은 .bat 사용)
  2. 이에 대해서 소스 코드 인덱싱을 하고 (ssindex.cmd)
  3. 이 정보를 포함한 .pdb 및 바이너리 정보를 심볼 서버에 저장하고 (symstore)
  4. 적당한 디렉터리에 배포할 파일들을 모으거나 / 파일 목록을 받아서 이를 묶어 배포할 형태로 만들어, 적당한 레이블을 붙여서 .zip 형태로 다운로드 할 수 있게 만들기

…로 끝?

변경사항 마다 빌드하지 않는 건, 일단 release 빌드는 시간이 오래 걸리고, symstore를 압축 옵션을 주고 해도 하드 디스크 용량이 좀 빨리 줄어드는 문제가 있어서. 예전에도 한 번 당했고(…). 그리고 빌드에는 기능이 하나 더 들어갈 예정인데 – 특정 형태의 .rc와 .h를 조작해서 svn revision 정보를 넣을 예정; 코드 테스트는 끝났고 적용만 남은 상태 – 여하튼 변경 없는 코드를 clean;build 하는 형태로 동작하게 했다.

그리고 이에 대해서 소스 서버 정보와 심볼 서버 정보를 남기고 묶는다는 것.

.zip 형태로 묶는 기능을 CCNet의 Package publisher가 처리해줘서 매우 간단히 했음. 다만 이게 1.6에서 바뀌는 거긴 한데, <file>???</file> 하는 식으로 파일 목록을 받는데, 이게 특정 디렉터리와 그 하위 디렉터리를 전부 포함하라는 문법이 dir-name\**\*.* 이라서 (백 슬래시인가?) 좀 헤맸다.

Subversion revision을 label로 붙이려 했더니 CCNet 자체 기능으론 없더라. 대략 major#.minor#.patch#.rev 정도로 하려고 했는데 안되더라고; 하지만 CCNet 용으로 이런 기능을 하는 모듈을 svnrevisionlabeller라는 이름의 오픈 소스로 만들고 있더라. 원저자는 요즘 작업을 안 하는 듯 하지만 다른 사람들이 이슈에 패치 달고 있는 거 보니 재미있음(…). 이 모듈 써서 label 달고, 패키지 이름도 이걸로 처리되게 했다.

의외로 오래 안 걸리고 설정이 끝나서 흡족한 상태 XD

  1. 소스 인덱싱에서 사용함 []
  2. 1.6이 Release가 있긴 한데, 뉴스 페이지엔 없어서 일단 1.5 []

svn tag 만들 때 svn:externals 리비젼 고정하기 (2)

얼마 전에 svn 에서 tagging 할 때, svn:externals 로 링크(?)된 저장소들의 리비젼을 고정하는 스크립트를 작성했다. pysvn 이 있고 python 이 깔려있으면, svn copy 후, svn:externals 를 하나씩 찾아서 리비젼을 고정시키는 스크립트다.

이 스크립트의 문제: SVN은 svn:externals 문법이 하나가 아니고, 저 스크립트에선 해당 문법을 전부 지원하는건 아니다. 일단 1.5+를 생각하면 (최신은 1.6.x) 두 가지 형태의 문법이 있다.

  • example_lib –r 100 svn://example.com/repos/subdir 처럼 local-directory  [-r rev] svn-repository-uri 형태
  • file:///home/johndoe/repos/subdir johndoe_lib 처럼 [-r rev] svn-repository-uri local-directory 형태

일단 순서가 바뀐다. 이래서 맨 처음에 잘 해야;

그리고 두번째 형태는 pegged-form을 지원한다. 첫번째 형태는 pegged-form 을 지원하면 안됨(…).

svn-repoistory-uri@rev local-directory

같은 형태가 가능… 당연한거지만, –r rev 사이의 공백은 없어도 됨. 진짜 파싱하기 귀찮다. 그리고 마지막 변형으로, 두번째 형식에선 svn-repository-uri 를 상대 주소로 표현하는 문법이 들어갔다. ^/, ../, //, / 로 시작하면 각각 특정 저장소 주소의 상대 주소가 되는데,[1] 이 문법도 인식 해야함;

svn help propset에 나오는 svn:externals 설명에는 이전 문법에서도 상대 주소가 될 것 처럼 적어놨지만 (모호성 얘기도 그렇고), 내가 쓰는 svn 배포판(collab.net svn) 에선 오류를 내면서 처리 안된다. 혹시 다른 클라이언트 쓰고 있는 분은 테스트 좀(…).

여튼 이런 svn:externals 문법을 전부 인식하도록 스크립트 업데이트.

스크립트 링크: https://rein.kr/archive/frozentag.py

  1. svn help propset 하면 설명이 잘 나온다 []

저장소 서버 업그레이드 기록

지난 주 후반 부에 사용 중인 Perforce/Subversion 서버가 올라가 있는 서버를 업그레이드 했다.

기존 시스템이 메모리가 부족해서(4GiB RAM on Windows Server 2003 R2; x86), 메모리를 증설하고(->8GiB), OS를 새 것으로(Windows Server 2008 R2; x64) 만드는 작업이 이루어졌다.

물론 이러다 저장소에 문제 생기면 말 그대로 망하는 거라, 일단 백업 작업이 진행;

  1. Perforce 서버 셧다운
  2. 전체 Perforce 서버 백업 (depot, journal, checkpoint …)
  3. 백업된 데이터 복사. 이건 외장 하드 디스크[1] 로.
  4. Subversion 저장소 전체를 hotcopy
  5. hotcopy된 저장소 네트웍 백업. (별도 HDD인지라 3과 동시 진행되었다)

이 후에 복구 작업 시작. 사실 저 백업은 최악의 경우를 상정하고 한 거라, 서버 업그레이드 / 재 설치 중에 별다른 문제는 없어서 1~5는 그냥 백업만 한 번 더 한 일이 되었지만(…).

Perforce 서버 재 설치 후, 저장소 디렉터리를 지정하고 실행하는데 서버가 안 뜬다? $P4ROOT 환경 변수 문제도 아니었음. Perforce 버전이 올라가서 저장소 업그레이드를 수행해야하는 문제였다. 업그레이드 후 서버 정상 동작.

Perforce 서버 주말 백업 스크립트 테스트. 안 돈다? 뭐가 문제인가… $P4PORT 환경 변수를 클라이언트 프로그램에서 요구하더군. 이거 설정하고 정상 동작 시작.

Subversion 서버 재 설치. Collabnet 에서 subversion 관련 바이너리만 배포하던게 없어졌더라; 그래서 collabnet edge 서버 설치하고, 거기 있는 svnserve.exe 바이너리만 이용하기로(…).

sc create 로 서비스 등록하고 서비스 시작. svn.conf 를 인식하지 않는다(…). SASL이 동작하지 않아서 그냥 구 버젼 바이너리 가지고 있던걸로(32bit 이긴하지만; 이쪽은 메모리 문제가 없었으니),

오늘 출근해보니 SVN은 주말 백업이 이루어지지 않았다. 아마도 퍼포스 백업 스크립트가 먼저 돌아야하는데, 이게 백업 완료 후 리붓해서 그런 듯. 순서 바꾸고 다음 주 테스트를 기다려야.

+ 근데 퍼포스 서버는 왜 죽었을까? 아무도 사용하지 않을 시간 대 인데;;;

  1. 네트워크 복사로는 안정적으로 초당 10메가 바이트 이상 전송한다고 해도 대략 17 시간에서 18시간 걸릴 양이라 []

가상화 서버 비정상동작 + redmine 재설치

가상화해서 돌리던 서버 중 1대에 redmine 이 올라가 있었는데, 이 서버가 주기적으로 죽는다. 대략 매 달 4주 차의 토요일 오전에 사망함. 게다가 이건 물리서버를 내가 접근할 수도 없는데, 해당 쪽의 처리도 늦어서 어제는 오전 10시 경에 보고했는데도 실제로 서버가 살아난건 오후 5시 다 되서…

근데 죽는 타이밍이 너무 기가 막히니(…), 저거 누군가 해답을 알려나?

Redmine 사용하는 2개 팀이 그냥 서버 이전하기로 합의 보고, 어제 저녁에 이전 작업을 했다. 일단 예전에 써놓은 대로 작업을 진행했다. 근데 이게 왠걸. 일단 mysql 백업 복구하는 과정에서 막혔다.

잠시 삽질하다 원인을 찾아보니, 해당 서버에 2 개의 mysqld 가 떠 있었고, 나는 내가 설치하지 않은(bitnami-redmine-stack 1.0 설치) mysqld 랑 연결하고 있던 것 Orz. 일단 이렇게 백업은 복구. redmine files 디렉터리도 복사하고, 서버를 띄웠더니 svn 저장소 로그는 보이는데, repository browser 탭이 보이질 않는다.

로그를 뒤져도 딱히 이상한게 없고(이건 log-level 문제인가?), redmine 홈페이지의 트러블슈팅을 따라하다가, 다음 명령에서 문제가 있는 걸 확인.

> rake -f Rakefile redmine:fetch_changesets RAILS_ENV=”production”
svn: Cannot negotiate authentication mechanism
svn: Cannot negotiate authentication mechanism
svn: Cannot negotiate authentication mechanism

이런 식으로 대략 연동된 저장소 수 만큼 나오더라. 문제의 원인은 svn 저장소 중 일부가 SASL 인증을 써서 였음. bitnami redmine stack 에 포함된 svn 으론 SASL 인증이 안되더라… Collabnet 에서 나온 svn 클라이언트 설치하고, binary 바꿔치기해서 상황 종료.

svn tag만들 때 svn:externals 리비젼 고정하기

svn tag 생성할 때, svn:externals 의 revision 정보를 python 스크립트로 깔끔하게 정리하기.

요즘 특정 바이너리를 배포할 때 마다 아래의 과정을 반복하고 있다.

svn tag 생성

1. svn copy trunk tags/version-identifier 로 태그를 생성한다

2. svn:extenrals 로 연결된 외부 의존성[1] 의 revision을 고정한다. 예를 들어 library_AAA svn://some_host/some_dir/trunk/ 인 svn:externals 를, library_AAA –r 1000 svn://some_host/some_dir/trunk/ 하는 식으로 revision 을 고정한다.

만약 이 revision 고정하는 부분을 건너 뛰면, 소스 서버를 따로 쓴다면 모르겠지만(…), 그렇지 않은 경우 나중에 버그 트래킹할 때 귀찮아진다. 태깅된 소스 트리 자체야 버젼이 맞지만, svn:externals 의 revision이 고정되지 않은 경우, 해당 저장소의 최신 버젼(…)을 가져오게 되기 때문에 바이너리와 소스 트리 사이의 버젼이 달라져서 제대로 디버깅하기 힘들다.[2]

변경 사항 리뷰

diff previous_tag current_tag 의 값으로 – 물론 적당한 필터링이 들어가지만 – 뭐가 바뀌었나 다시 한 번 확인하고, svn log 를 참조해서 변경 사항 로그(changelog)를 작성한다. 그리고 나서 커밋.

빌드

msbuild 스크립트로 전체 바이너리 빌드 + zip 으로 압축 + 적당한 버젼 정보를 넣은 압축 파일 생성한 후, changelog와 함께 전달하면 끝.

하다보니…

사실 별로 복잡할 것도 없는 작업이다. 변경 사항 리뷰하는 것 이외에는 다 자동화 해도 될 것 같이 보인다. 근데 정작 이게 좀 귀찮은 이유는 svn copy 가 멍청할 정도로 느리다는 것과, svn:externals 를 하나하나 수정해야한다는 점. 이에 대한 대안으로 svn:externals에 리비젼을 자동으로 고정 [3] 시키면서 태깅하는 툴들을 찾아보니 두 가지 툴이 있었다.

하나는 svncopy.pl 이라는 perl 스크립트였지만, 내 환경(Win64 + 한글)에서 비정상 종료하는걸 몇 번 보고나서 GG. 또 하나는 svnxf 라는 .net framework 기반 툴인데, .net framework의 마이너 버젼 문제인지 내 머신에서 제대로 실행이 안됨 Orz.  .net 3.5 깔려있는데 안되고, 이건 redistributable 도 인터넷 연결을 요구해서 폐쇄망에서 작업하는 내 경우엔 그냥 GG.

그래서 그냥 pysvn 을 써서 간단한 python 스크립트를 작성했다. svn copy 후에 svn:externals 를 찾아다니면서 고정되지 않은 경우 revision을 현재 체크아웃된 revision 값으로 바꾼다. 파일 자체는 여기서 받아볼 수 있다.

일단 *nix 환경에서는 잘 돔. 이걸 가지고 회사에서도 한 번 돌려봐야겠다. os.path 로만 패스를 다뤘으니 아마 문제 안생기겠지(…). 문자열 인코딩 문제도 (아마도) 어떻게든 해결할 수 있을테니…

  1. 같은 저장소 안의 특정 디렉토리, 혹은 다른 저장소(의 특정디렉토리)를 간접적으로 가져오도록 설정하는 기능이다. 다른 svn 저장소를 svn 저장소 밑에 링크(?)하기 참고. []
  2. 물론 로그를 뒤져서 svn:externals 의 버젼을 맞춰도 되지만 왜 그런 귀찮은 짓을… []
  3. freezing이라 부르더라? []

프로그래머의 일상: 업그레이드의 때

하드웨어 업그레이드가 아니긴 하지만. 아래는 몇 일 된  SW 업그레이드 기록들.

Firefox 3.5

Endgadget Korea에서 보고 업데이트. Firefox는 회사 출근해서 켜고, 퇴근할 때나 꺼지기 때문에(…), 이런 기사를 보고 업데이트하면 내가 더 빠르다(???). 메모리 사용량은 조금 줄어들었나? 이건 잘 알 수 없긴한데[1] 속도는 좀 더 올라간 느낌.

하지만 이건 플러그인이 전부 활성화 안되서 일 수도 있다는 생각이 불현듯 든다(…).

TortoiseSVN

1.5.x 를 천년만년 쓰다가 1.6.x으로 버전업. 일종의 staging? 기능이 생긴 것 같다. 이게 없어서 git branch 로 관리했는데 일단 좀 써봐야 할듯하다. 다만 퍼포스 작업 관리나 git branch 처럼 편한지는 잘(이건 익숙한 정도의 문제도 있겠다)

CruiseControl.net

1.3.0 쓰다가 1.4.4로 넘어왔다. 이거 완전 신세계네?

  • MSBuild 출력을 제대로 해석한다. 예전에는 MSBuild에 별도의 로거를 붙여서 이걸 해석해서 올리도록 했다.
  • 대쉬 보드가 쓸만해졌다. 예전엔 각 빌드별 링크 수준의 페이지였는데 이젠 좀 더 상세한 정보를 제공한다.
  • 각 프로젝트 별 페이지에 일별 빌드 성공/실패 내역을 간략히 보여준다
  • 소스 저장소 변경 사항 추적 — 이건 사실 trac / redmine 같은 류의 이슈 트랙커에서도 지원하는거라 그냥 feature-creep 같은 느낌인데 …
  • 예전에 UnitTest++ 테스트 케이스를 추적(?)하려고하면 꽤나 귀찮은 xsl 수정을 거쳐야 했는데 + 두 개 이상의 UnitTest++ 테스트 케이스가 있으면 또 고쳐야하기도 하고. 그래서 아예 Google-Test로 전부 옮겨 버렸다. 이건 제대로 파싱도 하고(XSL 수정없이), 보기도 쉽게 되더라.
  • 빌드 통계 페이지가 그래프 수준의 뭔가를 제공한다. 빌드 시간 등등을 그래프로 볼 수 있긴한데, 실제 용도는 좀 불명.

eclipse galileo

이클립스가 3.5(갈릴레오) 버젼으로 올라갔다. 회사나 집에서 파이썬 개발툴(…)로 사명을 다하고 있는데, 집 컴(…중에 맥북)에 설치한 이클립스는 버젼이 세 개가 있더라. 코코아 32/64, 카본 32. 근데 코코아 버젼이랑 카본 버젼 차이가 뭐지? GUI 라이브러리가 다른 이상의 사용성의 차이가 있나?

맥용 프로그램들이 예쁘고 쓰기 좋은게 꽤 많긴하지만, 그런 프로그램의 수준으로 이클립스를 수정하면 크로스 플랫폼 개발툴로의 가치가 범용성 면에선 줄어들 것 같은데?

사실 정말 해야할 업데이트는 소스 저장소 서버의 SVN 서버 버젼 올리기 같지만 이건 언제 하지 Orz

  1. 회사에서 쓰는 빌어먹을 삼성 SDS의 인캅스(한 단어) 덕분에 메모리 사용량은 알기 힘들다. 이 뭣 같은 프로그램은 자기 멋대로 DLL을 링크해서 메모리 사용량도 올리고, 시스템 콜 링크 주소도 고친다 []

프로그래머의 일상: svn + CruiseControl.net 설정 삽질

오늘 십여일만에 회사에서 빌드 툴을 만지다가 — 출근은 어제부터 하긴 했지만 — 심각한 오류를 발견 -_-

빌드 서버에서 빌드하는 소프트웨어 군이 5개 정도 있는데, 그 중 하나가 몇 일 전부터 제대로 빌드되지 않고 있었다. 덤으로 레드마인에도 해당 프로젝트가 업데이트 안된다(저장소;repository 메뉴가 표시되다 맘).

원인은 알고보니 좀 어이없긴 했다.

  • 원래 해당 SVN 저장소에서는 authz를 안 쓰고 passwd만 쓴다
  • CI 툴이나 redmine에서 쓰는 read-only 계정이 있다
  • authz는 디렉토리 별 그룹 권한 혹은 사용자 권한을 지정하고 / passwd 파일은 인증을 담당한다[1]
  • 얼마 전에 기획팀 쪽 분에게 특정 리소스 파일 디렉토리 권한을 준다고 authz 설정을 했다
  • 딱히 그룹 설정이 없던 CI/redmine용 계정은 권한 설정을 안했다
  • 그렇다고 / 에 대한 기본 권한 설정도 안했다

그래서 CI/redmine용 계정은 해당 프로젝트 저장소를 접근할 수 없었던 것.

Orz

authz에 한 줄 추가하고 ( / 에 대한 r 권한) 모든게 돌아가기 시작하고 작업 종료.

  1. local-closed network이라 딱히 인증을 빡쎄게 하진 않는다 []