mercurial-backlog拡張を作った中で知ったこと
このエントリはhttp://partake.in/events/902cd6d9-0215-4ea3-b51f-b8ff32e56426:title=の16日目です。ちょっと遅刻しました。。
仕事でBacklogというプロジェクト管理ツールを利用しています。とても便利なツールなのですが、Subversionのリポジトリしか連携出来ないという欠点があります。
というわけで、BacklogとMercurialのリポジトリを連携するためのmercurial-backlog拡張を作成してみました。
詳しい使い方はリンク先を見て貰うという事で、ここではmercurial拡張を作成する中で知ったことをいくつか紹介します。
(mercurial拡張の作り方についてはhttp://safx-dev.blogspot.com/2011/05/mercurial.html:title=が詳しいです)
ディレクトリ構成
mercurial拡張のディレクトリ構成は次の様になっている事が多いです。
{path-to-extension-root}/ |-- README |-- LISENCE |-- {extension-name}/ |-- __init__.py # エントリポイント |-- tests/ # 拡張のテスト
このような構成の場合、hgrcに次の様に記述して拡張を有効にします。
[extensions] backlog = {path-to-extension-root}/{extension-name}
依存するライブラリについて
setup.pyをしっかり書いてpythonのライブラリやツールのようにインストールさせれば良いのですが、大規模な拡張でないとあまりうまみがありません。
hg-reviewを見てみるとFlaskなどのライブラリをバンドルしています。これを参考にしてライブラリも一緒に配布してしまいましょう。
hg-reviewの例では次の様にbundled以下にライブラリを含んでいます。
hg-review/ |-- README |-- LISENCE |-- bundled/ |-- flask |-- jinja2 |-- werkzeug |-- simplejson |-- markdown2 |-- review/ |-- __init__.py |-- ...
hg-reviewでは次のコードをバンドルしたライブラリをpathに含めています。
import sys, os def unbundle(): package_path = os.path.split(os.path.realpath(__file__))[0] template_path = os.path.join(package_path, 'web_templates') media_path = os.path.join(package_path, 'web_media') top_path = os.path.split(package_path)[0] bundled_path = os.path.join(top_path, 'bundled') flask_path = os.path.join(bundled_path, 'flask') jinja2_path = os.path.join(bundled_path, 'jinja2') werkzeug_path = os.path.join(bundled_path, 'werkzeug') simplejson_path = os.path.join(bundled_path, 'simplejson') markdown2_path = os.path.join(bundled_path, 'markdown2', 'lib') sys.path.insert(0, flask_path) sys.path.insert(0, werkzeug_path) sys.path.insert(0, jinja2_path) sys.path.insert(0, simplejson_path) sys.path.insert(0, markdown2_path) unbundle()
これを改変して__init__.pyなどの先頭で呼び出すと良いでしょう。mercurial-backlogでは次のコードでバンドルしているbackloglibを有効にしています。
def unbundle(): package_path = os.path.split(os.path.realpath(__file__))[0] top_path = os.path.split(package_path)[0] bundled_path = os.path.join(top_path, 'bundled') backloglib_path = os.path.join(bundled_path, 'backloglib-0.2.1', 'src') sys.path.insert(0, backloglib_path) unbundle()
フックについて
拡張機能としてフックを作成する場合はhookメソッドを__init__.pyにimportしましょう。たとえば__init__.pyが次のようなファイルの場合、
# /path/to/hook-example/example/__init__.py def hook(ui, repo, hooktype, node=None, source=None, **kwargs): """hook example""" ui.write(hooktype) ui.write(' called\n')
次の様にフックを設定出来ます。
[extensions] example = /path/to/hook-example/example [hook] pre-commit.example = python:example.hook
バンドルされてる○○拡張みたいなことをしたい
○○拡張のコードを読みましょう。mercurial-backlogの場合、引数のリビジョンを取り出すときのコードはtransplant拡張から取ってきたり、
# revsetsなども解釈してリビジョン番号を取り出す for rev in scmutil.revrange(repo, revs): bn.node(repo[rev])
フックの定義はnotify拡張から取ってきたり
ctx = repo[node] if hooktype == 'changegroup': # pushされた変更を全て取り出す start, end = ctx.rev(), len(repo) for rev in xrange(start, end): bn.node(repo[rev])
notify拡張の様にhgrcから設定を取得したり
self.spacename = self.ui.config('backlog', 'spacename') self.username = self.ui.config('backlog', 'username') self.password = self.ui.config('backlog', 'password') self.key = self.ui.config('backlog', 'key');
しています。バンドルされている拡張はスニペットとして最適です。
まとめ
mercurial拡張、フックはとても簡単に作成出来ますし、参考になるコードはMercurial自体にたくさん含まれています。
参考文献
Mercurial拡張の作り方は次の資料が詳しいです。
- http://mercurial.selenic.com/wiki/WritingExtensions:title=
- http://mercurial.selenic.com/wiki/MercurialApi:title=
- http://safx-dev.blogspot.com/2011/05/mercurial.html:title=
mercurial-backlogは次のコードを参考にしています。ありがとうございます。