WindowsでKallitheaを動かす

以前、[WindowsでRhodeCode](http://qiita.com/wonderful_panda/items/a84dd411da69bd1f13ef) という記事を書いたのですが、古くなりすぎて今では間違いだらけだし、最近またセットアップする機会があったので改めて手順をまとめておきます。

## RhodeCode と Kallithea
RhodeCodeおよびKallitheaは、Git/Mercurial両対応のGitHubライクなアプリケーションです。
GitHub Issues相当の機能はありませんが、それ以外はPull Request、オンラインレビュー、Gistなどだいたい一通りの機能がそろっています。

RhodeCodeはもともとGPLv3下で開発されていたのですが、2.0からRhodeCode Enterpriseになり、条件によっては有償になりました。
かわりにRhodeCodeからForkされてGPLv3下で開発を継続しているのがKallitheaです。

今回はこのKallitheaのセットアップを行います。

## 前提条件とか
* アクセスするのは25人くらい。
* 既存の環境はできる限り汚したくない。
* リポジトリ内のファイルはUTF8とshift_jisが混在している。
* 今のところ日本語パスはないけど、一応日本語パスでも問題ないようにしておきたい。
* クライアントはすべてWindows。
* 外部からのアクセスはApacheでリバースプロキシを設定する。
* 認証はApache側で。
* Windowsサービス化する。

## Python環境の準備
残念ながらまだPython3には対応していないので、Python 2.7(2015.4現在の最新は2.7.9)をインストールします。
また、拡張モジュールのビルドのために、[Microsoft Visual C++ Compiler for Python 2.7 ](http://www.microsoft.com/en-us/download/details.aspx?id=44266) もインストールしておきます。

Kallitheaはvirtualenv環境下にインストールしたいので、virtualenvもインストールしておきます。

```console:console
> pip install virtualenv
```

## virtualenv環境の作成とモジュールのインストール

こんな感じで適当にディレクトリを作成します。

```
C:\Kallithea
├ HOME           ・・・config、dbなどを配置するディレクトリ
├ kallithea.sys  ・・・Pythonのモジュール類をインストールするディレクトリ(virtualenv)
└ repos          ・・・Git/Mercurialのリポジトリを配置するディレクトリ
```

Kallithea用のvirtualenvを作成してアクティブにします。

```console:console
> cd C:\Kallithea\kallithea.sys
> virtualenv .
> Script\activate
```

`pip install kallithea` で、基本的に必要なモジュールはすべてインストールされます。
RhodeCode 1.xの頃は追加で `chardet` をインストールしないとnon-asciiなファイルがうまく表示されなかったのですが、今でもそうなのかどうかは未確認なので一応それも入れておきます。
また、メンテナンス用のインタラクティブシェルを動かすために `ipython` が必要なのでこれも。

```console:console
> pip install kallithea chardet ipython
```

Lib\site-packages\sitecustomize.py を作成して、必要な環境変数の設定をしておきます。

```python:sitecustomize.py
import os
os.environ["HOME"] = 'C:/Kallithea/HOME'
os.environ["HGENCODING"] = 'cp932'
os.environ["PYTHON_EGG_CACHE"] = 'C:/Kallithea/.egg-cache'
```

## Kallitheaのインスタンスの作成

HOMEに移動して、 `paster make-config` と `paster setup-db` でKallitheaのインスタンスを作成します。

```console:console
> cd C:\Kallithea\HOME
> paster make-config Kallithea kallithea.ini
> paster setup-db kallithea.ini
Are you sure to destroy old database ? [y/n] y

Enter a valid absolute path to store repositories. All repositories in that path
will be added automatically: C:\Kallithea\repos

Specify admin username: iwata

Specify admin password (min 6 chars): ****

Confirm password: ****

Specify admin email: iwata@example.local
```

この段階で `paster serve kallithea.ini` を実行して、ブラウザから `http://localhost:5000` にアクセスすると、こんな感じで動いていることを確認できます。
![kallithea.png](https://qiita-image-store.s3.amazonaws.com/0/11109/7cf69b22-4d48-9092-5bcf-144cf8aac379.png)

IEで正しく表示されない場合は、とりあえず「イントラネットサイトを互換表示で表示する」設定をOFFにすると解決するかもしれません。

## Windowsサービスにする

[ここ](http://sourceforge.net/projects/pywin32/files/pywin32/) からpywin32のインストーラをダウンロードして、easy_installでインストールします。
http serverはcherrypyのものを使うことにしてそれもpipでインストール。

```console:console
> easy_install py2in32-xxx.xxx-py2.7.exe
> pip install cherrypy
```

こんな感じのスクリプトを用意して、

```python:service.py
import os
import win32serviceutil
import cherrypy

def make_kallithea_app():
    from paste.deploy import loadapp
    return loadapp("config:kallithea.ini", 
                   relative_to=os.path.dirname(__file__))

class KallitheaService(win32serviceutil.ServiceFramework):
    _svc_name_ = "Kallithea"
    _svc_display_name_ = "Kallithea service"
    _svc_description_ = "Kallithea service"

    def SvcDoRun(self):
        cherrypy.config.update({'service.socket_port': 5000})
        cherrypy.tree.graft(make_kallithea_app(), '/')
        cherrypy.engine.start()
        cherrypy.engine.block()

    def SvcStop(self):
        cherrypy.engine.exit()

if __name__ == '__main__':
    win32serviceutil.HandleCommandLine(KallitheaService)
```
こんな感じにインストール

```console:console
> python service.py install
```

## リバースプロキシと認証
フロントのApacheで認証して、Kallitheaにリバースプロキシするための設定をします。
この辺りRhodeCode 1.xの頃とは完全に別物ですね。

### Kallithea側の設定
Admin -> Authenticationから、まずは `kallithea.lib.auth_modules.auth_container` を enabled にして、save します。
話はそれるけどこの手のGUI、今の状態がenabledという意味なのか、クリックするとenabledになるという意味なのか分かりづらくてどうなんという感じ。

![container-auth-1.png](https://qiita-image-store.s3.amazonaws.com/0/11109/e4dcbe55-8252-524a-dd10-f98be1bb0777.png)

するとcontainer Authenticationプラグインの設定をするパネルが追加されるので、さらに Enabledにチェックを入れてもう一度save。

![container-auth-2.png](https://qiita-image-store.s3.amazonaws.com/0/11109/4429b98f-6a71-ac6f-9908-21bf0973634b.png)

認証の設定をWEBからできるの便利なんですが、設定を間違えると「缶詰の中に缶切り」状態になって困ったことになるので注意。

### Apache側の設定

Apache側でも、適当に認証とProxyの設定を。RewriteでREMOTE_USERの値をX-Forwarded-Userに設定してリクエストを転送するようにします。
ついでにヘッダにX-UA-Compatible="IE=Edge"を追加して、「イントラネットサイトを互換表示で表示する」設定のON/OFFにかかわらずIEで表示できるように。

```config:httpd.conf
<Location /kallithea>
    SetEnvIf X-Url-Scheme https HTTPS=1

    ProxyPass           http://127.0.0.1:5000
    ProxyPassReverse    http://127.0.0.1:5000

    AuthType Digest
    AuthName "Login to Kallithea"
    AuthDigestProvider file
    AuthUserFile /path/to/digest/file
    BrowserMatch "MSIE|Chrome" AuthDigestEnableQueryStringHack=On
    Require valid-user
    
    RequestHeader unset X-Forwarded-User
    RewriteEngine On
    RewriteCond %{LA-U:REMOTE_USER} (.+)
    RewriteRule .* - [E=RU:%1]
    RequestHeader set X-Forwarded-User %{RU}e

    Header set X-UA-Compatible: IE=Edge
</Location>
```

### ユーザの登録

さて、RhodeCode 1.xの頃はApache(もしくはその他のコンテナ)で認証する場合もRhodeCode側でユーザを作っておく必要があったのですが、今はApache側で認証した時点で自動的にユーザが登録されます。
逆に、Kallithea側でユーザを作ってしまうと、同じ名前のアカウントがコンテナ側にあってもコンテナ認証はできません。(コンテナで認証した後、もう一度Kallitheaのログイン画面での認証を求められる)
なので、

1. `http://localhost/kallithea`にアクセスして(コンテナ認証になる)、Admin権限を付与したいユーザIDでApacheにログインする
2. `http://localhost:5000`にアクセスして(Kallithea認証になる)、setup-db時に作ったAdminユーザでログインし、今作ったユーザにAdmin権限を付与する

という手順で、コンテナ認証できるAdminユーザを一つ作っておくといいと思います。

ここまででとりあえずセットアップ完了。

## (おまけ)困った時のはなし
認証の設定まちがえると困ったことになるという話をしました。
そういう場合はインタラクティブシェルからデータを修復する必要があります。(多分。他の手があるかもしれないけど。)

ishellコマンドでipythonベースのインタラクティブシェルを起動できるので、

```console:console
> paster ishell kallithea.ini
```

認証の設定をデフォルトに戻すならこんな感じのスクリプトを実行します。

```python:ishell
for k, v in (('auth_plugins', u'kallithea.lib.auth_modules.auth_internal'),
             ('auth_internal_enabled', False)):
    Session().add(Setting.create_or_update(k, v))
Session().commit()
```

他にも、指定のユーザにAdmin権限をつけたり

```python:ishell
u = User.get_by_username('iwata')
u.admin = True
Session().add(u)
Session().commit()
```
Kallitheaで登録したユーザをコンテナ認証できるようにに変えたり

```python:ishell
u = User.get_by_username('iwata')
u.extern_type = u'container'
u.extern_name = u'iwata'
Session().add(u)
Session().commit()
```
色々。もちろんAt your own riskですが。