TracLightningでMercurialを使ってみた
サーバリプレースのついでに、自己流でセットアップしたTrac0.10からTracLightning2.3.2に移行してみた。ついでにTracをmod_wsgiで動かしてみたり、Mercurialと連携させてみたり、チケットの自動クローズとかできるようにしてみたのでメモしておく。
環境はこんな感じ。
- Windows Server 2003 R2 Standard Edition
- TracLightning 2.3.2
- mod_wsgi 2.3
- Mercurial 1.4
- TracMercurial 0.11.0.8
- MinGW 5.1.6
TracLightningをmod_wsgiで動かす
参考URL
これは参考URLのまま。
1.2つ目のURLからmod_wsgi.soをダウンロードして、C:\TracLight\CollabNetSVN\httpd\modulesにコピー
2.C:\TracLight\CollabNetSVN\httpd\conf\httpd.confの書き換え
--- a/CollabNetSVN/httpd/conf/httpd.conf Fri Nov 20 14:55:06 2009 +0900 +++ b/CollabNetSVN/httpd/conf/httpd.conf Fri Nov 20 15:05:18 2009 +0900 @@ -496,7 +496,7 @@ LoadFile "../../python/python25.dll" LoadModule authz_svn_module modules/mod_authz_svn.so LoadModule dav_svn_module modules/mod_dav_svn.so -LoadModule python_module modules/mod_python.so +#LoadModule python_module modules/mod_python.so #LoadModule fcgid_module modules/mod_fcgid.so @@ -534,14 +534,19 @@ # PythonDebug On #DefaultInitEnv TRAC_ENV_PARENT_DIR "C:\TracLight\projects\trac" -ScriptAlias /trac "C:\TracLight\CollabNetSVN\httpd\cgi-bin\trac.cgi" +#ScriptAlias /trac "C:\TracLight\CollabNetSVN\httpd\cgi-bin\trac.cgi" +LoadModule wsgi_module modules/mod_wsgi.so +WSGIScriptAlias /trac "C:/TracLight/CollabNetSVN/httpd/cgi-bin/trac.wsgi" <Location "/trac"> - SetHandler mod_python - PythonHandler trac.web.modpython_frontend - PythonOption TracEnvParentDir "C:\TracLight\projects\trac" - PythonOption TracUriRoot /trac - PythonOption PYTHON_EGG_CACHE "C:\TracLight\projects\.egg-cache" +# SetHandler mod_python +# PythonHandler trac.web.modpython_frontend +# PythonOption TracEnvParentDir "C:\TracLight\projects\trac" +# PythonOption TracUriRoot /trac +# PythonOption PYTHON_EGG_CACHE "C:\TracLight\projects\.egg-cache" + WSGIApplicationGroup %{GLOBAL} + Order deny,allow + Allow from all </Location> <Location "/svn/">
3.C:\TracLight\CollabNetSVN\httpd\cgi-bin\trac.wsgiを作成
#!C:\TracLight/python/python.exe # -*- coding: utf-8 -*- # import sys sys.stdout = sys.stderr import os os.environ['TRAC_ENV_PARENT_DIR'] = 'C:/TracLight/projects/trac' os.environ['PYTHON_EGG_CACHE'] = 'C:/TracLight/projects/.egg-cache' import trac.web.main application = trac.web.main.dispatch_request
Mercurialとの連携
参考URL
Mercurialはバイナリインストーラは使わず、easy_installでインストールする。でないと、TracMercurialを入れても「Unsupported version control system "hg"」と言われてしまう。
なので、まずはeasy_installから。ez_setup.pyをダウンロードして、コマンドラインから以下のコマンドを実行する。
python ez_setup.py
次に、Mercurialをコンパイルできるようにするため、2つ目のURLを参考にMinGWをインストール。
環境変数PATHに以下の設定を追加して、
%PATH%;C:\MinGW\bin;
C:\TracLight\python\Lib\distutilsの中に「distutils.cfg」というファイルを作成し、そのファイルに下記を記述して保存。
[build] compiler=mingw32
あとは以下のコマンドでMercurialがインストールできる。
easy_install mercurial
次はTracMercurial – The Trac Project
easy_install -UZ http://svn.edgewall.com/repos/trac/sandbox/mercurial-plugin-0.11
SVNでチェックアウトできない場合は、ソースをダウンロードして、以下のコマンドでインストールする。
python setup.py install
最後にtrac.iniを変更する。
Mercurialのリポジトリを「C:\TracLight\projects\hg」に作ることにして、「C:\TracLight\projects\hg\SampleProject」にリポジトリを作成。
--- a/projects/trac/SampleProject/conf/trac.ini Fri Nov 20 15:05:18 2009 +0900 +++ b/projects/trac/SampleProject/conf/trac.ini Fri Nov 20 16:59:36 2009 +0900 @@ -29,7 +29,8 @@ resolve.permissions = TICKET_MODIFY [trac] -repository_dir = C:\TracLight\projects/svn/SampleProject +repository_dir = C:\TracLight\projects/hg/SampleProject +repository_type = hg authz_module_name = SampleProject [mainnav] @@ -53,6 +54,7 @@ timingandestimationplugin.ticket_daemon.timetrackingticketobserver = disabled timingandestimationplugin.ticket_webui.ticketwebuiaddon = disabled timingandestimationplugin.webui.timingestimationandbillingpage = disabled +tracext.hg.* = enabled [ticket-custom] due_assign = text @@ -73,8 +75,11 @@ private_wikis = SECRET #[searchhyperestraier] #index_path = C:\TracLight\casket #replace_left = C:\TracLight\rep #url_left = / +[hg] +show_rev = yes +node_format = short
チェンジセット1がない、とエラーにはなるが、コミットすれば問題なく使えるようになる。
チケットの自動クローズ
参考URL
- MercurialでPythonのフックを実行する方法 / PythonからOutputzにPOSTする方法 - SumiTomohikoの日記
- Mercurial のウェブインタフェースを mod_wsgi にのせてみた - daily dayflower
- Mercurial 勉強中 (7) - Web 経由の push と HTTP 認証 - daily dayflower
- Mercurial 勉強中 (8) - hook を Trac と絡めて - daily dayflower
- Mercurial: "Handling repository events with hooks" を読む | Inside ASCADE
これが一番苦労した。なぜかバッチファイルはどんなものを指定しても実行してくれない。権限の関係?
悩んでても解決しないので、1個目のURLを参考に別の方法でアプローチする。
まずMercurialリポジトリをhttpで公開する。
2個目と3個目のURLを参考に、C:/TracLight/CollabNetSVN/httpd/cgi-bin/hgweb.wsgiを作成して、
import os os.environ['HGENCODING'] = 'UTF-8' import mercurial.hg as hg from mercurial.ui import ui from mercurial.hgweb.hgweb_mod import hgweb from mercurial.hgweb.hgwebdir_mod import hgwebdir from mercurial.hgweb.request import wsgiapplication def listdir_dironly(base_dir): results = [] # prepare for failure for root, dirs, files in os.walk(base_dir): results = map(lambda d: os.path.join(root, d), dirs) dirs[:] = [] results.sort() return results def get_repo_for_path(path): return hg.repository(ui(interactive=False, report_untrusted=False), path=path) def isrepo(path): if False: # too redundant try: get_repo_for_path(path) return True except hg.RepoError: return False else: return os.path.isdir(os.path.join(path, '.hg')) def make_hgweb_maker(path): return lambda: hgweb(path, os.path.split(path)[1]) #return lambda: hgweb(get_repo_for_path(path)) def make_hgwebdir_maker(path): dirs = listdir_dironly(path) repos = [(os.path.split(dir)[1], dir) for dir in dirs] return lambda: hgwebdir(repos) def hgweb_wsgiapp(path): if isrepo(path): return wsgiapplication(make_hgweb_maker(path)) else: return wsgiapplication(make_hgwebdir_maker(path)) # for WSGI def application(environ, start_response): def filter_headers(status, response_headers): # stringify header content headers = [(key, str(value)) for key, value in response_headers] return start_response(status, headers) def error_dialog(message): headers = [('Content-Type', 'text/plain'), ('Content-Length', str(len(message)))] start_response('500 Internal Server Error', headers) return [message] if 'hgweb.reposdir' in environ: reposdir = environ['hgweb.reposdir'] else: return error_dialog("You must specify 'hgweb.reposdir' environment") wsgiapp = hgweb_wsgiapp(reposdir) return wsgiapp(environ, filter_headers) # for CGI if os.environ.get('GATEWAY_INTERFACE', '').startswith('CGI/'): import cgitb cgitb.enable() import mercurial.hgweb.wsgicgi as wsgicgi wsgicgi.launch(hgweb_wsgiapp('/path/to/repos'))
さらにhttpd.confを修正する。この時点でHTTP経由でリポジトリが参照できるはず。
--- a/CollabNetSVN/httpd/conf/httpd.conf Fri Nov 20 16:59:36 2009 +0900 +++ b/CollabNetSVN/httpd/conf/httpd.conf Fri Nov 20 17:30:12 2009 +0900 @@ -588,3 +588,19 @@ ProxyPass http://127.0.0.1:8010/hudson ProxyPassReverse http://127.0.0.1:8010/hudson </Location> + +WSGIScriptAlias /hg "C:/TracLight/CollabNetSVN/httpd/cgi-bin/hgweb.wsgi" + +<Location "/hg"> + WSGIApplicationGroup %{GLOBAL} + SetEnv hgweb.reposdir "C:/TracLight/projects/hg/" + Order deny,allow + Allow from all + + AuthType Digest + AuthName trac + AuthUserFile "C:\TracLight\projects\trac.htdigest" + <LimitExcept GET PROPFIND OPTIONS REPORT> + Require valid-user + </LimitExcept> +</Location>
次に4個目のURLを参考に、C:\TracLight\python\Lib\site-packages\hg_post_commit.pyを作成する。これは複数リポジトリがあってもいいように、少し修正してある。
# -*- encoding: utf-8 -*- hook_script = 'C:/TracLight/python-lib/trac/contrib/trac-post-commit-hook' # style:: 'short', 'long', or 'number' changeset_id_style = 'short' import os def invoke_trac_hook(trac_env, rev): def _make_smart_rev(trac_env, rev): if changeset_id_style == 'long': return rev else: import trac.env env = trac.env.open_environment(trac_env) # instance of mercurial.hg.repository repo = env.get_repository().repo ctx = repo.changectx(rev) if changeset_id_style == 'short': import mercurial.node return mercurial.node.short(ctx.node()) else: # 'number' return str(ctx.rev()) smart_rev = _make_smart_rev(trac_env, rev) f = open(hook_script, 'r') try: import imp ext = os.path.splitext(hook_script)[1] m = imp.load_module('trac_hook', f, f.name, (ext, f.mode, imp.PY_SOURCE)) m.CommitHook(project=trac_env, rev=smart_rev) finally: f.close() def hook(ui, repo, **kwargs): hg_dir, _ = os.path.split(repo.path) hg_root_dir, project_name = os.path.split(hg_dir) trac_dir = os.path.join(os.path.join(hg_root_dir, os.path.pardir, 'trac', project_name)) if 'PYTHON_EGG_CACHE' not in os.environ: os.environ['PYTHON_EGG_CACHE'] = os.path.join(trac_dir, '.egg-cache') invoke_trac_hook(trac_dir, kwargs.get('node'))
最後に「C:\TracLight\projects\hg\SampleProject\.hg\hgrc」を作成して、上記のスクリプトを指定する。
[hooks] incoming.trac = python:hg_post_commit.hook [web] push_ssl = false allow_push = * deny_push = unauthenticated_user
あとはSVNのときと同じようにコミットしてpushすれば、チケットが自動クローズされる。