2009年10月15日
Debianのwebwml CVSをgitで処理する (コミッタと内部処理編)
翻訳作業者編に引き続き、今度は、どうやって実装しているのかなどに興味のある方向けのお話。まずは概念図。
+-------------+ パッチメール送付
|HTTPユーザ環境| → debian-wwwメーリングリスト
+-------------+
↑ clone/pull
git.debian.or.jp ----------------+ clone/pull +-------------+
| CVS→gitの同期(cron) | → |sshユーザ環境|
| gitリポジトリの提供(ssh, HTTP) | ← +-------------+
+--------------------------------+ push
push↑ ↓pull
kmuto環境 ------------------------------+
| gitリポジトリのクローン(ssh) |
| git→CVSの同期(webwml-patch-commmit) |
+---------------------------------------+
作成したコードはSubversionリポジトリ「https://svn.debian.or.jp/repos/webwml-sync/trunk」にある。
git.debian.or.jp上で行う「CVS→gitの同期」は、syncというシェルスクリプトとwebwml-git-committer.rbというRubyスクリプトが担当している。準備としては、作業ディレクトリ上に、Debian.orgのCVSリポジトリをread only(pserver)経由でwebwmlという名前で置き、同期用にGitリポジトリからクローンした作業ツリーをwebwml-gitworkという名前で同様に置く。
そして、syncスクリプトで、CVSとGit作業ツリーをまず最新に更新し、CVS内のファイルをrsyncに-uオプションを付けて同期処理をする。続いてwebwml-git-committer.rbスクリプトをGit作業ツリーに対して実行し、追加されたものを調査する。既存ファイルの更新や削除であればgitが面倒を見てくれるのだが、新規についてはこちらで指示しなければならない。git ls-filesを使えばリポジトリ管理外のものが?マークで表されるので、これを新規ファイルと見なしてgit addで追加する。ここまでできたら、git commitの-aオプションでまだステージしていない残存ファイル、つまり既存ファイルが更新されたものや削除されたものを含めて全部コミットする。最後にpushを実行してリポジトリに反映し、CVS→gitの処理は完了だ。syncはヒューリスティックに15分ごとにcron実行させている。一応安全のためにロックなども作るようにしている。
Gitリポジトリでは今のところ複雑な処理はかけていない。HTTP向けも単にリポジトリのbareを外に見せているだけ。フックとしてpost-receiveでプッシュされたコミットメールをdebian-wwwメーリングリストに送るのと、post-updateでHTTP向けにgit-update-server-infoを実行しているくらい。今後はパスのチェックやエンコーディングテストなども入れたほうがよいかとは思っている。
さて、翻訳作業者にいただいた成果は、速やかにCVSに反映したい。 プッシュされたものならプルで、パッチなら適用・コミット・プッシュして、CVSに反映したいコミット履歴ができたとしよう。Debian.orgの(Aliothにあるリポジトリにssh経由で書き込み可能な)CVSツリーをwebwml、Gitの作業ツリーをwebwml-work、パッチ準備ディレクトリにpatches、パッチ済みディレクトリにpatchedといった形で用意しておく。
ここでwebwml-patch-commitの出番となる。これはシェルスクリプトで(Rubyで最初書いていたのだけれど、外部コマンドをたくさん呼ぶならシェルスクリプトのほうが扱いやすいという判断に至った)、履歴からパッチを生成し、パッチの確認・CVSへの適用・コミットをインタラクティブに進めていくもの。
実行すると、CVSとGitの最新への更新を行った後、指定のGit履歴ハッシュ値(lastcommitというファイル、または引数で指定)をgit format-patchに-oオプションでパッチ生成ディレクトリ指定を付けて渡す。format-patchは指定の履歴「より後」のコミットを「数値-*.patch」のファイル名で作ってくれる。
後は、各パッチの処理。先頭行にパッチ作成者情報があるので、これを見てCVS→Gitの同期に使っているエージェントコミッタだったらそのパッチは無視する(「パッチ済み」に移動する)。 そうでないなら、まずはパッチをCVSに適用テスト(patch --dry-run)してみる。ここでエラーが出たら実行自体を止めるか、そのパッチは保留にして先に進むかを選ぶことになる。
正常に適用できるようなら、コミットログと「適用する」「適用しない(保留)」「パッチをページャで表示」「適用しない(適用済みに移動)」の質問を提示して(実際には「Apply? [y/n/p/s]」と簡略化してるけど)、処理をコミッタが決める。普通は「適用する」を選ぶことになるわけだが、これでCVSにパッチが適用され、Gitに入れたコミットログをそのまま使ってCVSをコミットする。適用したパッチは「パッチ済み」ディレクトリに移動。Gitで行われたファイルの追加/削除については、git whatchangedを使って5列目がAあるいはDかどうかを調べて判断している。
「パッチ済み」のものは同時にその履歴ハッシュ値をlastcommitに書き込むようにしている。こうすることで、次回webwml-patch-commitを実行するときにどこまで処理していたかを確認に回る必要がなくなるわけだ。
仕組みとしてはだいたいこれでおしまい。日本語限定というわけではなく、変数targetlangをいじればほかの言語にも流用できるようにしてある。万が一のときを考えて、foolproof的な仕組みはいくつか入れているし、今後も追加したいと考えているけど、シェルスクリプトだとそろそろ厳しいなぁと感じるのも事実。数日で作ったにしてはちゃんと機能しているし、コミッタとしての作業はずっと楽になったし、翻訳作業者も広く募れる体制になったし、とりあえずは良かった、Git万歳ということで。
ttmnr's status on Sunday, 25-Oct-09 10:08:48 UTC
reading: KeN's GNU/Linux Diary | Debianのwebwml CVSをgitで処理する (コミッタと内部処理編) http://j.mp/1DFBAr
メッセージ: Ready to post a comment.
![[hatena]](http://d.hatena.ne.jp/images/b_entry_de.gif)
![[g15.jp]](http://g15.jp/powered-g15.png)
![[RSS]](/d/rss10.png)