GitHub Actions の新バージョンが 8/8 に発表されました。
自分は過去にも旧バージョン時に GitHub Actions の入門記事を書いていたのですが、新バージョンがこれまでと大きく変わってしまっているので、この記事ではあらためて GitHub Actions についていろいろ調べたり動かしてみたりした内容をまとめます。
目次
- 注意事項
- GitHub Actions とは
- これまでの GitHub Actions とどこが変わったか
- GitHub Actions と Azure Pipelines
- 簡単な例 (Hello, World)
- ワークフローの設定
- 制限事項
- まとめ
注意事項
GitHub Actions はこの記事の執筆時点(2019/08/19)ではまだベータです。11/13 の GitHub Universe のタイミングで GA になる予定との公式発表があるので今後大きくは変わらないと思いますが、まだなにかしら変更になる可能性はあります。
ベータへの申しこみは、GitHub Actions のページから可能です。自分は元々 GitHub Actions のベータに参加していましたが、新バージョンの発表から数日してからあらためてベータ利用可能になったというメールがきて、そのタイミングから新バージョンの機能が使えるようになりました。
GitHub Actions とは
GitHub Actions とは、GitHub のリポジトリでイベントが発生したときにワークフローを実行できる仕組みです。ワークフローは複数のアクションと呼ばれるタスクから構成されます。
例えば、リポジトリに push されたときに自動テストを実行したり、新しい PR や issue が作成されたときに slack に通知を飛ばしたりといったことができます。
アクションは GitHub リポジトリや Docker Hub などで公開できるので、世界中で共有することもできます。
これまでの GitHub Actions とどこが変わったか
GitHub Actions はこれまでもベータとして利用可能でしたが、新バージョンになって大きく変更になりました。なので、旧バージョンの GitHub Actions についての情報は基本的にもう参考にならないと考えてもらっていいです。
コンセプト
まず、コンセプト面としては、CI/CD を前面に押し出してきています。これまでも CI/CD としての利用はもちろん可能だったのですが、公式としてはワークフローという言葉を使うことが多く、あまり CI/CD ツールとしてはアピールしていないという印象でした。しかし、新バージョンでは明確に CI/CD 用途として使ってもらうことを目的としているようです。
マルチプラットフォーム対応
これまでは Linux のみでしたが、Windows と macOS も利用できるようになりました。マトリクスビルドも可能なので、各 OS 上で同時にテストを実行することができるようになりました。
HCL から YAML へ
ワークフローの記述方法も大きく変わりました。これまでは HCL という HashiCorp 社製の設定言語で記述していたのですが、新バージョンでは YAML で記述するようになっています。公式ドキュメントに、
Support for the HCL syntax in GitHub Actions will be deprecated on September 30, 2019.
とあるので、GitHub Actions の HCL 記法版は 9/30 で少なくとも非推奨となるようです。このタイミングで完全に利用できなくなるのかはよくわかりませんが、HCL を YAML に変換するマイグレーションツールが公開されており、移行手順も公式ドキュメントに書かれているので、旧バージョンユーザーは新バージョンのベータが有効になり次第、早めに移行しましょう。
https://github.com/actions/migrategithub.com
料金
料金についても明確にされており、パブリックリポジトリは完全無料、プライベートリポジトリは従量制の課金体系が発表されています。具体的な金額は公式ページの下の方に書かれています。
その他
ここまでに書いた以外にも、GitHub Package Registry との連携やログの改善など、様々な点が変更になっています。一方でなくなった機能もあり、ビジュアルエディタとかが見当たらなくなっています。
GitHub Actions と Azure Pipelines
今回の大きな変更には、GitHub Actions のバックエンドが Azure Pipelines の fork 版になったということが背景としてあります。公式ドキュメントに以下のように書かれています。
The GitHub Actions virtual environments are hosted on virtual machines in Microsoft Azure with the GitHub Actions runner installed. The GitHub Actions runner is a fork of the Azure Pipelines Agent. (訳: GitHub Actions の仮想環境は Microsoft Azure 上の VM に GitHub Actions runner をインストールしてホストされています。GitHub Actions runner は Azure Pipelines Agent の fork です。)
fork なので Azure Pipelines そのまま使えるというわけではなく、YAML の記述などで異なるところがあるようです。
簡単な例 (Hello, World)
実際に簡単な GitHub Actions を作成してみます。
GitHub Actions が有効になったユーザーか organization 下に適当なリポジトリを作成し、以下のような .github/workflows/hello.yml
ファイルを作成します。
name: Hello, World! on: push jobs: build: name: Greeting runs-on: ubuntu-latest steps: - run: echo "Hello, World!"
このファイルはワークフローを定義しています。
最初の name
はワークフローの名前です。次の on
でリポジトリへの push
イベント時にこのワークフローは実行されると定義しています。
jobs
ブロックでワークフロー内で実行されるジョブを定義しています。build
と書いているところはジョブの ID で、jobs
のキーとしてユニークであれば他の文字列でも大丈夫です。name
でジョブ名、runs-on
でこのジョブが実行される仮想環境を指定しています。
steps
でジョブ内で実行されるステップを定義しています。今回は、run
でシェルスクリプトを実行するだけです。
このファイルを作成してリポジトリに push すると、GitHub Actions が実行されます。GitHub のリポジトリのページ上で Actions タブをクリックすると見られます。
Workflow runs
に表示されている最新の実行をクリックすると各ステップが表示され、Run ステップをクリックすると標準出力に "Hello, World!" と表示されていることが確認できます。
以上で、リポジトリに push されるたびに "Hello, World!" と表示するだけの簡単なワークフローが作成できました。
ワークフローの設定
ワークフローについてより詳細に解説していきます。
ワークフローとは
ワークフローの定義は、リポジトリの .github/workflows
ディレクトリ下に YAML 形式のファイルとして保存されます。
ワークフローは複数のジョブから構成される自動化されたプロセスです。1 つのリポジトリに対して複数のワークフローが作成可能です。
ジョブは複数のステップから構成されます。ステップはコマンドの実行かアクションの使用が可能です。アクションは自作することもできますし、コミュニティで共有されたアクションを利用することも可能です。
ワークフローを実行するイベント
ワークフローを実行させる方法は、3 つあります。
1 つ目は、GitHub イベントで実行させる方法です。これは簡単な例と同じように on: push
のように記述することで指定した GitHub イベントが発生したときにワークフローを実行できます。on: [push, pull_request]
のように書くことで複数のイベントに対応させることもできます。イベントの種類については公式ドキュメントを参照してください。
ちなみに、fork 先のリポジトリから PR が作成された場合、pull_request イベントは fork 元のリポジトリで実行されます。fork 先で CI 的なワークフローを実行したい場合は、push イベントを設定したほうがいいようです。
2 つ目は、cron 形式で記述して定期実行させる方法です。例として、15 分ごとに実行させる場合は on
に以下のように記述します。
on: schedule: - cron: "*/15 * * * *"
*
は YAML で特殊文字なので、cron
の記述はクオートする必要があります。
3 つ目は、外部からワークフローを実行するための方法です。repository_dispatch
イベントを引き起こすための REST API エンドポイントが用意されているので、ワークフロー側は on: repository_dispatch
のように記述し、外部からエンドポイントにリクエストを投げると実行されます。例として、curl
なら以下のようにエンドポイントにリクエストを投げてワークフローを実行できます。
curl -X POST -H "Authorization: token PERSONAL_ACCESS_TOKEN" -H "Accept: application/vnd.github.everest-preview+json" --data '{"event_type": "test-hello"}' https://api.github.com/repos/:owner/:repo/dispatches
Authorization
ヘッダーに乗せるパーソナルアクセストークンには repo
権限が必要なようです。POST するデータの event_type
は必須ですが、任意の値で大丈夫です。Accept: application/vnd.github.everest-preview+json
ヘッダーはこの機能が現時点でプレビューなので必要になっています。
ワークフローの実行をブランチやタグ、変更されたファイルのパスでフィルタする
CI/CD でよくあるのが、master
ブランチが変更されたときや特定の命名規則のタグが作成されたときだけ実行したい、特定のディレクトリ以下に変更されたときだけ実行したい、といったワークフロー実行のフィルタリングです。
GitHub Actions でも on
の設定でそういったことが可能です。デフォルトで push
イベントのみを指定したときはすべてのブランチ、すべてのタグへの push
イベントに対して実行されてしまいますが、例えば、master
ブランチに push されたときのみ実行したいときは以下のように記述します。
on: push: branches: - master tags: - "!*"
v
で始まるタグが作成されたときのみ実行したいときは以下のように記述します。1
on: push: branches: - "!*" tags: - "v*"
tests
ディレクトリ直下のファイルが変更されたときのみ実行したいときは以下のように記述します。
on: push: paths: - "tests/**"
branches
, tags
, paths
の文法は、.gitignore と同じと公式ドキュメントには書かれています。2
所感としては、なにも指定しないとタグでも動いてしまうのがちょっと罠ですね。タグを除外する指定を忘れると、master
ブランチのみにしたつもりでも気づいたらタグでも動いててトラブル発生とかはありそうです。
needs を使ったフロー制御
なにも指定しないとワークフロー内のすべてのジョブは並列実行されますが、needs
を指定することによりジョブの実行順序を制御することができます。
name: Flow Control on: push jobs: job1: name: Greeting 1 runs-on: ubuntu-latest steps: - run: echo "Hello, World 1" job2: name: Greeting 2 needs: job1 runs-on: ubuntu-latest steps: - run: echo "Hello, World 2" job3: name: Greeting 3 needs: job1 runs-on: ubuntu-latest steps: - run: echo "Hello, World 3" job4: name: Greeting 4 needs: [job2, job3] runs-on: ubuntu-latest steps: - run: echo "Hello, World 4"
上記の例だと、job1
が完了した後に job2
, job3
が並列実行され、両方のジョブが完了すると job4
が実行されます。
このあたり、現時点だとジョブの依存関係が視覚的に可視化されていないので、よくあるパイプラインのグラフ的なのが表示されるようになってほしいです。
仮想環境の選択とコンテナ内ビルド
ジョブごとに runs-on
は必須で、ジョブが実行される仮想環境を選択する必要があります。指定できる環境は公式ドキュメントを参照してください。
仮想環境に入ってるソフトウェアも公式ドキュメントで公開されています。一通りの開発環境は全部入りという感じですね。
一つのジョブのすべてのステップは同じ仮想環境インスタンス上で実行されます。同じジョブ内であればファイルシステムを通じてデータのやり取りが可能ということです。
通常だと仮想環境を直接使ってすべてのステップが実行されますが、container
を指定することでコンテナ内ビルドが可能です。
name: Hello, World! on: push jobs: build: name: Greeting runs-on: ubuntu-latest container: image: node steps: - run: node -e 'console.log("Hello, World!");'
上の例だと、node
イメージを使ってそのジョブのステップがすべて実行されます。(後述するコンテナ内で実行されるアクションについてはその限りではありません)
context と expression と if によるステップの条件実行
ワークフローは自身の実行時情報を context として持っています。どういう情報を持っているかは、公式ドキュメントをご参照ください。
ワークフローの YAML ファイルからこの context 情報にアクセスするために、expression を使用します。expression は context にアクセスするだけでなく、演算式や関数を呼び出すこともできます。
${{ <expression> }}
のようにワークフローファイルに書くことで実行時に評価してくれます。例として、以下のように記述することで GitHub のコンテキスト情報を出力することができます。github
が組み込みの GitHub のコンテキスト情報を表す変数で、toJson
が受け取った値を JSON 形式の文字列にして返す組み込み関数です。
name: GitHub Context on: push jobs: github-context: name: GitHub Context runs-on: ubuntu-latest steps: - name: Dump GitHub Context env: GITHUB_CONTEXT: ${{ toJson(github) }} run: echo "${GITHUB_CONTEXT}"
ワークフローをトリガーした GitHub のイベント情報とかが含まれているので、ジョブの最初にとりあえず出力しておくとデバッグ時に役に立ちそうです。
さらに、if
とこの expression を組み合わせることで、ステップを特定の条件を満たすときだけ実行されるように設定できます。例えば、pull_request
イベントで action
が opened
なときだけ実行されるようにしたいときは以下のように設定します。3
name: If on: pull_request jobs: if-pull-request-is-opened: name: If runs-on: ubuntu-latest steps: - name: echo if pull request is opened if: github.event_name == 'pull_request' && github.event.action == 'opened' run: echo "pull request is opened"
if
の設定では、${{ <expression> }}
のように記述するのではなく、直接 expression が書けるようになっています。また、always
や failure
のような組み込みの条件式もあるので、公式ドキュメントをご参照ください。
環境変数と秘密情報
GitHub Actions は実行時にデフォルトで環境変数が設定されます。公式ドキュメントをご参照ください。
さらに、ワークフローの設定ファイルでも env
で環境変数を設定できます。
name: env hello on: push jobs: build: name: Greeting runs-on: ubuntu-latest steps: - env: NAME: Mona run: echo "Hello, ${NAME}"
上の例では、NAME
環境変数に値を設定して run
の中で使用しています。ちなみに、GITHUB_
で始まる環境変数は GitHub 側の予約語として使えないようになっているようです。
秘密情報については、リポジトリの Settings
→ Secrets
から設定します。
すると、以下のように expression で secrets
コンテキストから利用できるようになります。
name: secret hello on: push jobs: build: name: Greeting runs-on: ubuntu-latest steps: - run: echo "Hello, ${{ secrets.SECRET_VALUE }}"
ちなみに、ログ上では秘密情報はマスクされるようです。
さらに、組み込みで GITHUB_TOKEN
という秘密情報が存在します。ジョブ内で GitHub API を叩いたりするときに使います。トークンが持ってるパーミッションは公式ドキュメントをご参照ください。
ちなみに、fork されたリポジトリからトリガーされたワークフローでは GITHUB_TOKEN
以外の秘密情報は渡されません。GITHUB_TOKEN
は渡されはしますが、write
権限が剥奪され read
権限のみになります。
アクション
よくある処理は、アクションという単位で公開や共有が可能で、steps
の中で呼び出すことが可能です。
例として、リポジトリをチェックアウトする actions/checkout アクションを使用してみます。
name: Checkout on: push jobs: build: name: Greeting runs-on: ubuntu-latest steps: - uses: actions/checkout@v1 - run: cat README.md
uses: actions/checkout@v1
でアクションが実行され、リポジトリがチェックアウトされます。@
で git の SHA やブランチ、リリースなどを指定して特定のバージョンのアクションを利用することができます。
以下のように with
でアクションにパラメータを渡すことも可能です。
name: Checkout on: push jobs: build: name: Greeting runs-on: ubuntu-latest steps: - uses: actions/checkout@v1 with: fetch-depth: 1 - run: cat README.md
actions/checkout
は fetch-depth: 1
を指定することで、いわゆる shallow clone 相当の挙動になるようです。
actions/checkout
のような GitHub 社製のアクションは actions org で公開されています。
アクションは 2 種類の方法で作成することができます。1 つ目は Docker コンテナで、この種類のアクションは Linux の仮想環境のみで利用可能です。もう 1 つは JavaScript で、こちらはすべての仮想環境で利用可能です。具体的な作成方法については、この記事では省きます。
マトリクスビルド
strategy
を使うことで複数の OS や言語バージョンの組み合わせで同じジョブを実行する、いわゆるマトリクスビルド的なことが可能になります。以下は、actions/setup-node アクションを利用した、複数 OS、複数 Node バージョンでのマトリクスビルドの例です。
name: Node.js Matrix Build on: push jobs: build: name: Node.js ${{ matrix.os }} ${{ matrix.node }} runs-on: ${{ matrix.os }} strategy: matrix: os: [ubuntu-latest, windows-latest] node: [6, 8, 10] steps: - name: Set Node.js uses: actions/setup-node@v1 with: node-version: ${{ matrix.node }} - run: node -v
actions/setup-node
は、仮想環境上の node 環境を指定したバージョンにしてくれます。この例では node -v
を叩いてるだけですが、実際の CI では npm install
や npm test
といったことを行っていく想定です。
権限
ワークフローの作成、編集、Actions タブの閲覧にはリポジトリの write
か admin
の権限が必要なようです。
2019/08/22 追記: ↑嘘でした。Actions タブの閲覧は read
権限あればできるようです。
通知
GitHub の Settings
→ Notifications
から GitHub Actions の通知についての設定があります。
メール通知と Web Notifications による通知があり、失敗したときだけ送るようにするかの設定があります。
制限事項
GitHub Actions の利用については以下の制限があります。
- リポジトリごとに最大で 20 ワークフローまで同時実行できる
- リポジトリ内のすべてのアクション全体で 1 時間につき 1,000 API リクエストまで実行できる
- ワークフロー内のすべてのジョブの最大実行時間は 6 時間まで
- リポジトリごとにすべてのワークフロー全体で 20 ジョブまで同時実行できる
あとは、当たり前ですが暗号通貨採掘のような本来使われるべきでない目的での利用も明確に禁止されています。気になる人は公式ドキュメントを読みましょう。
まとめ
GitHub Actions について、旧バージョンとの違い、ワークフローの設定でどのようなことが設定できるかといったことを中心に書きました。実践的なワークフローとか、アクションの作成&共有方法とかについてはまとめきれなかったので、また別の記事でやります。(時間があれば…)
基本的には公式ドキュメントの情報をまとめただけの記事なので、最新情報を確認するときはそちらを見てください。
個人的な所感としましては、やはりパブリックリポジトリ完全無料で 20 並列を使えるのはとても強いですね。Windows や macOS 対応も強いですし、ちょっとした OSS のビルドの選択肢としてはかなり有力だと思います。
一方で、現状気になってる点としては以下の部分があります。
- CPU やメモリといったリソースを強化できるのか
- キャッシュ
- ジョブ間の成果物の受け渡し
- upload-artifact というそれっぽいアクションはあるけど、公式ドキュメント上では見当たらない
さらに、やはりまだベータだからか挙動が怪しいところもちょいちょいあります。本格的な運用は GA まで待ってもよさそうです。
また、旧バージョンとはだいぶ別物になってしまってるので、GitHub Actions という名前はこの機会に変えてしまってもよかったのではと思います。今後 GitHub Actions について検索するときに、旧バージョンについての記事が邪魔をしそうです。
全体としては CI/CD 業界が盛り上がること自体はとてもありがたく、また新たな CI 探求沼が増えたなという気持ちなので、引き続きいろいろ試していきたいです。
追記
アクションの作成方法を続編として書きました。
2020/02/27 追記
『GitHub Actions 実践入門』の電子書籍版を BOOTH で販売開始しました。より詳細な GitHub Actions の解説や最新情報が書かれています。
-
2019/08/22 追記: こちらの書き込みを見て確認したところ、
branches
にrefs/tags/*
を指定するとタグへの push だけビルドできるようになりました(正直このあたりの挙動はまだ変わりそうな気がします。。)↩ -
tests/**
を指定すればtests
ディレクトリ直下だけでなくサブディレクトリ全体を対象にできるはずですが、手元で試したときはそうならず、ディレクトリ直下のファイルを変更したときのみワークフローが開始されました。このあたりはサポートに問い合わせ中です。↩ -
現状なぜか
pull_request
イベントが発生するのがopened
だけになってしまってるので意味がない設定になってしまってます。サポートに問い合わせ中です。↩