Github Actions上のDockerコンテナ上のGCCの最新版でテストを走らせたかった

Github Actionsで用意されているubuntu 20.04環境に用意されているソフトウェアを見るとGCCは9.3が用意されています(2020年7月24日現在)。しかしGCC9.3はC++20対応がほぼなされていないので使い物になりません(個人の感想です)。いつかは追加されるでしょうが、GCC10.2(もう出た)、GCC 11.1とか言いだすと永遠に待ち続けることになりそうです・・・

一方、Docker公式のGCCコンテナは既にGCC10.1が利用可能です(2020年7月24日現在)。

つまり、この2つを組み合わせれば最新のGCCを使いながらGithub Actionsできるはず!

※以下ではGithub ActionsとかDocker関係の用語が必ずしも正しく使われていない可能性があります、ご了承ください。

Dcokerコンテナを起動しその上でビルドとテストを走らせるActionを作成する

この資料にはDockerコンテナを使ってCIを走らせるための初歩初歩の事が書いてあります。ちゃんと読むと、Dockerコンテナを起動し何かするためのオリジナルのActionを作成し、それをテストしたいリポジトリのymlファイルから呼び出す形になる事が分かります。

(なお、私はちゃんと読まなかったのでわかりませんでした)

というわけで、まずはGCCのDockerコンテナを起動してビルドとテストを行うActionを作成します。

Dockerファイルの作成

まずは所望の動作を達成するためのDockerファイルを作ります。

From gcc:latest
RUN apt-get update
RUN apt-get install -y python3-pip
RUN pip3 install meson ninja
COPY entrypoint.sh /entrypoint.sh
ENTRYPOINT ["/entrypoint.sh"]

私はビルドシステムに専らmesonを使っているので、mesonもインストールしておきます。上記Dcokerfileの2-4行目はお好きなビルドシステムのインストールに置き換えてください。

ここのRUNで全部やっても良いのですがあまり汎用性が無くなるので、ここでは最低限の環境構築に留めて、メインの処理は別に書いたシェルスクリプト(ここではentrypoint.sh)に記述します。

5行目で用意したシェルスクリプトをDockerインスタンス上にコピーして、6行目で開始時に実行するように指示します。

シェルスクリプトの作成

このページにあるものを参考にして書きます。ただこのページだけ見ても、テストしたいリポジトリのクローンをどうするのかが分かりません。試してみると別にクローンされてはいないので自分でクローンしてくる必要があります。なので、そのために必要な情報を引数として受け取るようにします。

公式のActionにはcheckoutというのがありますが、今回はDocker上で実行するため利用できなさそうです。

#!/bin/sh -l

git clone $2
cd $1
meson build
meson test -C build -v

第一引数にリポジトリ名(=ディレクトリ名)、第二引数にリポジトリのURLを受けるようにしています。5-6行目はmeson特有のものなので、お使いのビルドシステムのビルドコマンドと成果物の実行コマンドに置き換えてください。

ファイル名は先程Dockerfileに書いたentrypoint.shと合わせます。chmod +x entrypoint.shで実行可能にすることを忘れすに、 Windowsの場合は次のページが参考になります。

Actionファイルの作成

次にActionとして呼ばれたときに何をするかをaction.ymlというファイルに記述します。試してないですがファイル名は多分固定だと思われます。

name: 'build & test'

inputs:
  name:
    description: 'Name of target repository'
    required: true
  uri:
    description: 'URI of target repository'
    required: true

runs:
    using: 'docker'
    image: 'Dockerfile'
    args:
    - ${{ inputs.name }}
    - ${{ inputs.uri }}

inputsで先程のシェルスクリプトに渡す引数を定義しています。上から順番に第一引数(name)、第二引数(uri)で内容は先ほどの通り。descriptionは説明で無くても良いです。requiredは引数が省略可能かを表しており、今回は省略してほしくないのでtrueに設定しておきます。

他にも出力を定義できたりします。出来ることは次のページを参照。

runsにActionとして呼ばれたときにやることを書いておきます。usingは何のアプリケーションを使用するのかを指示するもので、ここではDockerを指定します。imageには使いたいDockerイメージ名を指定します。Docker Hubにあるイメージ名を指定しても良いようですが、今回は先ほど用意したDockerfile(のファイル名)を指定します。
そして、argsで引数をどの順で受け取るかを指定しています。先程inputsで定義した引数名をinputs.nameの様に参照しています。シェルスクリプトにはここで書いた順番に引数が渡されることになります。

Actionリポジトリの作成

ここまで用意したDockerfile、やることを記述したシェルスクリプトentrypoint.sh、Actionを記述したaction.ymlの3つをGithub上の公開リポジトリに保存しておき、リリースタグを打っておくことで、他のリポジトリからActionとして参照できるようになります。
ここまではリポジトリのトップに全てをぶちまける事を前提に書いてありますので、今回はそうします。

私の場合は次のようになりました。

Publish this Action to Marketplaceなるメッセージが表示されていたら、きちんとActionとして認識されています。もし自作のActionをMarketplaceに公開したい場合はreadmeを適切に整えておく必要がありそうです。

テストしたいリポジトリのワークフロー中でActionを呼ぶ

こうして用意した自作Docker ActionをテストしたいリポジトリGithub Actions ワークフローから呼び出します。

name: Test by GCC latest  # ワークフローの名前
on: [push, pull_request]  # 何をトリガーにして実行するか

jobs:
  test:
    runs-on: ubuntu-latest  # Dockerを実行することになるベース環境
    name: run test
    steps:
    - name: run test in docker
      uses: onihusube/gcc-meson-docker-action@v6
      with:
        name: 'harmony'
        uri: 'https://github.com/onihusube/harmony.git'

実物がこちらです。今回対象のリポジトリは私が衝動で書いていたC++のライブラリです。

ベースは普通のGithub Actionsのワークフローを書くのと変わらないはず。今回重要なのは先程作成したDocker Actionを適切に呼び出すことです。

steps中のusesで任意のActionを呼び出すことができます。先程作成したActionのリポジトリ参照する形で作ったユーザー名/リポジトリ名@タグというように指定します(上記タグがv6とかなってるのは試行錯誤の名残です・・・)。
その次のwithで順番に引数を指定します。指定出来るのはActionのymlに書いたものだけで、ここに書いた引数がDockerインスタンス上で実行されるシェルスクリプトに渡されることになります。

でこれをプッシュするとDocker上のGCCでビルドされテストが実行されたので、どうやら目的を果たせたようです(参考の結果)。

参考文献

謝辞

この記事の8割は以下の方によるご指摘によって成り立っています。

この記事のMarkdownソース