この記事は、ソフトウェアテストの小ネタ Advent Calendar 2018 の4日目の記事です。

ソフトウェアテストの小ネタ Advent Calendar 2018
https://qiita.com/advent-calendar/2018/software-testing-koneta

GitHub Flowで開発を行っている場合、
一般的に、PullRequestがOpenの状態で、
変更したモジュールをどこかの環境にデプロイし、
テスト担当者がテストを実施する流れで開発する事になります。

このような開発の流れでは、
並行した複数のPullRequestが開発・テスト段階にある場合、
PullRequest毎に、デプロイ先を切り替えられると便利です。

このエントリでは、GitHubの機能とCircleCIを使って、
デプロイ先環境を切り替える運用方法について説明します。

# GitHub Flowについては以下を参照。
# GitHub Flow | Scott Chacon
# http://scottchacon.com/2011/08/31/github-flow.html

運用方法の説明

ここで提案するデプロイ先の切り替え方法は、PullRquestのラベル機能を使います。

例えば、dev1, dev2, dev3 のように3つ環境がある場合、
「env:dev1」「env:dev2」「env:dev3」というラベルを作成しておきます。

PullRequestに対して、このラベルのどれかを適用した時に、
CircleCIで、そのPullRequestが対応する環境にデプロイするように設定します。

このような仕組みを作っておくことで、
以下のようなテストプロセスを並行的に運用する事が出来るようになります。

  • 開発者: PullRequestを上げる
  • 開発者: 開発完了をテスト担当者に伝える
  • テスト担当者: テスト環境のラベルをつける
  • CircleCI: ラベルに対応した環境へデプロイする
  • テスト担当者: デプロイした環境でテストを実施する
  • 承認者: テストOKであればmasterにマージする

また、PullRequestのラベルで環境を切り替えるので、
デプロイ先切り替えのための暫定的なコード変更も不要です。

運用方法の整え方

それでは、前項で説明した運用を行うための環境を整えていきます。

パラメータでデプロイ先を切り替えるスクリプトを作成

パラメータでデプロイ先を切り替える事が出来るスクリプトを作成しておきます。

ここは利用しているframeworkなどに影響するので、
それらのframeworkなどにあわせて対応します。

例えば、Ruby on Railsでcapistranoを使っている場合、
bundle exec cap dev1 deploybundle exec cap dev2 deploy
といった形式でデプロイ先を切り替えるようになります。

GitHubアクセストークンの取得と設定

CircleCIからPullRequestにつけられたラベルを参照するために、
GitHubのアクセストークンを取得します。

GitHubの Settings → Developer settings → Personal access tokenとたどって、
「Generate new token」からtokenを作成します。

Personal access tokens | GitHub
https://github.com/settings/tokens

scopeは「repo」にチェックをつけておきます。

トークンを取得したらCircleCIに設定します。

該当プロジェクトのSettingsから、
BUILD SETTINGS → Environment Variables とたどって。
「Add Variable」から以下の設定を追加します。

  • Name: GITHUB_ACCESS_TOKEN
  • Value: ユーザ名:取得したアクセストークン の形式の文字列

ラベルを読むシェルスクリプトの作成

以下のようなラベルを読むシェルスクリプトを作成します。

.circleci/deploy_by_label.sh

#!/usr/bin/env bash

# GitHubAPIでラベル一覧を取得
REPO=$CIRCLE_PROJECT_USERNAME/$CIRCLE_PROJECT_REPONAME
PR_NUM=${CIRCLE_PR_NUMBER:-${CIRCLE_PULL_REQUEST##*/}}
REQUEST_URL=https://api.github.com/repos/$REPO/issues/$PR_NUM/labels
LABELS=$(curl -s -u $GITHUB_ACCESS_TOKEN $REQUEST_URL | jq -r '.[].name' | sort)

# env:開始のラベルがあれば出力
for l in $LABELS; do
  if [[ "$l" =~ ^env: ]]; then
    echo ${l:4:100}
    exit 0
  fi
done

CircleCIへの設定

作成したシェルスクリプトを使って、
config.ymlからラベルを読み取り、デプロイ用スクリプトに引き渡すように設定します。

.circleci/config.yml

version: 2
jobs:
  build:
    docker:
      - image: debian:stretch
    steps:
      - run:
          name: install curl jq
          command: apt-get update && apt-get install -y curl jq
      - checkout
      - run:
          name: deploy
          command: |
            DEPLOYMENT_ENV=`.circleci/deploy_by_label.sh`
            if [ "$DEPLOYMENT_ENV" == "" ]; then exit 0; fi
            echo $DEPLOYMENT_ENV            

最後の echo $DEPLOYMENT_ENV
という行はデプロイ用スクリプトに置き換えます。

先ほどのRails/Capistranoの例でいうと
bundle exec cap $DEPLOYMENT_ENV deploy という具合になります。

以上の変更をGitHubのPullRequestとして上げると、
付与したラベルに応じて、デプロイ先環境を切り替えられるようになります。

※このイメージではDEPLOYMENT_ENV変数をechoしているので「dev1」と表示されています。

また、ラベルを付け直して別環境にデプロイする場合は、
CircleCIの画面から「Rerun Workflow」を押せば、変更した先にデプロイする事が出来ます。

私がこの方法で環境切り替えを行っていたときは、
デプロイ先環境へのリンクをPullRequestにコメントさせていました。
また、AWSやGCPにデプロイしている場合は、
対応する環境のCloudWatchLogsやStackDriverのログへのリンクもコメントに記載しておくと捗ります。

この考え方を応用すると、
マイクロサービス間で依存しているAPIの環境を切り替える対応も可能でしょう。

世間にはいろいろとデプロイ管理ツール(Spinnakerとか)もありますが、
ここに紹介した単純な方法でも十分な場合も多いのではないかと考えています。


(2021.1.22追記)

GitHub Actionsを利用する場合は、以下のエントリも参考にして下さい。

GitHubのPullRequestでデプロイ先環境を切り替える運用方法 (GitHub Actions編) | takemikami.com https://takemikami.com/2021/01/22/GitHubPullRequest-GitHub-Actions.html