[GitHub Actions] ブランチごとにジョブの実行を制御できる Environments を試してみた

2023.06.14

こんにちは、CX事業本部 Delivery部の若槻です。

今回は、GitHub Actions でブランチごとにジョブの実行を制御できる Environments を試してみました。

Environments とは

Environments とは、リポジトリごとに作成した Environment に対して、デプロイメントの保護規則や variable、secret を設定できる GitHub Actions の機能です。

Environments で提供されている機能は大きく分けて次の7つとなります。

  • Deployment protection rules(デプロイ保護規則)
    • Required reviewers(必須のレビュー担当者)
    • Wait timer(待機タイマー)
    • Deployment branches(デプロイメントブランチ)
    • Allow administrators to bypass configured protection rules(構成された保護規則を管理者がバイパスすることを許可する)
    • Custom deployment protection rules(カスタム デプロイ保護規則)
  • Environment secrets(環境シークレット)
  • Environment variables(環境変数)

各機能ごとに必要なプランは以下のようになります。パブリックリポジトリでは全ての機能が使用できますが、プライベートリポジトリではプランによって使用できる機能が異なります。

機能 プライベートリポジトリ パブリックリポジトリ
Required reviewers GitHub Enterprise Free 以上
Wait timer GitHub Enterprise Free 以上
Deployment branches GitHub Pro または GitHub Team 以上 Free 以上
Allow administrators to bypass configured protection rules GitHub Enterprise Free 以上
Custom deployment protection rules GitHub Enterprise Free 以上
Environment secrets GitHub Pro または GitHub Team 以上 Free 以上
Environment variables GitHub Pro または GitHub Team 以上 Free 以上

今回は、GitHub Pro または GitHub Team 以上のプランで利用できる次の3機能を試してみます。これらの機能により、Environment ごとに異なるブランチを対応付けて、ブランチごとに実行させるジョブや、参照させる secrets および variables を制御することができます。

  • Deployment branches
  • Environment secrets
  • Environment variables

試してみた

GitHub Team のアカウントで試していきます。

Environment の作成

リポジトリの Settings > Environments より Environments のメニューを開きます。(必要なプランでない場合はメニューは表示されません)

New environment をクリックします。

Environment の名前を指定し、Configure environment をクリックして Environment を作成します。

作成できました。このメニューで Environment の設定を行います。

Deployment branches の設定

まず Deployment branches を設定します。今回は Selected branches を選択します。

Deployment branchesAdd deployment branch rule をクリックします。

ルールを適用するブランチのパターンを指定します。ここでは develop を指定し、develop というブランチでのみこの Environment が使われるようにします。

Deployment branches でルールを追加できました。

Environment secrets の設定

Environment secrets の値は作成後に暗号化され、GitHub Actions のコンテキストからのみアクセスできるようになります。APIキーなどの機密データを保存するのに適しています。

Environment secretsAdd secret をクリックします。

secret の名前および値を指定して、Add secret をクリックします。

HOGE_SECRET という名前で secret を追加できました。

Environment variables の設定

Environment variables の値は作成後にもメニューから参照することができ、センシティブでないデータを保存するのに適しています。

Environment variablesAdd variable をクリックします。

variable の名前および値を指定して、Add variable をクリックします。

HOGE_VARIABLE という名前で variable を追加できました。

Environment をさらに追加

さらに、staging および main ブランチに対応した Environment を追加します。

これで developmentstaging および main の3つのブランチに対応した Environment が作成できました。

動作確認

ワークフローでは、jobs.<job_id>.environment に Environment の名前を指定することで、その Environment のルールの適用や、Environment secrets および Environment variables の参照が可能になります。

今回は次のようなワークフローを作成しました。dev_deploymentstg_deployment および prod_deployment のジョブは、integration ジョブ後に並行して実行されるようにしています。

.github/workflows/cicd.yml

on:
  push:
    branches:
      - develop
      - staging
      - main

jobs: 
  integration:
    runs-on: ubuntu-latest
    steps:
      - name: integration
        run: |
          echo "Integration"
  dev_deployment:
    runs-on: ubuntu-latest
    if: github.ref_name == 'develop'
    environment: development # Environment の名前を指定
    needs: integration
    steps:
      - name: deploy
        run: |
          echo "Development Deployment"
          echo ${{ vars.HOGE_ENV }}
          echo ${{ secrets.HOGE_SECRET }}
  stg_deployment:
    runs-on: ubuntu-latest
    if: github.ref_name == 'staging'
    environment: staging # Environment の名前を指定
    needs: integration
    steps:
      - name: deploy
        run: |
          echo "Staging Deployment"
          echo ${{ vars.HOGE_ENV }}
          echo ${{ secrets.HOGE_SECRET }}
  prod_deployment:
    runs-on: ubuntu-latest
    if: github.ref_name == 'main'
    environment: production # Environment の名前を指定
    needs: integration
    steps:
      - name: deploy
        run: |
          echo "Production Deployment"
          echo ${{ vars.HOGE_ENV }}
          echo ${{ secrets.HOGE_SECRET }}

Pull Request のマージなどによる main ブランチへの push が行われると、Deployment branche で main を指定している production Environment のジョブのみ実行されました。

ジョブの実行内容を見ると、main ブランチに対応した Environment の variable および secret が参照されていることが分かります。

また、Environment ごとの最後に実行された Deployment のステータスがリポジトリトップページに表示されます。

アクセスすると Deployment の履歴が確認できます。これは GitHub Actions とは異なる UI となっています。

Environment ごとの履歴も確認できます。

ポイントおよび注意点

Environments を使うにあたりいくつかポイントや注意点がありました。

ref による実行条件により、ブランチルールを満たさないジョブを実行させないようにする必要がある

前述のワークフローでは、if: github.ref_name == 'branch_name' という条件を使用することで、ブランチルールを満たさないジョブを実行させないようにしています。

検証として、次のように実行条件を指定しないワークフローを試してみます。

.github/workflows/cicd.yml

on:
  push:
    branches:
      - develop
      - staging
      - main

jobs: 
  integration:
    runs-on: ubuntu-latest
    steps:
      - name: integration
        run: |
          echo "Integration"
  dev_deployment:
    runs-on: ubuntu-latest
    # if: github.ref_name == 'develop'
    environment: development
    needs: integration
    steps:
      - name: deploy
        run: |
          echo "Development Deployment"
          echo ${{ vars.HOGE_ENV }}
          echo ${{ secrets.HOGE_SECRET }}
  stg_deployment:
    runs-on: ubuntu-latest
    # if: github.ref_name == 'staging'
    environment: staging
    needs: integration
    steps:
      - name: deploy
        run: |
          echo "Staging Deployment"
          echo ${{ vars.HOGE_ENV }}
          echo ${{ secrets.HOGE_SECRET }}
  prod_deployment:
    runs-on: ubuntu-latest
    # if: github.ref_name == 'main'
    environment: production
    needs: integration
    steps:
      - name: deploy
        run: |
          echo "Production Deployment"
          echo ${{ vars.HOGE_ENV }}
          echo ${{ secrets.HOGE_SECRET }}

main ブランチへの push を行うと、main ブランチに対応したジョブの実行は成功しましたが、 develop および staging Environment に対応したジョブは失敗しています。

Branch "main" is not allowed to deploy to development due to environment protection rules. The deployment was rejected or didn't satisfy other protection rules.

指定のブランチへの push 時に行いたいジョブ実行は行われますが、ワークフローの実行自体は失敗してしまうため、ref による実行条件により ブランチルールを満たさない Environment のジョブを実行させないようにする必要がありました。

レビューによりワークフローを進めるためには GitHub Enterprise が必要

本来であれば develop -> staging -> main という順番で、前段の Environment へのデプロイ後に、人間によるレビューを経て次の Environment へのデプロイを行いたいところですが、承認によりワークフローを進めるには GitHub Enterprise 以上で使える「Required reviewers」機能が必要となります。

レビューを想定した場合のワークフローとしては次のようになります。stg_deployment および prod_deployment で前段のデプロイのジョブを指定します。

.github/workflows/cicd.yml

on:
  push:
    branches:
      - develop
      - staging
      - main

jobs: 
  integration:
    runs-on: ubuntu-latest
    steps:
      - name: integration
        run: |
          echo "Integration"
  dev_deployment:
    runs-on: ubuntu-latest
    environment: development
    needs: integration
    steps:
      - name: deploy
        run: |
          echo "Development Deployment"
          echo ${{ vars.HOGE_ENV }}
          echo ${{ secrets.HOGE_SECRET }}
  stg_deployment:
    runs-on: ubuntu-latest
    environment: staging
    needs: dev_deployment # 前段のデプロイのジョブを指定
    steps:
      - name: deploy
        run: |
          echo "Staging Deployment"
          echo ${{ vars.HOGE_ENV }}
          echo ${{ secrets.HOGE_SECRET }}
  prod_deployment:
    runs-on: ubuntu-latest
    environment: production
    needs: stg_deployment # 前段のデプロイのジョブを指定
    steps:
      - name: deploy
        run: |
          echo "Production Deployment"
          echo ${{ vars.HOGE_ENV }}
          echo ${{ secrets.HOGE_SECRET }}

するとワークフローは一直線となりますが、レビューの要求を設定していないブランチルールだと、ブランチパターンにマッチせず、ジョブが失敗します。

よって GitHub Enterprise でないアカウントの場合は、上述の直列ではなく、最初に試したような並行な形式のワークフローを組む必要があります。

pull_request トリガーの場合は、ヘッドブランチ(マージ元)が Environment で使われる

次のように pull_request トリガーのワークフローの場合、ヘッドブランチ(マージ元)が Environment で使われる挙動となりました。(つまり github.base_ref ではなく github.head_ref が使われる)

.github/workflows/cicd.yml

on:
  pull_request:
    types:
      - closed

jobs: 
  integration:
    runs-on: ubuntu-latest
    steps:
      - name: integration
        run: |
          echo "Integration"
  dev_deployment:
    runs-on: ubuntu-latest
    if: github.base_ref == 'develop'
    environment: development
    needs: integration
    steps:
      - name: deploy
        run: |
          echo "Development Deployment"
          echo ${{ vars.HOGE_ENV }}
          echo ${{ secrets.HOGE_SECRET }}
  stg_deployment:
    runs-on: ubuntu-latest
    if: github.base_ref == 'staging'
    environment: staging
    needs: integration
    steps:
      - name: deploy
        run: |
          echo "Staging Deployment"
          echo ${{ vars.HOGE_ENV }}
          echo ${{ secrets.HOGE_SECRET }}
  prod_deployment:
    runs-on: ubuntu-latest
    if: github.base_ref == 'main'
    environment: production
    needs: integration
    steps:
      - name: deploy
        run: |
          echo "Production Deployment"
          echo ${{ vars.HOGE_ENV }}
          echo ${{ secrets.HOGE_SECRET }}

feature ブランチから develop ブランチへ Pull Request のマージをしてみます。

すると、develop ブランチに対応した Environment のジョブが実行されますが、失敗してしまいます。

エラーメッセージによるとヘッドブランチ(マージ元)が Environment で使われているため、ブランチルールにマッチせず、ジョブが失敗してしまっています。

Branch "cm-rwakatsuki-patch-1" is not allowed to deploy to development due to environment protection rules.

同じ Pull Request のマージによるワークフロー実行でも、push と pull_request では使用されるブランチが異なるため、注意が必要です。

おわりに

GitHub Actions でブランチごとにジョブの実行を制御できる Environments を試してみました。

Pull Request マージなどが行われたブランチごとに異なる Environment に対応したジョブを実行させたり、secret および variable を参照させたりすることができることが確認できました。今回は GitHub Team の環境で試したましたが、GitHub Enterprise を使用すれば更に柔軟なワークフローを組むこともできます。

ただしブランチごとにジョブの実行内容を、参照させる secret や variable 以外に変える必要が無い場合は、ジョブの記述が重複して冗長となるため、次のようなブランチごとに参照先を変更する方法をとっても良さそうです。

参考

以上