ククログ

株式会社クリアコード > ククログ > GitHub Actionsを使ったGroongaのパッケージのインストール、テストの自動化

GitHub Actionsを使ったGroongaのパッケージのインストール、テストの自動化

Groongaでは、これまでもGitHub Actionsを使ってパッケージの作成を自動化したり、テストの自動化を実施してきました。 いままで自動化してきたテストは、リポジトリーにpushされたソースコードに対してビルド、テストするものでした。 これらの自動化により、リリース前に初めて問題が発覚することが少なくなり、問題が発生した段階で対処を進めることができています。

ただ、リポジトリーにpushされたソースコードに対するテストだと、各OS向けに作成したパッケージがちゃんとインストールできるか、 パッケージからインストールした環境で動作するかは確認できていませんでした。 そのため、パッケージの作成に失敗していた場合には、リリース後、パッケージからGroongaをインストールする段階にならないと問題に気がつけない状態でした。

リリース後にパッケージに問題があるとわかった場合は、再リリースすることになり、余計な時間がかかってしまいます。 そこで、リポジトリーにソースコードがpushされた段階でパッケージのインストールとパッケージからインストールしたGroongaのテストを実行するようにしました。

この記事は、作成したパッケージをインストール、テストする方法を説明したものです。 Groongaに固有の部分もありますが、各OS向けにパッケージを提供しているプロジェクトにとって参考になる情報もあると思います。

パッケージのテスト環境の構築

パッケージをテストするためには、当然テスト対象のパッケージを作成する必要がありますが、Groongaでは、既に自動化されています。 パッケージは既にできているので、この記事では、作成されたパッケージを取得するところから説明します。

前述の通りGroongaでは、リポジトリーにソースコードがpushされるたびにパッケージの作成が実行されるので、パッケージ作成後にテストを実行します。

パッケージの作成とパッケージのテストのジョブを分けても良いのですが、そのようにすると、パッケージを作成するジョブでartifactsにパッケージを保存し、 パッケージをテストするジョブでは、artifactsから必要なパッケージをダウンロードする操作が必要になり煩雑です。 (GitHub Actionsでは、パッケージ等のワークフローの成果物をartifactsとして保持できます。)

そのため、Groongaではパッケージの作成とパッケージのテストは同一のジョブで実行しています。

具体的には、以下のようにしています。

  1. Docker上にホストのディレクトリをマウント

  2. テスト用のイメージ、テストスクリプトを指定

# Test
- name: Test
  run: |
    docker run \
      --rm \
      --tty \
      --volume ${PWD}:/groonga:ro \
      ${{ matrix.test-docker-image }} \
      /groonga/${{ matrix.test-script }}

上記設定は、 https://github.com/groonga/groonga/blob/v10.0.2/.github/workflows/package.yml#L158 に記載されています。

1. Docker上にホストのディレクトリをマウント

docker run--volumeオプションを使って、ホストのディレクトリをDocker上にマウントできます。 上記では、--volume ${PWD}:/groonga:roと指定されているので、現在居るディレクトリをDcoker上の/groongaにマウントしています。

2. テスト用のイメージ、テストスクリプトを指定

${{ matrix.test-docker-image }}で実行するイメージを指定し、/groonga/${{ matrix.test-script }}で実行するテストスクリプトを指定しています。

GitHub Actions上で作成しているパッケージは、CentOS向けとDebian向けのものなので、この2つのOSのイメージを使用します。 複数のバージョンがあるので、matrixを使用し、バージョンごとにイメージを変更してテストしています。

${{ matrix.test-docker-image }}は以下のように定義されているので、Debian stretch、Debian buster、CentOS6、CentOS7、CentOS8のイメージを使用するようになっています。 テストに使用するスクリプトもOSによって異なるので、${{ matrix.test-script }}としてそれぞれのパスを指定しています。

        include:
          - label: Debian GNU/Linux stretch amd64
            id: debian-stretch-amd64
            test-docker-image: debian:stretch
            test-script: packages/apt/test.sh
          - label: Debian GNU/Linux stretch i386
            id: debian-stretch-i386
            test-docker-image: i386/debian:stretch
            test-script: packages/apt/test.sh
          - label: Debian GNU/Linux buster amd64
            id: debian-buster-amd64
            test-docker-image: debian:buster
            test-script: packages/apt/test.sh
          - label: Debian GNU/Linux buster i386
            id: debian-buster-i386
            test-docker-image: i386/debian:buster
            test-script: packages/apt/test.sh
          - label: CentOS 6
            id: centos-6
            test-docker-image: centos:6
            test-script: packages/yum/test.sh
          - label: CentOS 7
            id: centos-7
            test-docker-image: centos:7
            test-script: packages/yum/test.sh
          - label: CentOS 8
            id: centos-8
            test-docker-image: centos:8
            test-script: packages/yum/test.sh

また、テストに使用するイメージは既存のイメージを再利用せず、新規に作るのが良いです。 新規の環境でテストしないと依存ライブラリーが足りない等の問題に気がつけない可能性があるためです。

ここまでで、それぞれのDockerイメージ上でテストを実行する準備が整いました。 次は、パッケージのインストールとテストを実施します。 パッケージのインストールとテストは、テストスクリプト内で実施しています。

パッケージのインストールとテストの実施

Debian向け、CentOS向けのパッケージのテストスクリプトは、以下の場所にあります。 ここからは、これらのスクリプトの内容を説明し、どのような流れでパッケージのインストールとテストを行っているかを説明します。

Debian向け: https://github.com/groonga/groonga/blob/v10.0.2/packages/apt/test.sh

CentOS向け: https://github.com/groonga/groonga/blob/v10.0.2/packages/yum/test.sh

基本的な流れはどちらのスクリプトも同じで、それぞれのOSのパッケージ管理システム(Debianならapt、CentOS6,7ならyum、CentOS8ならdnf)を使って ホストからマウントしたディレクトリにある、パッケージをインストールし、その後grntest(Groonga用のテストツール)を使ってGroongaのテストを実行しています。

ただ、CentOS6,7はRubyのバージョンが古く、grntestを使ったテストができないので、パッケージのインストールのみを確認しています。 また、CentOS8は、CentOS8向けのMessagePackのパッケージが無いため、現状ではテストを実行せずにスクリプトを終了しています。 (MessagePackがないとすべてのテストを実行できないためです。)

Debian向けパッケージのインストール、テストの流れ

まず、テストスクリプトの以下の箇所でOSのコードネームとアーキテクチャを取得します。 これらは、インストールするパッケージのパスに使用します。

code_nameには、stretchbuster等のDebianの各バージョンのコードネームが入ります。 architectureには、amd64i386等のCPUアーキテクチャが入ります。

code_name=$(lsb_release --codename --short)
architecture=$(dpkg --print-architecture)

次にインストールするパッケージのパスを指定してaptコマンドでパッケージをインストールします。 aptコマンドは、APTリポジトリを用意しなくても、以下のようにローカルに保存されているパッケージを直接指定してインストールできます。

repositories_dir=/groonga/packages/apt/repositories
apt install -V -y \
  ${repositories_dir}/debian/pool/${code_name}/main/*/*/*_{${architecture},all}.deb

インストール後、インストールが成功しているかを、groongaコマンドを使って確認します。

groonga --version

次にテスト用のディレクトリを作成し、テストスクリプトを移動します。 この段階で、i386の環境では動作しないテストを削除します。

mkdir -p /test
cd /test
cp -a /groonga/test/command ./
if [ "${architecture}" = "i386" ]; then
  rm command/suite/ruby/eval/convert/string_to_time/over_int32.test
  # TODO: debug this
  rm command/suite/select/filter/geo_in_circle/no_index/north_east.test
fi

最後にgemgrntestをインストールし、テストを実行しています。

apt install -V -y \
  gcc \
  make \
  ruby-dev
gem install grntest

export TZ=Asia/Tokyo

grntest_options=()
grntest_options+=(--base-directory=command)
grntest_options+=(--n-retries=3)
grntest_options+=(--n-workers=$(nproc))
grntest_options+=(--reporter=mark)
grntest_options+=(command/suite)
grntest "${grntest_options[@]}"
grntest "${grntest_options[@]}" --interface http
grntest "${grntest_options[@]}" --interface http --testee groonga-httpd
CentOS向けパッケージのインストール、テストの流れ

まず、テストスクリプトの以下の箇所でOSのバージョンを取得します。 これは、パッケージのパスを指定するのとパッケージ管理システムのコマンドを指定するのに使います。 (CentOSのパッケージ管理は、CentOS6,7では、yumコマンドで実行しますが、CentOS8では、dnfコマンドで実行するためです。)

version=$(cut -d: -f5 /etc/system-release-cpe)

以下の箇所でバージョン毎にパッケージ管理システムのコマンドを指定します。

case ${version} in
  6|7)
    DNF=yum
    ;;
  *)
    DNF="dnf --enablerepo=PowerTools"
    ;;
esac

以下の箇所でGroongaの公開鍵のインポートを行い、RPMパッケージをインストールします。 yumdnfコマンドもaptコマンドと同様、YUMリポジトリを用意しなくても、以下のようにローカルに保存されているパッケージを直接指定してインストールできます。

${DNF} install -y \
  https://packages.groonga.org/centos/groonga-release-latest.noarch.rpm

repositories_dir=/groonga/packages/yum/repositories
${DNF} install -y \
  ${repositories_dir}/centos/${version}/x86_64/Packages/*.rpm

インストールが成功しているかを、groongaコマンドを使って確認します。

groonga --version

CentOS6,7はRubyのバージョンが古くgrntestが動作しないためここでスクリプトを終了します。

case ${version} in
  6|7)
    exit 0
    ;;
  *)
    ;;
esac

CentOS8は、以下の箇所でgrntestをインストール後テストを実行します。 ただ、前述の通り現在は、CentOS8向けのMessagePackのパッケージが無いため、テスト実行前にexit 0でスクリプトを終了しています。

# TODO: Require msgpack for testing normalizer options
exit 0

${DNF} install -y \
  gcc \
  make \
  redhat-rpm-config \
  ruby-devel
gem install grntest

export TZ=Asia/Tokyo

grntest_options=()
grntest_options+=(--base-directory=/groonga/test/command)
grntest_options+=(--n-retries=3)
grntest_options+=(--n-workers=$(nproc))
grntest_options+=(--reporter=mark)
grntest_options+=(/groonga/test/command/suite)

grntest "${grntest_options[@]}"
grntest "${grntest_options[@]}" --interface http
grntest "${grntest_options[@]}" --interface http --testee groonga-httpd

以上のようにして、Groongaではリポジトリへのpushをトリガーとして、パッケージの作成、インストール、テストまでを自動で実行しています。 こうすることで、作成したパッケージがインストールできないといった問題を未然に防げるようになり、より安定したものをリリースできます。

各OSに向けのパッケージを配布しているプロジェクトは、上記のようなやり方を参考にして、パッケージのテストも自動化してみてはいかがでしょうか?