RedmineのtrunkのDockerイメージを毎日自動でビルドする
なんとなくやってみた。リポジトリはhttps://github.com/vzvu3k6k/docker-library-redmine/にある。
Docker official imageのhttps://github.com/docker-library/redmineをベースにしている。このリポジトリの機能のうち、今回やりたいことに関係するのは以下の2つ。
*.templateというのがDockerfileのテンプレートで、update.shを実行するとDockerfileが生成される。- TravisCIを使っていて、
.travis.ymlの設定に従ってビルドしたDockerイメージに対して簡単なテストを実行している。
Dockerfileを生成するために*.templateとupdate.shを編集する
オリジナルのリポジトリではRedmine 3.4とRedmine 4.0に対応するためにテンプレートを使っている。今回はtrunkだけに対応すればいいのでテンプレートは不要だが、4.0-stableや3.4-stableのブランチにもそのうち対応したくなるかもしれないので元の仕組みをそのまま残している。
update.shの書き換えは特に書くほどのこともない。
*.templateはRedmineのソースのtarballをダウンロードする処理をsvnでcheckoutする処理に置き換えた。いくつか引っかかったことがあるのでメモしておく。
中間証明書問題
https://svn.redmine.org/は中間証明書を提供していないらしく、svn coするとエラーになる。
HTTPSを諦めてHTTPを使う、証明書の検証をスキップする、証明書を自分でインストールするなどの対応が必要。今回は 証明書をインストールすることにした。agileware-jp/redmine-plugin-orb#8の実装内容を使わせてもらっている。
元の実装では証明書の受け渡しにprocess substitutionを使っているが、DockerfileのRUNはデフォルトでは/bin/shなのでこの機能が使えなかった。
The default shell on Linux is
["/bin/sh", "-c"]
参考: https://stackoverflow.com/q/41354864
最新のリビジョン
「リビジョン番号を指定しないときには最新のリビジョンを取得する」という動きにしたかったが、SVNで最新のリビジョンを表すにはどうしたらいいのかわからなくて困った。
パースできないリビジョン番号を指定したときに出てくるエラーメッセージ(Syntax error in revision argument)でsvnのソースをgrepして、svn_opt_parse_revision_to_range→parse_one_rev→revision_from_wordという感じで関数をたどってみて、headという文字列でいけることが分かった。
あとから急に思いついて「svn revision format」でググってみたらドキュメントがあっさりでてきた。
http://svnbook.red-bean.com/en/1.7/svn.tour.revs.specifiers.html
よく見たらsvn help coでも普通に説明されていた。
.travis.ymlを編集する
Dockerイメージのテスト
docker-library/official-imagesにはDockerイメージが意図通りに動くかどうかざっくりテストする仕組みがあって、docker-library/redmineもそのテストを利用している。テストコードはすべてofficial-imagesリポジトリに入っていて、~/official-images/test/run.shにイメージ名を渡すとそのイメージを対象にしたテストが実行される。このとき、特に設定しなければイメージ名のネームスペースを無視してくれるので、vzvu3k6k/redmineというようなイメージ名にしておけば何も手を加えなくてもdocker-library/redmineのイメージと同じテストが実行される。これはとても助かった。
フェーズ間で情報を受け渡す
TravisCIのジョブはいくつかのフェーズに分かれている(参照: https://docs.travis-ci.com/user/job-lifecycle/)。今回はそれぞれのフェーズで以下のような処理をしている。
before_scriptフェーズ: 最新のリビジョン番号を取得するscriptフェーズ: そのリビジョン番号のコードを取得してイメージをビルドするdeployフェーズ: ビルドしたイメージにリビジョン番号のタグをつけて公開する
before_scriptフェーズで定義した環境変数はscriptフェーズからは参照できるが、deployフェーズからは参照できなかった。
フェーズごとに最新のリビジョン番号を取り直せばだいたいうまくいくが、何度もSVNサーバーにリクエストを送りたくはないし、ジョブの実行中にコミットがあるとイメージの中身とタグがずれてしまう可能性がある。
TravisCI側では特に解決策を提供していないようなので、値をファイルに書き出しておくことにした。デフォルトではdeployフェーズの前にgit stash --allでファイルがリセットされるので設定で無効にしておく必要がある。(参照: https://docs.travis-ci.com/user/deployment#uploading-files-and-skip_cleanup)
シェルスクリプト
細かいところでいろいろ詰まって調べた。
puts $stdin.read.slice(/^Revision: (\d+)$/, 1)相当のことをgrepでサクッと書きたい- https://unix.stackexchange.com/q/13466
- GNU grepなら
grep -o -P "^Revision: \K\d+$"で近い結果が得られる -oでマッチした箇所だけを表示-PでPCREを有効にして後読み、先読みを使って不要な箇所を読み捨てる
- GNU grepなら
- しかしデフォルトの設定ではRubyが使えるっぽいので
grep使う必要はなかった
- https://unix.stackexchange.com/q/13466
sourceで実行したシェルスクリプトの中でsetを使うと呼び出し元にも影響する- https://superuser.com/q/648331/
SETTING=$(set +o); something; eval "$SETTINGS"で設定を保存して復元できる
- https://superuser.com/q/648331/
- 実行中のシェルスクリプトと同じディレクトリにあるシェルスクリプトを実行したい
- https://stackoverflow.com/q/6659689
- いろいろ方法はあるけど、どれも確実ではない。
- 今回は
$BASH_SOURCEを使った。
- https://stackoverflow.com/q/6659689
感想など
- 普段はCircleCIを使っていて、TravisCIは軽く触ったことがある程度だった。
- 見た目はTravisCIのほうが好み。機能面では後発のCircleCIのほうが充実しているように見える。
- CircleCIのworkflowに相当するのはbuild stagesだろうか。
- 処理を並行で進めたり、待ち合わせたりする制御機能。
- Docker HubにpushするときにはアカウントのIDとパスワードが必要だった。
- CircleCIのENVに設定している。
- 生のパスワードが漏洩するとアカウントごと乗っ取られてしまうのであまり外部に渡したくないが…
- トークン的なものはあるようだが有効期間が短いのでCIには使えない。
- push権限しかないbotユーザーを作るといいよという話があった。なるほど。
- 2FAを実装してほしいという要望も前々から上がっているものの進んでいない(https://github.com/docker/hub-feedback/issues/358)。開発リソースの問題だろうか。
- Docker Hubにpushするたびにイメージのpull数が増えてる気がする。自動的にイメージを収集するbotかなにかが走っているのだろうか。