RedmineプラグインをGitHub Actionsでテストする（Docker編）

以前に書いた[RedmineプラグインをGitHub Actionsでテストする](https://qiita.com/two_pack/items/31d159b28663b57614c4)での、以下の問題点を解消するために、Dockerイメージを使用した形に改良したまとめです。

* パッケージやRedmine自体のインストールも都度行われるため、時間がかかる。
* [actions/setup-ruby](https://github.com/actions/setup-ruby)で、Ruby v2.3.xはインストールできない。

## 改良後の結果

[Redmine Auto Assign Group Plugin](https://github.com/two-pack/redmine_auto_assign_group)のテストを行う[ワークフロー](https://github.com/two-pack/redmine_auto_assign_group/blob/master/.github/workflows/ci.yml)での結果です。

### テストにかかる時間

[Redmine Auto Assign Group Plugin](https://github.com/two-pack/redmine_auto_assign_group)のテストに、GitHub Actionとして[Redmine plugin test action](https://github.com/two-pack/redmine-plugin-test-action)を使用しています。改良前のv1と改良後のv2での比較は以下の通りで、大きく改善できました。

| 対象      | 実行時間（m:ss） | v1-v2 |
|:----------|--------------:|------:|
| v1(改良前)|          6:28 |------|
| v2(改良後)|          2:33 | 3:55|

※記事を書いている時点で最新の成功ビルド５件ずつの時間から、最大最小を除いた３件の平均で算出。
※Redmine-Rubyの組み合わせ10環境、テストは49 runs, 244 assertionsでMiniTestを使用。ただし、v1ではRuby v2.3.xは使用できないため、v2.3.xは除いた場合。

### Ruby v2.3.xでのテスト

dockerhubの[ruby:2.3-slim](https://hub.docker.com/layers/ruby/library/ruby/2.3-slim/images/sha256-fa491ba0d8083a98291cedbc642ea7787d9aaca07438ffb9d2508a2d33c7b42b)ベースのイメージを作成することで、テストできるようになりました。

## 改良内容

[Redmine plugin test action](https://github.com/two-pack/redmine-plugin-test-action)のv1は、Github Actionsのubuntu-latestに対して環境を作っていくものでした。これだとテストまでの準備に時間がかかるため、あらかじめRedmineをインストールしたテストを実行するイメージをDockerHubに作成しておき、その上でプラグインのテストを実行する動きにしました。

流れをまとめると以下のとおりです。
**プラグインのGitHubアクションワークフロー → DockerHubからイメージ取得 → プラグインのテスト実行**

### プラグインのGitHubアクションワークフロー

[Redmine Auto Assign Group Plugin](https://github.com/two-pack/redmine_auto_assign_group)での変更は、[このコミットのような感じ](https://github.com/two-pack/redmine_auto_assign_group/commit/45119ed9bb5c0951601ce5a21e9614c11fcc84b9)です。
環境構築のための定義はバッサリなくなり、RedmineとRubyのバージョン、プラグイン名の指定のみとなりました。これに伴って、GitHubアクションとしてパラメータが変更になったため、v2に変更しています。

### DockerHubからイメージ取得

ベースのなる環境は、[DockerHubのrubyのslimイメージ](https://hub.docker.com/_/ruby)を元にして、RedmineとChrome、ChromeDriverをインストールしたイメージを作っています。行っている事自体は、v1と同じです。DBはPostgreSQL, MySQL, sqlite3[^1]。[Dockerfileのリポジトリはここ](https://github.com/two-pack/redmine-test-image)、[DockerHubはここ](https://hub.docker.com/repository/docker/twopackas/redmine-test-image)です。

#### RedmineとRubyのバージョン組み合わせの作成

以下の記事がとても参考になりました。
[dockerhubのautomated buildで任意のdocker buildコマンドを利用し、単一のDockerfileから設定を動的に変えて複数イメージをビルドする](https://qiita.com/tuttieee/items/69e35ade3428075481ef)

DockerHubのビルド設定で行ったタグ名を元に、フックでRedmineとRubyのバージョンを取得して、ビルドを行っています。フックは以下のようなものです。

```bash
#!/bin/bash
set -eux

LIST=(${DOCKER_TAG/_/ })
RUBY_VERSION=${LIST[1]/ruby/}
REDMINE_VERSION=${LIST[0]//trunk/master}

LIST=(${REDMINE_VERSION//[.-]/ })
if [ ${#LIST[*]} -eq 2 ]; then
  LATEST_VER=LATEST_${REDMINE_VERSION/./_}
  if [ ${LATEST_VER} != "" ]; then
    eval REDMINE_VERSION=\$$LATEST_VER
  fi
fi

docker build --build-arg REDMINE_VERSION=${REDMINE_VERSION} --build-arg RUBY_VERSION=${RUBY_VERSION} -f $DOCKERFILE_PATH -t $IMAGE_NAME .
```

**DOCKER_TAG** に、DockerHubのタグ名が入ってきます。 **4.1_ruby2.6** などのような規則にしておき解析することで、バージョンを決定して以下のようにDockerfileへARGで渡しています。

```dockerfile
ARG RUBY_VERSION
FROM ruby:${RUBY_VERSION}-slim
ARG REDMINE_VERSION
```

DockerHubのビルド設定は、 **Builds - Configure Automated Builds** の **BUILD RULES** に以下のような感じで追加しています。Sourceはすべてmasterにしていますが、 **Docker Tag** を変えて複数登録することで、組み合わせでイメージをビルドできます。
![build_rules.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/30716/cc41d469-bbdd-574a-0804-285ec93cfdee.png)

#### GitHubアクションからのイメージ使用

実際にテストを行うGitHubアクションである、[Redmine plugin test action](https://github.com/two-pack/redmine-plugin-test-action)は、[Dockerコンテナのアクション](https://help.github.com/ja/actions/automating-your-workflow-with-github-actions/creating-a-docker-container-action)として作成するつもりでした。プラグイン側のアクションから受け取ったパラメータでDockerHubのイメージを指定できるかと思ったのですが、ARGの受け渡しができず、うまくいきませんでした。
このため、v1と同じくシェルスクリプトをキックして、DockerHubのイメージをベースにしたDockerfileをビルド、起動する形にしています。シェルスクリプトは[これ](https://github.com/two-pack/redmine-plugin-test-action/blob/master/lib/redmine-plugin-test.sh)です。

GitHubのブランチ名の取得は環境変数から取得しますが、プルリクの場合とブランチへのコミットの場合で分けて取得する必要がありました。

```bash
if [ "${GITHUB_HEAD_REF}" = "" ]; then
  PLUGIN_BRANCH=$(echo ${GITHUB_REF#refs/heads/})
else
  PLUGIN_BRANCH=${GITHUB_HEAD_REF}
fi
```

あとはDockerfileにブランチ名やアクションのパラメータを渡してビルド、実行します。

```bash
docker build -t redmine-plugin-test \
             --build-arg REDMINE_VERSION=${REDMINE_VERSION} \
             --build-arg RUBY_VERSION=${RUBY_VERSION} .
docker run -e "GITHUB_REPOSITORY=${GITHUB_REPOSITORY}" \
           -e "PLUGIN_NAME=${PLUGIN_NAME}" \
           -e "PLUGIN_BRANCH=${PLUGIN_BRANCH}" \
           -e "DATABASE=${DATABASE}" \
           -t redmine-plugin-test
```

### プラグインのテスト実行

テスト自体はDockerfileのENTRYPOINTで実行する[シェルスクリプト](https://github.com/two-pack/redmine-plugin-test-action/blob/master/lib/entrypoint.sh)に書いています。
**インストール→テスト→アンインストール** のrakeを実行する内容になっています。


### まとめ

[Redmine Auto Assign Group Plugin](https://github.com/two-pack/redmine_auto_assign_group)での結果は、以下のような感じです。
![build_rules.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/30716/92c85d8e-7ce5-0d67-4115-3791b0bcd48c.png)


[Redmine plugin test action](https://github.com/two-pack/redmine-plugin-test-action)のv1に比べて、だいぶ実行時間を短縮できました。プラグイン側のワークフローもシンプルになったので、使いやすくなったのではと思います。


[^1]: 2020/04/04編集：v2.0.1でPostgreSQL、MySQL(MariaDB)にも対応しました。