GitHub Actions で SATySFi の文書やパッケージの CI

これは SATySFi Advent Calendar 2019 6日目の記事です。 5日目はzr_tex8rさんによるSATySFiコード中で整数を16進数で書きたいでした。

3日目の記事では satysfi-docker の紹介をしましたが、今回は satysfi-docker と GitHub Actions を使って SATySFi パッケージや文書の CI をする例を紹介したいと思います。

GitHub Actions は最近正式版になった GitHub の機能で、GitHub の様々なイベント (リポジトリへのプッシュ、Pull Request、リリースの作成、などなど) に反応してワークフローと呼ばれる一連のタスクを実行できるというものです。

GitHub のサービスなだけあって GitHub との親和性は抜群で、CI サービスとしては後発なのでいろいろやりやすかったり (個人の感想です)、Microsoft パワーなのか CI サービスとしては利用制限が緩かったりします。

サンプルリポジトリ

github.com

こちらがサンプルコードが入ったリポジトリです。 簡単なものから少しだけ複雑なものまで5つの例を用意したので、順に説明していきます。

  • 01-simple-example
  • 02-library-example
  • 03-submodule-example
  • 04-satyrographos-example
  • 05-custom-action-example

01-simple-example

一番簡単な例です。 doc.saty というファイルがあり、リポジトリへの push 毎にビルドできるか確認したいという状況です。 依存パッケージもなく、文書をあるバージョンの SATySFi でだけビルドできればいいならこの方法で十分だと思います。

name: build   # ワークフローの名前は build (バッヂをつける際はこの名前で参照することになります)
on: push      # push 毎にワークフローを実行

jobs: # job 定義
  build-simple-example:      # job 定義の名前
    runs-on: ubuntu-latest   # ubuntu 環境で実行
    steps:
      - uses: actions/checkout@v1   # ソースコードをチェックアウトしてくる
      # satysfi-docker を使ってビルド (この例くらいなら slim タグを使ったほうがいいですが簡単のため latest)
      - run: docker run --rm -v $(pwd):/satysfi amutake/satysfi satysfi doc.saty  
        # 今回は 01-simple-example というディレクトリの下にあるのでその中でコマンドを実行
        working-directory: ./01-simple-example   
      - uses: actions/upload-artifact@master
        with:
          name: 01-simple-example          # 01-simple-example という名前で、
          path: 01-simple-example/doc.pdf  # doc.pdf をアップロード

GitHub Actions ではジョブの中で普通に docker run できるので、satysfi-docker の使い方の通りに satysfi を実行するだけです。

生成した PDF はアーティファクトとしてアップロード・ダウンロードできます。 勝手に zip 形式に圧縮されます。PDF はブラウザから確認できたら嬉しいのですがダウンロードして展開してとやらなければいけないのでちょっと面倒。

実行されたワークフローは Actions タブ から確認することができます。

↓ここからダウンロードできるようになります。

f:id:amutake:20191202194831p:plain

02-library-example

次は SATySFi のライブラリの CI をしたい例です。以下のような状況だと思ってください。

  • commands.satyh をライブラリとして配布したい
  • commands.satyh の使い方の説明ドキュメント doc.saty (この中で commands.saty を使っている) をテストとしてビルド・目視確認したい
  • ライブラリなので SATySFi の複数のバージョンで使えるか確認したい

この場合は下のようにするといいと思います。

  build-library-example:
    runs-on: ubuntu-latest
    strategy:
      matrix:
         # 確かめたい SATySFi のバージョンを列挙
        version: [0.0.3-slim, 0.0.3-dev2019.11.16-slim, nightly] 
    steps:
      - uses: actions/checkout@v1
      # matrix.version によってタグを指定
      - run: docker run --rm -v $(pwd):/satysfi amutake/satysfi:${{ matrix.version }} satysfi doc.saty  
        working-directory: ./02-library-example
      - uses: actions/upload-artifact@master
        with:
          name: 02-library-example_${{ matrix.version }}
          path: 02-library-example/doc.pdf

01-simple-example との差分は matrix を使っている部分です。 GitHub Actions の matrix 機能を使って簡単に複数バージョンでのビルドができます。今回はバージョン0.0.3, 2019-11-16時点のスナップショット、nightly で確認しています。 そしてバージョンごとに成果物がアーティファクトとしてアップロードされます。

ライブラリのドキュメントや使用例のPDFはリポジトリに含めておきたい (つまり生成した PDF をリポジトリに push したい) ことが多いと思いますが、 その場合も、 github actions push to repo とかで検索すればいろいろ出てくるので簡単に実現できると思います (試してはないですが…)。

03-submodule-example

以降の3つは依存パッケージがある場合の例です。 依存パッケージがある場合にビルドする方法はいくつかありますがこの例では submodule を使う方法を説明します。

  build-submodule-example:
    runs-on: ubuntu-latest
    strategy:
      matrix:
        version: [0.0.3-dev2019.11.16-slim, 0.0.3-dev2019.07.14-slim]
    steps:
      - uses: actions/checkout@v1
        with:
          submodules: recursive  # submodules: recursive を指定することで submodule init --recursive をしてくれるようになる
      - run: docker run --rm -v $(pwd):/satysfi amutake/satysfi:${{ matrix.version }} satysfi doc.saty
        working-directory: ./03-submodule-example
      - uses: actions/upload-artifact@master
        with:
          name: 03-submodule-example_${{ matrix.version }}
          path: 03-submodule-example/doc.pdf

02-library-example との差分は checkout に submodules: recursive を指定していることです。 あとは doc.saty から @import: ... で読み込むと submodule にしたライブラリが使えるようになります。

この方法の利点としては、slim が使えるので CI も速い (ダウンロード時間が小さい) のと、ジョブ定義もシンプルに保てて手軽なことです。 ただ、文書を作成したい場合はこれでいいかもしれませんが、@import を使っているため、ライブラリとして配布したい場合にこの方法を使うのは配布方法によっては微妙かもしれません (ライブラリの利用者にも submodule で recursive update してもらうことになるかも)。

04-satyrographos-example

こちらは依存パッケージを Satyrographos と opam を使ってインストールする例です。

  build-satyrographos-example:
    runs-on: ubuntu-latest
    strategy:
      matrix:
        version: [0.0.3-dev2019.11.16]
    container:
      image: amutake/satysfi:${{ matrix.version }}   # run で使うイメージを指定
    steps:
      - uses: actions/checkout@v1
      - run: |
          export HOME=/root # workaround
          eval $(opam env)
          opam update
          opam install satysfi-grcnum # opam で SATySFi のライブラリをインストール
          satyrographos install -library grcnum # Satyrographos で SATySFi のライブラリをインストール
          satysfi doc.saty
        working-directory: ./04-satyrographos-example
      - uses: actions/upload-artifact@master
        with:
          name: 04-satyrographos-example_${{ matrix.version }}
          path: 04-satyrographos-example/doc.pdf

差分は run のところで、 opam と Satyrographos を使って SATySFi のライブラリ (ここでは satysfi-grcnum) をインストールしています。

また、 container でジョブの中でコマンドを実行するコンテナイメージを指定しています。 こうしないと、

run: docker run --rm -v $(pwd):/satysfi amutake/satysfi:${{ matrix.version }} bash -c "opam update && opam install satysfi-grcnum && satyrographos install -library grcnum && satysfi doc.saty"`

と長々と書かなければならずちょっとだるいです。ただ GitHub Actions は環境変数 HOME を上書きして走らせるため、 opam がインストールされてあるディレクトリに指定し直しています。

こういう感じで opam リポジトリに登録されている SATySFi のパッケージを使うことができます。

(なお、インストールするパッケージはワークフローの定義に直接書くのではなくパッケージ一覧を書いたファイルかなにかをリポジトリに入れておくのがいいと思います)

05-custom-action-example

SATySFi 用の Action を作って使う例です。 04-satyrographos-example は run にコマンドがベタ書きされていて見づらかったので、 Action としてモジュール化しています。

以下がジョブの定義になります。

  build-custom-action-example:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v1
      - uses: ./.github/actions/satysfi
        with:
          main: ./05-custom-action-example/doc.saty # メインファイルを指定
          packages: 'satysfi-fonts-theano satysfi-grcnum' # パッケージ一覧を指定
      - uses: actions/upload-artifact@master
        with:
          name: 05-custom-action-example
          path: 05-custom-action-example/doc.pdf

.github/actions/satysfi に Action の定義を入れておいて、ジョブの中からはそれを参照しています。

以下が Action の定義 (.github/actions/satysfi/action.yml) です。いわゆる container action になっています。mainpackages を受け取って、docker run の引数として与えています。

name: 'SATySFi'
description: 'Build SATySFi file'
inputs:
  main:
    description: 'Target file which will be built via SATySFi.'
    required: true
  packages:
    description: 'Space separated SATySFi packages which will be installed via opam & Satyrographos'
    required: false
    default: ''
outputs:
runs:
  using: docker
  image: Dockerfile
  args:
    - ${{ inputs.main }}
    - ${{ inputs.packages }}

以下が docker のエントリポイントとなるファイル (.github/actions/satysfi/entrypoint.sh) です。 ここに、04-satyrographos-example に長々と書いていたコマンドの列を書いています。

#!/bin/sh -l

main=$1
shift
packages=$@

# workaround. GitHub Actions overwrites $HOME.
export HOME=/root
eval $(opam env)

opam update
opam install $packages

satyrographos install

satysfi $main

あとはsatysfi-docker をベースイメージにした Dockerfile を書いてやればそれで完了です。CI が走るたびにイメージがビルドされて Action が実行されます。

おわりに

この記事では、GitHub Actions を使って SATySFi のパッケージや文書の CI をする説明をしました。 GitHub Actions を使うと、依存ライブラリをインストールしつつ文書をコンパイルできたり、 matrix を使ってライブラリがサポートしたいバージョンで簡単に CI を走らせることができたりします。 ライブラリの信頼性を高めたり、複数人で文書を書く際には有効だと思うので、ぜひ使ってみてください。