新GitHub ActionsはCIに使えるか

**GitHub Actionsは執筆時の2019/08/12時点でまだbetaであり、ここに記載されている内容やリンクは正式版リリースに伴って変更される可能性がありますので、注意してください**

# tl;dr;

- matrix buildが可能になったのでOSSのCIとしてTravisCIの代替になりえる
- 細かい機能はまだ色々足りていない（TravisCIは老舗中の老舗なので比べてしまうのは酷ですが）
- 単純にTravisCIの置き換えとしてmatrix buildしたい場合は以下のサンプルコードを参照

nodejs（ubuntu-18.04, ubuntu-16.04 x nodejs 10, 12の4パターン）

    name: "CI"
    on: [push]
    
    jobs:
      check_pr:
        strategy:
          matrix:
            node: [10.x, 12.x]
            os: [ubuntu-18.04, ubuntu-16.04]
    
        runs-on: ${{ matrix.os }}
        steps:
        - uses: actions/checkout@v1
        - name: Setup node version
          uses: actions/setup-node@master
          with:
            version: ${{ matrix.node }}
    
        - name: "npm ci"
          run: npm ci
    
        - name: "npm run test"
          run: npm run test

ruby（ubuntu-latest x ruby 2.4, 2.5, 2.6の3パターン）

    name: CI
    on: [push]
    
    jobs:
      test:
        name: Test
        runs-on: ubuntu-latest
        strategy:
          matrix:
            ruby: [ '2.4.x', '2.5.x', '2.6.x' ]
    
        steps:
        - uses: actions/checkout@master
        - name: Set up Ruby version
          uses: actions/setup-ruby@master
          with:
            version: ${{ matrix.ruby }}
    
        - name: Set up bundler
          run: gem install bundler
        
        - name: bundle install
          run: bundle install --jobs 4 --retry 3
    
        - name: test
          run: bundle exec rake spec

# 新しいGitHub Actionsについて

2019/08/09にGitHub Actionsが新しくなると発表され、従来に比べて特にCIとしての機能が強化されました。特に目玉機能としてTravisCIのようなmatrix buildが追加されました。

CIサービスは老舗、新興合わせて無数に存在しますが、OSS界隈ではほぼTravisCI一択だったと思います。TravisCIが使われるのは老舗という理由が大きいと思いますが、OS x 言語バージョンのmatrixでテストを行うことが非常に簡単というのも大きな理由だと思います。

matrixビルドの結果画面のUIも非常によくできており、どの組み合わせでテストが成功、失敗したかがひと目で分かるようになっています。

![travis_matrix_result_ui.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/10384/e5b8eba4-8141-6da6-b01c-3f49b129e6bd.png)

GitHub Actionsでmatrix buildを行った場合でもpull-reqの画面に組み合わせごとのビルド結果が並びます。TravisCIのUIほどではないですが、実用的には十分な分かりやすさでしょう。

![github_actions_matrix.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/10384/732b729b-d70b-ef8f-27ff-18870855a858.png)

また、大きな変更として今までのHCL（HashiCorp Configuration Language）から他のCIサービス同様にyamlで設定するようになりました。そのため、従来までの.workflowファイルは書き直す必要がありそうです。

自分はまだ使用していないですが、マイグレーションツールも用意されているようです。

[https://help.github.com/en/articles/migrating-github-actions-from-hcl-syntax-to-yaml-syntax](https://help.github.com/en/articles/migrating-github-actions-from-hcl-syntax-to-yaml-syntax)

## ドキュメント一覧

- ドキュメントTOP [https://help.github.com/en/articles/about-actions](https://help.github.com/en/articles/about-actions)
- トリガー一覧 [https://help.github.com/en/articles/events-that-trigger-workflows](https://help.github.com/en/articles/events-that-trigger-workflows)
- 基本的な文法 [https://help.github.com/en/articles/workflow-syntax-for-github-actions](https://help.github.com/en/articles/workflow-syntax-for-github-actions#jobsjob_idsteps)
- 式と関数 [https://help.github.com/en/articles/contexts-and-expression-syntax-for-github-actions](https://help.github.com/en/articles/contexts-and-expression-syntax-for-github-actions)
- 実行環境と環境変数 [https://help.github.com/en/articles/virtual-environments-for-github-actions](https://help.github.com/en/articles/virtual-environments-for-github-actions)
- VMにプリインされている一覧 [https://help.github.com/en/articles/software-in-virtual-environments-for-github-actions](https://help.github.com/en/articles/software-in-virtual-environments-for-github-actions)

# matrix build

yamlの書き方については冒頭のサンプルを参照してください。

公式ドキュメント内にサンプルとなりそうなコードは乏しいですが、以下のリポジトリのREADMEを見れば大体分かると思います。

- [https://github.com/actions/setup-node](https://github.com/actions/setup-node)
- [https://github.com/actions/setup-ruby](https://github.com/actions/setup-ruby)

## actions/setup-xxxについて

TravisCIでは言語のバージョン切り替えは既存のツールを使用しており、例えばrubyのバージョン切り替えにはrvmが使われていました。GitHub Actionsでは[https://github.com/actions/](https://github.com/actions/) に存在するsetup-xxxxを使うのをデファクトにしたいらしい？

少なくとも、リポジトリで新しいGitHub Actionsを有効にして最初にテンプレを選択する画面でnodejsを選択した場合は、このようにactions/setup-nodeを使用するyamlのサンプルが表示されます。

![github_actions_wizard.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/10384/6b1d5dc1-c640-24e5-0605-965011281231.png)


setup-xxxxの実装は言語毎に異なっているようです。例えば[setup-ruby](https://github.com/actions/setup-ruby)はVMにインストールされているruby（2.3.7, 2.4.6, 2.5.5, 2.6.2）を切り替えているだけのように見えます。 一方、[setup-node](https://github.com/actions/setup-node)はVM内のバージョンを切り替えるのではなく、指定したバージョンのバイナリをダウンロードしてからPATHを通しているようです。

なぜrvmのような既存のツールを使わないのか分からないですが、とにかくsetup-xxxは独自の実装になっているため、rvmのようなツールでインストールできるバージョンが全て使えるわけではないことに注意しましょう。（少なくとも今は）

rubyに関しては既にrvmを使う方法を紹介されている方もいましたので、どうしてもその類のツールを使いたい場合は参考になると思います。

[GitHub Actions がローンチされたのでマイグレートしてる](https://www.hsbt.org/diary/20190811.html)

## dockerでmatrix buildはおそらく使えない？

前述のようにVMに言語の特定バージョンをインストールする代わりに、dockerイメージのタグ指定にmatrixの変数を使えれば解決"でした"。

試してみたのですが、どうも`uses`の指定では変数の展開が許されていないのか、文法エラーになってしまいました・・・。

```yaml
on: [push]
jobs:
  check_pr:
    strategy:
      matrix:
        node: [10.x, 12.x]
        os: [ubuntu-18.04, ubuntu-16.04]

    runs-on: ${{ matrix.os }}
    steps:
    - uses: actions/checkout@v1
    - name: "node version"
      uses: docker://node:${{ matrix.node }} # ここがエラーになる
      continue-on-error: true
      with:
        args: node --version
```

エラーメッセージ:

```
### ERRORED 06:07:27Z

- Your workflow file was invalid: .github/workflows/blog.yml (Line: 15, Col: 13): Unrecognized named-value: 'matrix'. Located at position 1 within expression: matrix.node
```

自分の調査不足の可能性はありますが、今後のバージョンアップで対応される可能性もあるかもしれません。自分は一旦諦めて今後のアップデートを期待することにしました。

# docker imageを使う場合

`run`に普通にコマンドを渡すとVM上で実行されますが、`uses: docker://node:10`のようにdockerイメージやタグを指定することで、旧Actionsのようにdockerを使用できます。

実際に使ってみてハマったポイントをいくつか紹介します。

## 実行コマンドの指定

`uses` だけしか指定しなかった場合はENTRYPOINTのコマンドが実行されるようです。従って、例えば`docker://node:10`を使用して`npm install`を実行したい場合はこのように書く必要があります。

    - name: "npm install"
      uses: docker://node:10
      with:
        args: npm install

[ドキュメント](https://help.github.com/en/articles/workflow-syntax-for-github-actions#jobsjob_idstepsargs)を読んだ感じでは`args`は`with`にネストさせる必要はないように見えるのですが、実際にはこのように書く必要がありました。

実際に実験して動作を確認した[リポジトリはこちら](https://github.com/Kesin11/github_actions_matrix_js_sandbox/blob/master/.github/workflows/ci_docker.yml)です。

## DockerfileでUSERを指定してはいけない

サラッと書いてあるので見逃しやすいのですが、[ドキュメント](https://help.github.com/en/articles/virtual-environments-for-github-actions#filesystems-on-github-hosted-machines)にその記述があります。


> /github/workspace - Note: GitHub Actions must be run by the default Docker user (root). Ensure your Dockerfile does not set the USER instruction, otherwise you will not be able to access GITHUB_WORKSPACE.

root以外のユーザで実行されてしまうと`$GITHUB_WORKSPACE`に書き込むことができないようです。  
`docker://node`のようにDockerfileに`USER`が指定されていない場合はデフォルトのrootユーザで実行されるので問題はないです。  
一方で、`docker://circleci/node`のように`USER`が指定されているイメージの場合は`npm install`などを行った際にPermission deniedが起きてしまいます。

その場合でもsudoを付けて`sudo npm install`などとすることで動作はしました。（他に副作用があるかもしれませんが、詳しくは調査していないです）

CircleCIからの移行組は注意すべきポイントだと思います。

# その他のCIサービスで一般的な機能の有無

## バッジ
**2019/09/10 追記**
actionsのステータスバッジを追加する方法について公式にドキュメントが追加されました。

https://help.github.com/en/articles/configuring-a-workflow#adding-a-workflow-status-badge-to-your-repository

今後は、3rdパーティ製よりも公式のバッジのURLを使用すると良いでしょう。

thanks! @technote-space  


~~OSSのREADME冒頭に置いてある最新のビルドが成功しているかどうかを示しているあれです。 ドキュメントにそのような記述はなかったので、少なくとも今の所は提供されていないようです。~~

~~3rdパーティ製ではありますがLambdaなどを使って自作した方が使わせてくれるものがいくつかあるので、それを使わせてもらうことで一応対応はできます。~~

~~このようなものです [https://github.com/atrox/github-actions-badge](https://github.com/atrox/github-actions-badge)~~


## ci skip

多分効かないっぽい？コミットメッセージに[ci skip]を付けても普通に実行されてしまったと思います。

Actions自体がpushやpr以外のトリガーでも動作するものなので仕方ないか。

## secret

GitHub以外のサービスと連携する場合などのために、トークン等の文字列を管理する仕組みが通常のCIサービスには存在します。

GitHub Actionsにも同等の仕組みはあるようです。今回試してはいないのですが、ドキュメントに記載があったのでおそらく普通に使えそうです。

[https://help.github.com/en/articles/virtual-environments-for-github-actions#creating-and-using-secrets-encrypted-variables](https://help.github.com/en/articles/virtual-environments-for-github-actions#creating-and-using-secrets-encrypted-variables)

## キャッシュ

`npm install`のnode_modulesや、`bundle install —path=vendor`のvendorディレクトリを保存しておいて、次回の実行時に依存ライブラリをダウンロードしてくる工程をスキップするための仕組みです。

一般的なCIサービスでは当然用意されているのですが、GitHub Actionsでは少なくともドキュメントにこれについての記述が見つけられませんでした。

OSSのように依存が少ない場合は毎回ダウンロードしてもそれほど時間はかからないのでそれほど問題ではないですが、RailsやiOS/Androidのビルドなど依存ライブラリがどうしても多くなるリポジトリの場合は、キャッシュの仕組みがあるかないかでビルド時間がかなり変わってきます。

自前でS3やGCSに保存するロジックを書けば同等のことは可能でしょうが、公式でサポートしてくれないと使いづらいですね。まだβですし今後に期待。

（actions/setup-nodeの挙動やソースを見るとキャッシュの仕組みは何らかあるようなので、それをハックすることで自分でも使えるようにすることは可能かもしれません。ですが少なくともドキュメントに書かれていない時点で非公式でしょうし、やるとしても安定はしていないという心構えはしておいた方が良いでしょう）

# 総評

OSSのCI用途としてTravisCIの代替にはなるという印象。ですが、まだ細かいところは機能が足りなかったりするため、今すぐ乗り換える必要があるほどではないかなと思います。

バックにMS様のAzureが付いているので、matrixの並列数が多くてもクラウドの力で何とかしてくれそう。

OSS以外のWebサーバーやiOS/AndroidのビルドにはCircleCIを使っている方が多いと思います。Linux/macOS/WindowsのVMが揃っているのでCircleCIでできることは基本的に置き換えられるはずですが、メリットがあるかと言われるとまだ微妙という印象。

少なくともキャッシュの仕組みを誰かが3rdパーティ製actionsとして公開してくれるか、公式が用意してくれないと厳しいと思います。

一方で、GitHub Actionsはpushやpr以外のトリガー（issueのコメントとかラベルを付けたタイミング）で起動することもできます。最初はCIとして使わずとも、CircleCIでは自動化できずにGitHubのwebhookを使ったbotなどで対応してきた部分からGitHub Actionsに置き換えていくと良いのではないかと思います。

最後に自分が実験したリポジトリのurlを貼っておきます。これから新しいGitHub Actionsを試してみたい方は参考にしてみてください。

- matrix build + nodejs
    - https://github.com/Kesin11/github_actions_matrix_js_sandbox/blob/master/.github/workflows/ci_vm.yml
- docker + nodejs
    - https://github.com/Kesin11/github_actions_matrix_js_sandbox/blob/master/.github/workflows/ci_docker.yml
- matrix build + ruby
    - https://github.com/Kesin11/danger-textlint/blob/master/.github/workflows/ci.yml