-----
2019/08/18 追記:GitHub Actions が新バージョンで大きく変わったため、新しい記事を書きました。こちらの記事は古い内容なので、新しい記事を参照してください。
-----
現在パブリックベータの GitHub Actions が自分も使えるようになったので、いろいろ調べたり動かしてみたりした内容をまとめます。
目次
- 注意事項
- GitHub Actions とは
- 簡単な例 (Hello, World)
- ワークフローの設定
- ワークフローの制限事項
- アクションの作成と公開
- Docker イメージの作成
- アクションの実行環境
- まとめ
注意事項
GitHub Actions は、この記事の執筆時点(2019/01/13)ではまだパブリックベータの段階です。まだ本格的な開発用途では使わないほうがいいですし、将来的に機能や仕様などが大きく変更になる可能性があります。
パブリックベータへの参加は、GitHub Actions のページから可能です。自分のときは有効になっても特にメールとかは来なくて、けっこう経ってからいつの間にか有効になってました。
今は GitHub Actions でワークフローを作成できるのは、
- プライベートリポジトリ
- パブリックリポジトリの
push
イベントのみ
に限定されています。
プライベートリポジトリは年明けに無料プランでも利用できるようになったので、GitHub Actions を実験するのにもちょうどいいタイミングですね。
GitHub Actions とは
GitHub Actions とは、GitHub のリポジトリでイベントが発生したときにワークフローを実行できる仕組みです。ワークフローは複数のアクションで構成され、アクションは Docker コンテナでタスクを実行します。
例えば、リポジトリに push されたときに自動テストを実行したり、新しい PR や issue が作成されたときに slack に通知を飛ばしたりといったことができます。
アクションは GitHub リポジトリや Docker Hub などで公開できるので、世界中で共有することもできます。
簡単な例 (Hello, World)
実際に簡単な GitHub Actions を作成してみます。
GitHub Actions が有効になったユーザーか organization 下に適当なリポジトリを作成し、以下のような階層でファイルを用意します。
|-- (repository) | |__ .github | |__ main.workflow | |__ action-a | │__ Dockerfile | |__ entrypoint.sh |
action-a/Dockerfile
の中身は以下のようにします。
FROM debian:9.5-slim LABEL "com.github.actions.name"="Hello World" LABEL "com.github.actions.description"="Write arguments to the standard output" LABEL "com.github.actions.icon"="mic" LABEL "com.github.actions.color"="purple" ADD entrypoint.sh /entrypoint.sh ENTRYPOINT ["/entrypoint.sh"]
このファイルは、ワークフロー内で実行されるアクションを定義しています。中身としてはリポジトリ内に置いた entrypoint.sh
を呼び出すだけです。(LABEL
については後述)
action-a/entrypoint.sh
の中身は以下です。
#!/bin/sh -l sh -c "echo $*"
このシェルスクリプトが先ほどの Dockerfile
から実行されます。
entrypoint.sh
はワークフロー内で実行するために実行権限が必要なので、chmod
コマンドで実行権限を与えておきます。
$ chmod +x entrypoint.sh
.github/main.workflow
の中身は以下です。
workflow "New workflow" { on = "push" resolves = ["Hello World"] } action "Hello World" { uses = "./action-a" env = { MY_NAME = "Mona" } args = "\"Hello world, I'm $MY_NAME!\"" }
このファイルはワークフローを定義しています。
最初の workflow
ブロックの on
でリポジトリへの push 時に実行されるワークフローということを定義しています。resolves
でこのワークフローでは Hellow World
アクションを実行することを定義しています。
action
ブロックでは uses
でリポジトリ内への相対パスで action-a
ディレクトリをアクションの内容として参照しています。env
では環境変数を設定しています。args
はアクションに渡される引数を定義しており、先ほどの Dockerfile
の ENTRYPOINT
で指定した entrypoint.sh
に渡されます。
ちなみに、GitHub Actions のワークフローの設定ファイルの文法は、HCL という HashiCorp 社製の設定言語で書かれています。
こららの 3 つのファイルを作成してリポジトリに push すると、GitHub Actions が実行されます。GitHub のリポジトリのページ上で Actions タブをクリックすると見られます。(自分のときは push してから最初のビルドまで 1 分くらい待ちました)
Log をクリックするとログが見られ、最後の標準出力に Hello world, I'm Mona!
と表示されているのが確認できます。
以上で、リポジトリに push されるたびに "Hello, World!" を表示するだけの簡単なワークフローが作成できました。
ワークフローの設定
例で書いた .github/main.workflow
の設定オプションについて説明します。
workflow
ブロック
main.workflow
は複数の workflow
ブロックを含むことができます。workflow
ブロックは次のパラメータを設定できます。
on
: このワークフローをトリガーする GitHub のイベント名を指定する(指定できるイベント名は後述)- ワークフローをトリガーするイベントによってどのバージョンのワークフローファイルが使われるかが変わる
- 例えば、
push
なら push されたコミット時のワークフローファイルが使われるけど、issue_comment
ならデフォルトブランチの最新のワークフローファイルが使われる
- 例えば、
- ワークフローをトリガーするイベントによってどのバージョンのワークフローファイルが使われるかが変わる
resolves
: ワークフローで実行するアクションを指定する(文字列 or 文字列の配列)- 複数のアクションを指定したときは並列で実行される
- 後述する
action
ブロック側でneeds
で依存関係を指定するとそちらが先に実行されるので、直列で複数のアクションを実行することもできる
action
ブロック
main.workflow
は最大で 100 ブロックまで action
ブロックを含むことができます。action
ブロックは次のパラメータを設定でき、uses
は必須です。
needs
: このアクションを実行する前に完了しなければならないアクションを指定する(文字列 or 文字列の配列)- 共通の
needs
を持つアクションが並列に実行されてるとき、片方が失敗すると、もう片方も自動でキャンセルされる
- 共通の
uses
: アクションを実行する Docker イメージを指定する- 指定方法は複数あるので後述
runs
: Docker イメージが実行するコマンドを指定する- 省略したときは
Dockerfile
のENTRYPOINT
で指定したコマンドが実行される
- 省略したときは
args
: アクションに渡す引数を指定する(文字列 or 文字列の配列)- 文字列で指定したときは空白文字で分割されるので、
args = "container:release --app web"
とargs = ["container:release", "--app", "web"]
は同じ意味
- 文字列で指定したときは空白文字で分割されるので、
env
: アクションの実行環境に渡す環境変数を指定するsecrets
: アクションが環境変数としてアクセスできるようにする秘密情報の名前を指定する- 秘密情報の管理については後述
needs
を使ったフロー制御
例えば、最初に書いた例の main.workflow
を次の書き換えると、ACTION1
→ ACTION2
の順番で実行されるワークフローになります。
workflow "New workflow" { on = "push" resolves = ["ACTION2"] } action "ACTION1" { uses = "./action-a" args = "\"Hello world, I'm Action1!\"" } action "ACTION2" { needs = "ACTION1" uses = "./action-a" args = "\"Hello world, I'm Action2!\"" }
ACTION2
のログに表示される実行時間は、ACTION1
の完了を待ってる時間も含まれるようです。
uses
における Dockerfile
の指定方法
action
ブロックの uses
では、いくつかのパターンでアクションを実行する Docker イメージを指定できます。
{user}/{repo}@{ref}
: GitHub の公開リポジトリ内の特定のブランチ or ref or SHA- 例:
actions/heroku@master
- 例:
{user}/{repo}/{path}@{ref}
: GitHub の公開リポジトリ内の特定のブランチ or ref or SHA におけるサブディレクトリ- 例:
actions/aws/ec2@v2.0.1
- 例:
./path/to/dir
: ワークフローと同じリポジトリ内のパス- 例:
./.github/action/my-action
- 例:
docker://{image}:{tag}
: Docker Hub に公開されている Docker イメージdocker://{host}/{image}:{tag}
: 公開レジストリの Docker イメージ
外部のアクションを利用する場合はバージョンを指定することが推奨されています。指定しない場合は、リポジトリや Docker イメージの更新によって Action が壊れて予期しない挙動になる可能性があるので、注意してください。
ワークフローがサポートしているイベント
workflow
ブロックの on
ブロックでサポートされているイベントは、リンク先を見てください。
ちなみに、fork 先のリポジトリから PR が作成された場合、pull_request
イベントは fork 元のリポジトリで実行されます。fork 先で CI 的なワークフローを実行したい場合は、push
イベントを設定したほうがいいようです。
秘密情報の設定
action
ブロックの secrets
で設定する秘密情報は、リポジトリの Settings → Secrets で追加できます。
この秘密情報は、リポジトリに write 権限がある人は誰でもアクセスできることになるので、注意してください。
さらに注意点として、ログ出力はこの秘密情報をマスクしてくれません。一部の CI サービスなどとは違う点です。誤って echo したりしないよう気をつけてください。
秘密情報は最大 64 KB までなので、より大きな秘密情報を扱うときはその情報を暗号化してリポジトリ内にファイルとして保存し、復号化の鍵を secrets として保存するような手段をとる必要があります。
今はパブリックベータ期間なので、本当に重要な秘密情報はまだ保存しないようにしておいたほうが無難です。
2019/01/14 追記
そうなんだ。これ見れるのはコラボレータだけ? 「ログ出力はこの秘密情報をマスクしてくれません。」 "GitHub Actions 入門 - 生産性向上ブログ" https://t.co/LajWHxZdt3
— azu (@azu_re) January 14, 2019
どのような権限があればログが閲覧可能かについて見当たらなかったので、id:teppeis 先生に協力してもらって実験したところ、リポジトリの Actions タブ以下のページを見られるのはコラボレータのみのようです。なので、万が一秘密情報をログに書き出してしまっても、流出するのはコラボレータのみにおさまりそうです。
GITHUB_TOKEN
デフォルトで GITHUB_TOKEN
という GitHub の API にアクセスするためのトークンが秘密情報として存在しており、secrets
で指定することで利用することができます。
GITHUB_TOKEN
を curl
で使うときは以下のような感じになります。
curl -s -X GET -H "Authorization: token ${GITHUB_TOKEN}" https://api.github.com/repos/miyajan/test-github-actions/commits
GITHUB_TOKEN
を使った API アクセスは、リポジトリ内で 1 時間で最大 1,000 回まで実行できます。
GITHUB_TOKEN
が持っている権限についてはリンク先を見てください。
ビジュアルエディタ
ブラウザ上で .github/main.workflow
を編集するとビジュアルエディタでワークフローを設定できます。
最終的には全部コードで書いてしまう気はしますが、featured のアクション一覧とか見てみるとどういうことができるかパッと理解できるのでいいかと思います。
ワークフローの制限事項
現時点で、ワークフローには次の制限事項があります。
- 各ワークフローは最長 58 分まで動く(キューと実行時間含めて)
- 各ワークフローは最大 100 アクションまで実行できる
- 1 リポジトリにつき 2 つのワークフローまで同時に実行できる
- アクション内で実行されるタスクからさらにアクションをトリガーすることはできない
- 上で書いた
GITHUB_TOKEN
を使って push や deploy を行っても、それらのイベントに設定されているワークフローはトリガーされない
- 上で書いた
- Docker コンテナのログは最大で 64 KB までに制限されている
アクションの作成と公開
上で書いたように、作成したアクションは公開リポジトリに置くことで他リポジトリからも参照できるようになります。
例えば、最初に書いた簡単な例のアクションを他リポジトリから利用するには以下のように書きます。
action "EXTERNAL_ACTION" { uses = "miyajan/test-github-actions/action-a@master" args = "Hello world!" }
上で書いたようにバージョンを指定する関係上、アクションを公開するリポジトリは他のアプリなどとは独立して管理することを推奨されています。
また、アクションを公開するリポジトリには README.md
を用意し、以下のような内容を記述することが推奨されています。
- アクションの詳細な実行内容
- アクションが使用する環境変数
- アクションが使用する秘密情報
- 必須の引数
- 任意の引数
Docker イメージの作成
アクションを実行する Docker イメージを作成する上での注意点を書きます。そもそもの Dockerfile
などについては、Docker のドキュメントを参照してください。
USER
アクションは root
ユーザーで実行されます。Dockerfile
内で USER
を指定してしまうと GITHUB_WORKSPACE
にアクセスする権限がなくなってしまうので、指定してはいけません。
既存のイメージでは USER
を指定してしまっているものもありそうなので、そういったイメージをそのまま GitHub Actions で使い回そうとするとハマりそうです。
FROM
FROM
を設定するときは以下が推奨されています。
- 公式の Docker イメージを使う(
python:
,ruby:
など) - バージョンタグを指定する(
node:10
のようにメジャーバージョンを指定するのが望ましい) latest
は指定しない- Debian OS ベースの Docker イメージが推奨
Debian 推奨がなぜなのかはよくわかりません。
WORKDIR
GitHub Actions は /github/workspace
をワーキングディレクトリとして設定します。そのため、Dockerfile
で WORKDIR
を指定しないことが推奨されています。
ENTRYPOINT
action
ブロックで runs
を設定しないときは ENTRYPOINT
で指定したコマンドが実行されます。
注意点として、コマンドシェル形式じゃなくて exec
形式でコマンドを実行すると環境変数の埋め込みが行われないので注意してください。
例えば、簡単な例の entrypoint.sh
で、以下のようにしてしまうと Hello world, I'm $MY_NAME!
のように環境変数がそのまま表示されてしまいます。
#!/bin/sh -l echo $*
最初の例のように sh -c
で実行すると環境変数が処理されます。
CMD
action
ブロックで args
を指定すると CMD
が置き換えられます。
なので、CMD
には args
を指定しなかったときのデフォルト引数を入れたり、--help
を入れておいて必須引数を忘れたときに役に立つ情報を表示するような用途で使うといいでしょう。
LABEL
Dockerfile
内で特定の LABEL
を指定することで、ブラウザ上でのアクションの見た目を変えることができます。
com.github.actions.name
: アクション名com.github.actions.description
: アクションの説明com.github.actions.icon
: Feathers のアイコン名- サポートされているアイコン名はリンク先を見てください
com.github.actions.color
: アクションの背景色white
,yellow
,blue
,green
,orange
,red
,purple
,gray-dark
から指定する
また、次の LABEL
を指定することが推奨されて言います。
repository
: アクションのリポジトリ URLhomepage
: アクションのホームページ URLmaintainer
: アクションのメンテナンス開発者の名前- 例:
LABEL "maintainer"="Mona the octocat <octocat@github.com>"
- 例:
アクションの実行環境
1 つのワークフロー内のすべてのアクションはすべて同じ環境で実行されるので、アクション間で情報の受け渡しが可能です。
リソース
アクションの実行環境は以下のリソースで実行されます。
- 1 vCPU
- 3.75 GB メモリ
- インターネットアクセス
- 後述する環境変数
- 後述するファイルシステムへの書き込み権限
- 100 GB のディスク容量
現時点ではちょっとした用途であれば十分ですが、CPU やメモリを大量に利用するような用途には厳しそうです。
環境変数
ワークフローの設定で自身が設定した環境変数以外に、GitHub が設定する環境変数があります。
一覧はリンク先を見てください。
GITHUB_
で始まる環境変数は予約されてるので、自身で設定してしまうとエラーになります。
ファイルシステム
GitHub が /github
下にディレクトリやファイルを作成します。
/github/home
:HOME
環境変数に設定されるディレクトリ/github/workspace
: リポジトリがコピーされるディレクトリ- Docker コンテナのワーキングディレクトリになり、
GITHUB_WORKSPACE
環境変数に設定される - アクション内でこのディレクトリ以下を変更した場合、後続のアクションはその内容にアクセスできる
- root ユーザーでないとアクセスできない
- Docker コンテナのワーキングディレクトリになり、
/github/workflow/event.json
: ワークフローをトリガーした webhook イベントのレスポンスGITHUB_EVENT_PATH
環境変数に設定される
終了ステータス
アクションの終了ステータスは次の 3 種類があります。
0
:success
相当で、成功を意味する78
:neutral
相当で、成功でも失敗でもなく、特定の条件を満たさなかったことを意味する- 例えば、filter action で特定の条件以外ではビルドしないような用途でこのステータスを使う
- その他:
failure
相当で、失敗を意味する
filter action については他の方が解説されているので、そちらをご参照ください。
まとめ
GitHub Actions についてまとめました。ほとんど本家のドキュメントをまとめただけなので、最新情報を確認するときはそちらを見てください。
個人的な所感としましては、CI/CD 用途として使うよりも、Webhook の用途で使うのが便利そうに感じました。これまでの CI/CD サービスですと push 以外のイベントに対しての処理を実行しづらく、かといって Webhook を利用するにはイベントを受け取るサーバーを立てるなり API Gateway を設定するなりしないといけなくて手間だったのですが、そういったところが GitHub で完結するようになるのはとても嬉しいことだと思います。
もちろん CI/CD 用途としても使えないことはないのでしょうが、その用途では現状だと機能的に少々物足りないところがありそうです。例えば、成果物やテスト結果といった Artifact の保存や、キャッシュ機能、通知、コンテナへの SSH のようなデバッグ機能などです。多少はアクションなどでどうにかできることではあるのですが、そこは素直に専門のサービスを使い、GitHub Actions はそれらでは埋められない部分で活用したり、サービス間の中心となるつなぎの役割を担ったりするのかなと思いました。
まだまだパブリックベータの段階なので、要望とかあれば本家にどんどんフィードバックしていくといいと思います。個人的には、すごい細かいところですが、.github
ディレクトリをシェルで補完しようとすると .git
ディレクトリとかぶってぐぬぬとなる場面が多かったので、どうにかなると嬉しいです。