Travis CIからGitHub Actionsに移行してみました。

Table of Contents

GitHub Actionsとは

GitHubが提供するCI/CDの仕組みです。

yamlファイル1つであらゆる処理を実行できるのがポイント。
雑に言うと、JenkinsfileのGitHub版みたいなイメージでOKです。

GitHub Actionsそのものの経緯や、料金、仕様、使い方などは以下のサイトが丁寧で分かりやすかったです。

本記事では基本的な部分は割愛しますので、そちらをご覧下さい。

なぜGitHub Actionsを使うのか

以下の理由から、当初は興味がありませんでした。

  • HCLよりYAMLが好き
  • Travis CIで十分やりたいことができるし、移行が面倒
  • CI/CDではなくイベントトリガーアクションがメイン(に思えた)
  • ビジュアルエディタは気になりつつも必要性を感じない

しかし、今年になって一気に変わりました。

  • YAMLで書ける!
  • CI/CDがメイン
  • GitHubリポジトリとの強力で安心な連携 (同じGitHubですから)
  • Matrix Buildができる
  • Dockerを使える
  • Dockerを使わなくても色々できそう
  • (ネットを見る限り) ビルド待ち時間が無さそう

元々できたこともあると思いますが、この辺が魅力に感じたので使うことにしました。

移行前と移行後

対象リポジトリ

私が開発しているowcliというCLIフレームワークでやりました。

owcliの宣伝

APIフレームワークに見られるような、階層構造開発できるCLIフレームワークです。
巨大CLIを作りたい場合など是非お試し下さい😄

  • コマンド/サブコマンドまで対応し、各helpドキュメントも自動生成
  • 引数情報は自動でClassに変換
  • 関数型言語のようなメソッドチェーン

少し古いバージョンですが以前書いた記事はこちら。

移行前のファイル

Travis CIのYAMLは以下です。

travis.yml

language:
  - python
matrix:
  include:
  - python: "3.6"
    dist: xenial
  - python: "3.7"
    dist: xenial
install:
  - pip install pipenv
  - make init
script:
  - make test-cli
notifications:
  slack:
    secure: zxxxxx

Travis CIlanguage: pythonを指定することで、PythonやMake, batsなどが入ったVMが提供されていました。
そのため、yamlファイルだけを見るとTravis CIのほうがシンプルだと思います。

移行後のファイル

行数は3倍になりました。

.github/workflows/e2e.yml

name: "e2e tests"

on:
  push:
    paths:
      - '.github/**/*'
      - 'owcli/*'
      - 'owcli/**/*'
      - 'tests/*'
      - 'tests/**/*'
      - 'Pipfile.lock'
  schedule:
    - cron: '0 0 * * *'

jobs:
  test:
    runs-on: ubuntu-latest
    strategy:
      matrix:
        python: [ '3.6', '3.7']
    name: Python ${{ matrix.python }}

    steps:
    - name: Checkout a repository
      uses: actions/checkout@v1
    - name: Install bats
      run: sudo npm install -g bats
    - name: Set up Python
      uses: actions/setup-python@v1
      with:
        python-version: ${{ matrix.python }}
    - name: Install dependencies
      run:  |
        python -m pip install --upgrade pip pipfile-requirements
        pipfile2req > requirements.txt
        pip install -r requirements.txt
    - name: Test with bats
      run: bats tests/test.bats
    - name: Slack notification
      uses: homoluctus/slatify@master
      if: always()
      with:
        type: ${{ job.status }}
        job_name: ":python:*${{ matrix.python }}* e2e tests"
        icon_emoji: "tio2"
        url: ${{ secrets.SLACK_WEBHOOK }}

しかし、この比較はフェアではありません。
今回の移行に伴い、ファイルに含まれる情報として以下のような差分があるのです。

  • 変更時にジョブを実行するファイルを制限する
  • 定期実行する
  • makeを使わない
  • pipenvを使わない
    • pipenvを使うことにより、travis.yamlの方はMatrix buildがちゃんとできていなかった
  • Slackのメッセージを細かく制御する

本記事は比較すること自体が目的ではないため、ご了承ください。

YAMLファイル作成中に疑問に思ったことと回答

Actionの一覧はどこにある?

actionsにぶら下がっているリポジトリがそのようです。

定期的に実行する方法は?

on.scheduleにcron形式で設定すればOKでした。

こんな感じですね。

on:
  schedule:
    - cron: '0 0 * * *'

Status badgeが欲しい

Markdownの場合、以下のように書けばOKです。

[![Actions Status](https://github.com/{ユーザ名}/{リポジトリ名}/workflows/{ワークフロー名}/badge.svg)](https://github.com/{ユーザ名}/{リポジトリ名}/actions)

owcliだと下記のように書きます。

[![Actions Status](https://github.com/tadashi-aikawa/owcli/workflows/e2e%20tests/badge.svg)](https://github.com/tadashi-aikawa/owcli/actions)

表示結果↓ Actions Status

参考: Automating your work with Github Actions

Pathsの構文がよく分からない…

owcliでもこう書いていますのでお察しかもしれませんが..

on:
  push:
    paths:
      - '.github/**/*'
      - 'owcli/*'
      - 'owcli/**/*'
      - 'tests/*'
      - 'tests/**/*'
      - 'Pipfile.lock'

公式では『.gitignoreの書き方でOK』と説明されているようですが、実際はそうなっていないとのこと。

参考: GitHub Actions 入門

不要なWorkflowの結果を消したい

試行錯誤した過去のWorkflowがずっと残っており、邪魔なので消したいです..。

今のところ消し方が分かりません.. ご存知の方いらっしゃいましたらTwitterにコメントいただけると大変助かります🙇

ビルドスピードとキャッシュ

ビルドキャッシュの対応有無

GitHub Actionsは現時点でビルドキャッシュに対応していないようです。

Dockerでそれまでの結果をキャッシュ化したい場合は、Dockerイメージを作ってアップロードする必要がありそうです。

I want a predefined Docker container to be spun up and get right to work. You can have that too! If you create a Docker image and upload it to Docker Hub or another public registry, you can instruct your Action to use that Docker image specifically using the docker:// form in the uses key.

HCLだった時代にはDockerfileの結果はキャッシュとして考慮されていたようですね。

I can confirm that the previous HCL syntax version of Actions does show “Using cache” when building from a Dockerfile as seen here. However, since upgrading to the yml workflows, it stopped caching, as seen in the lack of “Using cache” here.

今後のアップデートに期待したいところですが、構造を考えると難しそうな気がします..。

ビルドスピードを上げるには

インストールやビルドのタスクをできるだけ高速化することが大事だと思いました。

たとえば、owcliのテストで使うBatsは複数のインストール方法があります。

  • npm
  • ソースからビルド
  • Docker

実際に比較していませんが、この中で最速なのは恐らくnpmです。
npm install -g batsは12秒で終わりました。

GitHub Actionsが利用するイメージはかなり全部盛りです。
Ubuntu 18.04 LTSの場合は以下に記載されたpackageが入っています。

ビルドキャッシュが使えない以上、イメージに含まれているpackageを最大限に利用してジョブを作成していきましょう👍

並列処理/最大20並列の恩恵

たとえ他のCIサービスより1つのジョブを実行する時間がかかっても、最大20並列できるというのは圧倒的メリットです😄

Matrix Buildをしているプロダクトなら、総合的にはGitHub Actionsの方が早くジョブの実行が完了するのではないでしょうか。

Slack通知

現状、GitHub ActionsはSlack通知の仕組みを提供していません。
Slack通知は必須のため、Actionをいくつか調べたところ以下を利用させていただくことにしました。

Slatifyに決めた理由

一番の決め手はTwitterでお声がけいただいたことですね😃

もちろん、それだけではありません。

  • Ownerの技術的背景に親近感を感じた (TS/Python/Goなど)
  • Ownerが日本人
  • 機能が必要十分で直感的だった
  • 要望をFBしたらスピーディーに対応いただけた

SlackのActionが今後沢山出てきそうですが、個人的に応援しております👍

設定方法

owcliでは以下の様な設定にしています。

    - name: Slack notification
      uses: homoluctus/slatify@master
      if: always()
      with:
        type: ${{ job.status }}
        job_name: ":python:*${{ matrix.python }}* e2e tests"
        icon_emoji: "tio2"
        url: ${{ secrets.SLACK_WEBHOOK }}

事前準備として、リポジトリの秘密情報SLACK_WEBHOOKにSlack Incoming Webhooks URLを設定する必要があります。

通知の結果はこのように表示されます。

今後、失敗したときはメンションを飛ばすようにするかもしれません。 (job_nameに入れればいけそう)

総括

Travis CIからGitHub Actionsに移行したとき、気になったことと知見をまとめてみました。

今のところ、イベント発生からジョブ実行までの間にほぼラグはありません。
また、ジョブの実行速度自体も非常に高速です。

現状は招待制ベータであるため、利用ユーザはそこまで多くないと思います。
実際、私も申し込みをしてから利用開始までは1ヶ月くらいかかりました。
正式にサービスインした後もこのスピード感が維持されるかは要注目です。

ただ、スピード感が落ちたとしてもGitHub Actionsは間違い無く有用です。 そのため、owcli以外のプロジェクトもGitHub Actionsに随時移行していきます。