GitBucket に参加しよう

この文章は、主にgitbucketやgithubが大好き、俺専用githubを作りたい、という人たちに向けて書いた。
向いてない人はきちんと詳細・原理まで説明しないと使えない人、向いている人はなんか分からないけれどコピペしたら動いたからがんがん機能かくで！という人。
java は知っているけれど scalaは知らない人に向けて書いていますがscalaの説明は最低限しかしないので、別途リファレンスや入門書などを参考にすること。javaもscalaも知らなくてもrubyやphpやjavascriptを知っていれば何とかなるはず。
webアプリケーション開発経験が無いとつらい。

# gitbucket とは

gitbucket とは github 上で開発されているオープンソースの github っぽいgitリポジトリサーバです。

https://github.com/gitbucket/gitbucket

star が 6000 もついてる！
開発者は日本人が中心で、[日本語のチャットルーム](https://gitter.im/gitbucket/gitbucket_ja)で相談できる！
インストールが超簡単。開発も簡単

利用者向けのドキュメントは[公式のwiki](https://github.com/gitbucket/gitbucket/wiki)、開発者用のドキュメントは[ソースのdocsディレクトリ](https://github.com/gitbucket/gitbucket/tree/master/doc)にある。

# やってみよう

git のインストール、java/jdk (1.8) のインストール、java, javac にパスを通す（←説明しませんので分からなければググって）。ちなみに私は

```bat
SET JAVA_HOME=c:\Program Files\Java\jdk1.8.0_92
SET PATH=%JAVA_HOME%\bin;%PATH%
SET _JAVA_OPTIONS="-Dinput.encoding=Cp1252"
SET JREBEL=c:/bin/jrebel/jrebel.jar
```

のような環境設定用のバッチファイルを用意している。

できたら、シェル(bash, cmd.exe) から

```bash
# ソースを取り込んで
git clone https://github.com/gitbucket/gitbucket.git
# ソースディレクトリに移動して
cd gitbucket
# 実行!
sbt ~jetty:start
# ↑ bash の場合は sbt.sh を実行
```

すると

```
Java HotSpot(TM) 64-Bit Server VM warning: ignoring option MaxPermSize=256m; support was removed in 8.0
Listening for transport dt_socket at address: 5005
[info] Loading global plugins from C:\Users\nazoking\.sbt\0.13\plugins
[info] Loading project definition from C:\Users\nazoking\git\gitbucket\project
:
```

のようなログが流れて、たくさんのライブラリをダウンロードし、たくさんのファイルをscalaのコンパイルする。結構（初回は特に）時間がかかるので、アニメでも見て気を紛らわせましょう。Aパートが終わる頃には

```
：
2017-12-29 17:27:49.625:WARN:oejsh.RequestLogHandler:main: !RequestLog
2017-12-29 17:27:49.685:INFO:oejs.ServerConnector:main: Started ServerConnector@7c6748bc{HTTP/1.1}{0.0.0.0:8080}
2017-12-29 17:27:49.685:INFO:oejs.Server:main: Started @8625ms
```
のようなメッセージが見えるはずだ。 `http://localhost:8080/` にアクセスしてみよう。gitbucket が走ってる！超簡単。

普通にリポジトリを作ったり、アカウントを発行したりできる。

更に、この状態でサーバは自働でリロードされる。試しに `src/main/twirl/gitbucket/core/main.scala.html` のタイトル等を書き換えて、ブラウザをリロードしてみよう。修正が反映されるはずだ。

とりあえず以上で、 gitbucket を修正・改造することが可能だ。`sbt ~jetty:start` は ENTER 等で終了することができる。


## sbt インタラクティブコンソール

OSのシェルから

```
sbt ~jetty:start
```

と打ち込んだとき、 [sbt](http://www.scala-sbt.org/) というプログラムが動いている。sbt とは java で言うところの maven + ant 、c で言うところの make ＋ライブラリ管理機能、ruby で言うところの gems + bundler + rake のようなものだ。
そして、インタラクティブコンソール（シェルのようなもの）も持っている。

コマンドラインからオプションなしで

```
sbt
```

とだけ打ち込んでみよう。

```
Java HotSpot(TM) 64-Bit Server VM warning: ignoring option MaxPermSize=256m; support was removed in 8.0
Listening for transport dt_socket at address: 5005
[info] Loading global plugins from C:\Users\nazoking\.sbt\0.13\plugins
[info] Loading project definition from C:\Users\nazoking\git\gitbucket\project
[info] Set current project to gitbucket (in build file:/C:/Users/nazoking/git/gitbucket/)
>
```

のような画面になって入力をうながすプロンプトが出るかと思う。これで、 sbt のコンソールに入れた。ここで `jetty:start` と打ち込んで ENTER すると、最初にやったようにサーバが起動する。
起動したまま、再びコンソールに戻る（ログに隠れてプロンプトが見にくくなっているが）
`jetty:stop` で、サーバを止めることができる。

ここで、 `jetty:start` `jetty:stop` は、sbtのタスクと呼ばれる。

`jetty:start` はjettyサーバを起動するタスクだ（jetty は java 製の軽量webサーバ）

タスクの前に `~` を付けると、「ソースを監視して、に変更があったらもう一度タスクを実行する」のような意味になる。
つまり最初に実行した `sbt ~jetty:start` は「sbtから jetty:startタスクを実行する。その後ソースの変更を監視して、変更があれば再度 jetty:start タスクを実行する」のような意味になる（ jetty:start は二重起動しようとすると最初に走っている jettey を止めてから新たに起動する）。

sbt のインタラクティブコンソールからは `exit` で抜けることができる（このとき jetty は終了していなければ終了する）。

sbt という存在があること、sbt の中でタスクを実行することができること、ファイルを監視してタスクを再実行する機能もあること、を、とりあえず覚えておこう。

sbt については [始める sbt](http://www.scala-sbt.org/0.13/docs/ja/Getting-Started.html) という公式ドキュメントがあるので、知りたくなったら読めばいいけど gitbucket をいじりたい、という上で知る必要のあることは（とりあえずは）それほど無い（既に設定されているので）。


## windows 用追加設定

sbt インタラクティブコンソールは 上下矢印キーでヒストリーをさかのぼることができるのだが、 windows だと文字化けする。起動時に `-Dinput.encoding=Cp1252` というオプションを指定するとこの文字化けが解消される。最初に紹介した環境変数設定スクリプトで `SET _JAVA_OPTIONS="-Dinput.encoding=Cp1252"` と指定してあるのはそのためだ（ `_JAVA_OPTIONS` があると java.exe が勝手にそれを起動オプションに追加する）。

また、 windows だと css 等を変更し、パッケージングが開始されたときに次のようなエラーが出る。

```
[trace] Stack trace suppressed: run 'last *:webappPrepare' for the full output.
[error] (*:webappPrepare) java.io.FileNotFoundException: C:\Users\nazoking\git\gitbucket\target\webapp\assets\common\css\gitbucket.css (要求された操作はユーザー マップ セクションで開いたファイルでは実行できません。)
[error] Total time: 3 s, completed Jan 13, 2016 12:07:07 PM
```

これは jetty がファイルをロックしているからだ。解決策として、 `src/main/webapp/WEB-INF/jetty-web.xml` として次のファイルを作ると、リロードできるようになる。

```
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure.dtd">
<Configure class="org.eclipse.jetty.webapp.WebAppContext">
  <Call name="setInitParameter">
    <Arg>org.eclipse.jetty.servlet.Default.useFileMappedBuffer</Arg>
    <Arg>false</Arg>
  </Call>
</Configure>
```

参考： http://www.eclipse.org/jetty/documentation/current/troubleshooting-locked-files-on-windows.html

この `src/main/webapp/WEB-INF/jetty-web.xml` は、コミットしないように `gitbucket/.git/info/exclude` にファイル名を追加しておこう。


## 高速開発のための追加設定(jrebel)

jrebel というものを入れると、 java vm の再起動なしでクラスのリロードをしてくれる。起動の処理を毎回しないのでとても高速だ。個人ライセンスがあるので、無料で使えるっぽい（たまに jrebel最高！ 的な事をつぶやかれる？)

my jrebel からログイン
https://my.jrebel.com/

SNSログインすると自働でアカウントが作られるようだ。

`Install and Activate`

で、ライセンスキーがもらえる（なんか暗号っぽいやつ）

ダウンロードは https://zeroturnaround.com/software/jrebel/download/#!/have-license から「`Download jrebel`」（ライセンスサーバーじゃない方）

zip を適当なところに展開して `bin/activate-gui` を実行して、ライセンスキーを入れる（なんか暗号っぽいやつ）

jrebel.jar のパスを環境変数に設定する

```
SET JREBEL=c:\develop\util\jrebel\jrebel.jar
```

jrebel を使う場合は、`~ jetty:start` ではなく、 `jetty:start` と `~webappPrepare` を使う。

```
sbt
:
その他いろいろログが流れる
:
Java HotSpot(TM) 64-Bit Server VM warning: ignoring option MaxPermSize=256m; support was removed in 8.0
Listening for transport dt_socket at address: 5005
:
その他いろいろログが流れる
:
> jetty:start
[info] Wrote rebel.xml to C:\Users\nazoking\git\gitbucket\target\scala-2.11\resource_managed\main\rebel.xml
[info] Compiling 2 Scala sources to C:\Users\nazoking\git\gitbucket\target\scala-2.11\classes...
[info] Packaging C:\Users\nazoking\git\gitbucket\target\scala-2.11\gitbucket_2.11-3.11.0-SNAPSHOT.jar ...
[info] Done packaging.
[info] starting server ...
[success] Total time: 20 s, completed Jan 13, 2016 12:01:55 PM
:
その他いろいろログが流れる
:
2017-01-13 12:05:18.699:INFO:oejs.ServerConnector:main: Started ServerConnector@28486680{HTTP/1.1}{0.0.0.0:8080}
2017-01-13 12:05:18.700:INFO:oejs.Server:main: Started @19739ms
>
```

最後まで来たら（ログに隠れて最後のプロンプトが見えないかもしれないが）、今度は `~webappPrepare` と打ってみよう。「ソースを監視して、ファイルが変更されたら `webappPrepare` を実行する」くらいの意味になる。

この状態で、例えば `src/main/twirl/gitbucket/core/main.scala.html` 等を書き換えてブラウザをリロードすると、 `~jetty:start` の時よりも高速に内容が反映されることが分かると思う。

この二つのファイルはコミットされないように `.git/info/exclude` に追加しておくとよい。

jRebel では、クラスのリロードはしてくれるが関連するクラスの初期化はされない。例えばコントローラーに新しいルートを追加した場合などは反映されない。
また、たまいろいろな関係でキャッシュされたクラスなどが不正後を起こして、たまに NoClassDefined エラーが出る事がある。
おかしいと思ったら、 `~webappPrepare` を一旦止めて（エンターキーを押すと止まる） `jetty:start` と打って、サーバを再起動してみよう。

# gitbucket のファイル構成

git clone した直後、ディレクトリは以下の通り。javaの伝統 maven っぽい。sbtがmavenを意識して作られているからだ。

 * **contrib** デーモン化するときの起動スクリプトとかそういうの
 * **doc** 開発者用ドキュメント
 * **project** sbt用の構成管理ファイル
 * **release** gitbucket本体のリリース用のスクリプト。gitbucket本体のリリースをする人以外は関係ない
 * **src** ソースコード
   * **test** 自働テスト用のソースコード
   * **main** アプリケーション本体のソースコード
     * **java** java のソースコード。ちょっとだけjavaが混じっているが最初は気にしなくていい
     * **resources** リソースファイル。xml とか アップデート用スクリプトとか
     * **twirl** html テンプレート
     * **webapp** webアプリケーションのリソースファイル。cssとかjava script とか
     * **scala** scala のソースコード
 * その他に自動で作成されるファイルとかがあるけどとりあえず気にしない

ソースは主に `src/main/scala` 以下をいじることになる。 html は `src/main/twirl` 以下、javascript/css は `src/main/webapps/assets/` 以下にある。

# view をいじってみる。scala テンプレート twirl

gitbucket では [twirl](https://www.playframework.com/documentation/ja/2.2.x/JavaTemplates) というテンプレートエンジンを使っている。これは [play フレームワーク](https://www.playframework.com/) で使われているもので、html中に scala を書ける、というものだ。

scala の文法（の基礎）はそれほど難しくはない。 javascript や ruby などを触ったことがあるのであれば、何となく分かると思う。

scala をいじるなら IDE の利用がお勧めだが、 view の変更程度にとどめるなら vim や sublime text のような、「html/javascriptを編集できる程度の能力を持ったエディタ」でも十分である（私はsublime text に [ensime](http://ensime.github.io/editors/sublime/) プラグインを使っている）。

twirl では、 jsp や erb や php のようにHTMLタグの中に HTMLではない言語を書く。 twirl は `@` から後をいい感じに scala 言語として解釈してくれる。ファイルの拡張子は `.scala.html` で、`src/main/twirl/gitbucket/core` 以下に置かれている。

試しに  `src/main/twirl/gitbucket/core/main.scala.html` 等を、 scala っぽい部分を避けて、書き換えてブラウザをリロードしてみよう。内容が反映されることが分かると思う。

 * `@` が現れたら、そこからしばらく scala だ。具体的には
   * 変数名っぽい間 `<h1>@value1</h1>`
   * 関数名 + 引数 `<h1>@func1(value1)</h1>`
   * 関数名 (+ 引数) + ブロック `<ol>@func1(value1){ value2 => <li>@value2</li> }</o1>`
   * match `<p>@value1 match { case condisiton-expression => <span>@condition</span> }</p>`
   * 括弧 `<h1>@(value1 + value2)</h1>`
   * などなど

`@` の後、 `()` の間、 `{}` のはじめの `=>` まで、あたりが scala である（違う場合もある）。これらの記号に注目しながら試行錯誤してみて欲しい。なお `@` 自体は `@@` で出力できる。

以下、簡単に説明する。

```
@(title: String)

<h1>@title</h1>
```

というテンプレートファイルがある。一行目は引数の宣言(コントローラーから受け取る値)である。このテンプレートファイルは

```scala
def view(title: String){
  return "<h1>" + escape(title) + "</h1>";
}
```

のような形に変換される（正確には違うが説明のため [^1] ）。`def` はメソッド・関数の宣言キーワードで、javascript だと `function` キーワードと同じような意味だ。 `title: String` が引数の一つで、`:` より前が変数名、後が型（クラス名）になる。
つまりこのテンプレートでは、 title という String 型の変数をコントローラー等から受け取って使うことになる。

[^1]: 実際に `src/main/twirl/gitbucket/core/main.scala.html` から生成されるのは `target/scala-2.11/twirl/main/gitbucket/core/html/main.template.scala` なので、悩んだら覗いてみると何解決するかも

実際に変数を使っているのは `<h1>@title</h1>` の部分である。ここは php で言うと

```php
<h1><?php echo escape(title) ?></h1>
```

のような形に変換される。twirl では、変数の型によって自働でHTMLエスケープされる(XSS対策)。

`@` の後は変数だけでは無く、関数呼び出しやif、for 等も使える。

```
@for(order <- orders) {
  <li>@order.getTitle()</li>
}
```

のような例が [play の html テンプレートの解説ページ](https://www.playframework.com/documentation/ja/2.2.x/JavaTemplates) に載っているので、そのあたりを読んで習得してほしい。とはいえそこに、

> テンプレートは複雑なロジックを記述する場所ではない事を忘れないで下さい。複雑な Scala コードをここに書く必要はありません。ほとんどの場合は以下のようにモデルオブジェクトからデータにアクセスするだけで済むでしょう。

と書かれているように、そんなに複雑なプログラムは入れてはいけないし、既存のコードを読む時も出てこないはずだ。

gitbucket は [bootstrap](http://getbootstrap.com/)を使って作られている。javascript は jQuery ベースだ。

これまでで、とりあえず UI を変更したり、ロゴを変更したり、簡単な挙動を変更したりするようにできると思う。


# コントローラーをいじってみる。web アプリケーション・フレームワーク scalatra

gitbucket で使われているのは [scalatra](http://www.scalatra.org/) というwebフレームワークである。 scalatra は ruby の [sinatra](http://www.sinatrarb.com/) を参考にして作られた scala の web フレームワークだ。 scala で webフレームワークと言えば [play](http://playframework.com) が有名だが、scalatra はそれに対して「java の servletとして動く」という特徴がある(play は servlet として動かしにくい)。tomcatや既存のwebアプリケーション資産を生かしやすく、gitbucketでは [jgit](https://eclipse.org/jgit/) の提供するgitサーブレットなど、それを利用して動かしている。

scalatra は、非常に簡単にサーブレットが書けるのも特徴だ。例えばこんなコードになる。

```scala
class MyController extends ScalatraServlet with ScalateSupport {
  get("/hoge") {
    "Hello, world!"
  }
}
```

もう少しjava風に書くと、上のコードは

```java
class MyController extends ScalatraServlet, ScalateSupport {
  // コンストラクタ
  public MyController(){
    Route route1 = new Route("GET", "/hoge")
    route1.setAction(new Action(){
      pulbic Object apply(){
        return "Hello, world!";
      }
    });
    this.addRoute(route1);
  }
}
```

くらいの意味になる(上のコードは全く動かないしクラス名・メソッド名も適当。雰囲気を掴んで！)。webアプリケーションの開発経験があれば、何をしているのか何となく分かると思う。 scala では、クラス定義文がコンストラクタと混在するので、このような DSL が書きやすい。

gitbucket では、このコードは `src/main/scala/gitbucket/core/controller/MyController.scala` として置かれる。

`src/main/scala` ディレクトリは

 * gitbucket/core
   * **controller** コントローラー
   * **model** データベース関係
   * **service** サービス。コントローラーとモデルの間くらい
   * **ssh** ssh関係
   * **util** その他
   * **view** html/twirlで使うヘルパーとか
   * **servlet** サーブレット/フィルタ(コントローラーの前に処理をするとか)
   * **plugin** プラグイン関係
   * **api** api関係

のようになっている。まずリクエストを受け取るのがコントローラーなので、コントローラーを説明する。

コントローラはブラウザからのリクエストを処理して、viewを返す。先ほど view は文字列として返したが、gitbucket では基本的には html テンプレートを返す。

html テンプレートは scala の著名なwebフレームワークである play から独立した twirl というテンプレートエンジンを使っている。twril は html 中にscalaを書くと sbt がscalaにコンパイルしてくれるテンプレートエンジンだ。
具体的には `src/twirl/gitbucket/core/hoge.scala.html` というファイルを書くと `gitbucket.core.html.hoge` という関数にコンパイルされる（scala には「関数（のように扱えるクラス）」がある＆クラス名を全部小文字にすることもある。この例は `gitbucket.core.html` パッケージの `hoge` 関数型クラス（を拡張した何か）が生成される）。

```html
@(message:String)
<div>@message</div>
```

として


```scala
  get("/hoge") {
    gitbucket.core.html.hoge("Hello World")
  }
```

こんな感じのコードを 例えば `src/main/scala/gitbucket/core/IndexController.scala` に書けば、`/hoge` にアクセスすると `Hellow World` が表示されるはずだ。

twirl は html 中の `@` から後をいい感じで scala と見なして、いい感じで出力してくれる（いい感じで＝説明が難しいけど何となくうまく動く）。 `src/twirl/gitbucket/core` 以下はだいたい url に合致しているので、gitbucketを動かしながら気になる画面を見てみよう。

scalatra では単純には次のようにすることでリクエストパラメータを取得できる

```scala
  get("/hoge/:aaa") {
    "aaa="+ parem("aaa")+ " bbb="+parem("bbb")
  }
```

このようにして、 `/hoge/xxxx?bbb=yyy` にアクセスすると "aaa=xxxx bbb=yyy" が表示される。

一応ここまでで、リクエストを受け取ってviewを返す事ができるはず。

----

今日は力尽きたのでここまで。リクエストが多かったりやる気が出てくれば続きを書きます。。。