GitHub Actions の if と env
GitHub Actions に関して、どこに env が書けるんですか?とか、if ってどうやって使うんですか?とか色々と質問を受けることがあります。
そういった、if と env に関してフォーカスして、こちらの記事で紹介させていただきます。
env
まず、env に関してです。
env が使える場所
env の対象範囲は、step 単位です。
env が指定されていると、環境変数として、step 毎に展開されます。
steps:
- name: "Say Hello, world!"
run: echo "$HELLO, world!"
env:
HELLO: Hello
では例えば、全ての step にて同じ環境変数を使用したい時は、以下のように job 単位でも同一の環境変数を定義できます。
echo_job:
runs-on: ubuntu-latest
env:
HELLO: Hello
steps:
- name: "Say Hello, world!"
run: echo "$HELLO, world!"
- name: "Say Hello, foo!"
run: echo "$HELLO, foo!"
- name: "Say Hello, bar!"
run: echo "$HELLO, bar!"
さらに、job を跨ぐ環境変数も定義できます。
env:
HELLO: Hello
jobs:
echo_job:
runs-on: ubuntu-latest
steps:
- name: "Say Hello, world!"
run: echo "$HELLO, world!"
hello_job:
runs-on: ubuntu-latest
steps:
- name: "Say Hello, world!"
run: echo "$HELLO, world!"
ファイル全体での環境変数定義は非常に便利ですが、1 つの step でしか使用しない環境変数をそのように定義してしまうのは良くないので避ける方が望ましいです。
あくまで、対象範囲を広げざるを得なくなった時に、job 単位やファイル単位に広げるようにしましょう。
job 内で環境変数をセットする
job の実行結果に応じて、変数が出力され、それを後から環境変数として使用したい時があります。
その時は、GITHUB_ENV に対して、key=value の形式で追記すると、その後でも環境変数としてアクセスできます。
steps:
- name: "Output docker image url"
run: echo 'DOCKER_IMAGE_URL=gcr.io/cloudrun/hello' >> $GITHUB_ENV
- name: "Show the docker image url"
run: echo "$DOCKER_IMAGE_URL"
env を contexts として使用する
${{ github.sha }} でそのコミットハッシュが取れたりするのですが、その github.sha の部分が contexts にあたります。
例えば以下のケースを考えてみます。
steps:
- name: "Output docker image url"
run: echo 'DOCKER_IMAGE_URL=gcr.io/cloudrun/hello' >> $GITHUB_ENV
- uses: 'google-github-actions/deploy-cloudrun@v0'
with:
service: 'hello-cloud-run'
image: '' # 環境変数が使えない?
この時、with として、環境変数を渡したいわけです。
例えば以下のようにしても、そのままの内容が入ってしまうため、エラーになってしまいます。
image: '$DOCKER_IMAGE_URL'
そこで、contexts を使用すると、簡単に中身を展開できます。
image: '${{ env.DOCKER_IMAGE_URL }}'
contexts はどんなところでも展開してくれるので、run の中に書いても大丈夫です。
steps:
- name: "Show the docker image url"
run: echo '${{ env.DOCKER_IMAGE_URL }}'
違いは、bash が実行時に中身を展開するか、GitHub Actions が実行前に展開するかの違いです。
if
次は if に関してです。
if が使える場所
if は env と違って、ファイル単位では指定できません。
指定できるのは、以下のように job 単位と step 単位のみです。
jobs:
assign:
if: github.actor != 'dependabot[bot]'
jobs:
assign:
runs-on: ubuntu-latest
steps:
- name: Notify actor
if: github.actor != 'dependabot[bot]'
run: bash ./notify
if はカッコを省略できる
if における ${{ }} は、expression syntax と言うみたいですが、それは省略できます。
例えば、dependabot がワークフローを実行したユーザーではない時に、job を実行したいときには、以下のようにできます。
jobs:
assign:
if: github.actor != 'dependabot[bot]'
省略できるだけなので、以下のように書いても問題ありません。
jobs:
assign:
if: ${{ github.actor != 'dependabot[bot]' }}
if と env を組み合わせる
if と env を組み合わせてみましょう。
以下のように使用できます。
steps:
- name: "Output docker image url"
run: echo 'DOCKER_IMAGE_URL=gcr.io/cloudrun/hello' >> $GITHUB_ENV
- uses: 'google-github-actions/deploy-cloudrun@v0'
if: startsWith(env.DOCKER_IMAGE_URL, 'gcr.io')
with:
service: 'hello-cloud-run'
image: '${{ env.DOCKER_IMAGE_URL }}'
if & env & startsWith & !
色々と組み合わせてみましょう。
- uses: 'google-github-actions/deploy-cloudrun@v0'
if: ${{ !startsWith(env.DOCKER_IMAGE_URL, 'gcr.io') }}
with:
service: 'hello-cloud-run'
image: '${{ env.DOCKER_IMAGE_URL }}'
! (否定演算子) と合わせて使用する時は、上記のように、expression syntax が必須になります。
こういった話は、以下に書かれています。
最後に
色々と細かいところで、迷うところがあると思います。
特に expression に関しては、syntax が少し特殊なので、慣れるまで少し時間がかかる印象です。
また、以下の記事で紹介させていただいた actionlint を使用すれば、事前にそういった部分もチェックができます。
その場合は、GitHub Actions としてではなく、ローカルで actionlint を実行すると push する前にミスを発見できるので安心です。
こちらの記事が参考になれば幸いです。
Discussion