Cypress × Firebase × Angular の組み合わせのE2EをGitHub Actionsで実行する
前回はCypress × Firebase × Angularの組み合わせのアプリケーションのe2eをLocalで回す方法を説明しました。今回はで開発環境でe2eを実行し、通った場合に、staging環境にデプロイするGitHub Actionsのパイプラインを構築します。 またモノレポ想定でアプリケーションのsourceも、e2eのsourceも同じプロジェクト内にいる構成になっています。 今回のコードはここにおいています。
開発・staging 環境の準備
今回はfirebase hostingを使ってアプリケーションを公開します。環境の切り替えはfirebaseのプロジェクトを複数作ることで実現します。つまり開発環境用プロジェクト、staging環境用プロジェクトを作ってしまって、そこにアプリケーションをデプロイしe2eを回します。なのでプロジェクトを作成していない方はfirebaseのプロジェクトを作成し、firestoreとhostingを実行できるようにしておいてください。
Angularのアプリケーションなので、環境の切り替えはファイルで行うことになります。こちらのサイトを参考にしながらangular.json
に設定を追加することで様々な環境設定ファイルを切り替えられるようになります。今回はdevとstagingを用意しています。
ファイルで環境を切り替えられるのですがfirebaseのシークレットの情報はrepositoryにpushすべきではないので、pipelinde実行するには少しトリッキーな方法を使う必要があります。このあたりは後ほど解説します。
パイプラインの流れ
大きな流れは以下のようになっています。
開発環境にデプロイ
まずは開発環境にデプロイする方法について解説します。開発環境にデプロイする用のymlは以下のようになります。
// $PROJECT_ROOT/.github/workflows/pipeline.yml name: new-app-e2e-pipeline on: [push] jobs: deploy-to-dev-for-e2e: runs-on: ubuntu-20.04 steps: - name: Checkout Repo uses: actions/checkout@v2 - uses: actions/setup-node@v1 - run: npm install -g firebase-tools - name: Decrypt secret run: ./decrypt_secret.sh environment.dev.ts working-directory: ./.github/scripts env: LARGE_SECRET_PASSPHRASE: ${{ secrets.LARGE_SECRET_PASSPHRASE }} - name: Move secret run: mv $HOME/secrets/environment.dev.ts ./new-app/src/environments - name: Build and Deploy to dev run: ./build_and_deploy.sh dev shell: bash working-directory: ./enviroments/firesbase env: FIREBASE_TOKEN: ${{ secrets.SERVICE_ACCOUNT }
はじめの部分ではpushがトリガーであることやfirebase-toolsのinstallをしています。
Decrypt secret
、Move secret
部分は少しややこしいので解説をします。Angularは基本的に環境変数を受け取れない (僕が知る限り) ので、環境ごとの設定の切り替えはファイルで行います。しかし、GitHub Actionsでは安全にファイルをアップロードする仕組みは特にありません。なのでenv ファイルを暗号化してpushを行い、パイプライン上で複合化する必要があります。
暗号化したいファイルはgpgおよびAES256暗号アルゴリズム使って以下のように行います。
gpg --symmetric --cipher-algo AES256 my_secret.json
この時聞かれるパスフレーズは覚えておいてください。
複合化は以下のdecrypt_secret.sh
を使っています。
// $PROJECT_ROOT/.github/scripts/decrypt_secret.sh #!/bin/sh FILE_NAME=$1 # Decrypt the file mkdir $HOME/secrets # --batch to prevent interactive command # --yes to assume "yes" for questions gpg --quiet --batch --yes --decrypt --passphrase="$LARGE_SECRET_PASSPHRASE" \ --output $HOME/secrets/$FILE_NAME "${FILE_NAME}.gpg"
$LARGE_SECRET_PASSPHRASE
は暗号化する際に聞かれたフレーズを指します。これを予めGitHubのsecretsとして登録しておくことで、環境変数として取り出すことができます。登録方法や詳しい、このあたりの流れはこちらを参考にしてください。
Move secret
は複合化されたenv fileをangularのenvironmentsディレクトリに移動させています。
ちなみにちょくちょく見受けられるworking-directory:~
はコマンドが実行されるディレクトリ を指定します。
Build and Deploy to dev
では予め作成したAngularのアプリをビルドして、firebaseのプロジェクトにデプロイしてくれるシェルを叩いています。
$PROJECT_ROOT/environments/firebase
でfirebase initを行い、firebaseの設定ファイル生成させてbuild、deployを行うシェルをおいています。その中では$PROJECT_ROOT/environments/new-app
にあるbuild用のシェルを実行するようにしています。
deployを行うシェルではfirebase deploy
を実行しているのですが、その際にFIREBASE_TOKEN
という環境変数が必要となります。これはlocalでfirebase login:ci
を叩き、ログインをすると返却されるトークンで、これをsecretとして登録する必要があります。(pipeline.ymlのFIREBASE_TOKEN: ${{ secrets.SERVICE_ACCOUNT }
の部分)
またfirebase use --add
で各環境にdev、stagingというaliasを貼っています。
aliasについては詳しくはこちらをご覧ください。
// $PROJECT_ROOT/environments/firebase/build_and_deploy.sh ## !/usr/bin/env bash set -e env=$1 env=${env:-"dev"} src=sources cwd=$(realpath $(dirname $0)) project_root=$(git rev-parse --show-toplevel) cd ../tuning-front ./build.sh "${env}" cd ${cwd} echo "deploy to ${env}" firebase deploy --project ${env} rm -rf "${cwd}/public/"
// $PROJECT_ROOT/enviroments/new-app/build.sh ## !/usr/bin/env bash set -e env=$1 env=${env:-"dev"} echo "build ${env} mode" src=sources cwd=$(realpath $(dirname $0)) project_root=$(git rev-parse --show-toplevel) rm -rf "${cwd}/${src}" echo "copy project.." cp -R "${project_root}/new-app" "${cwd}/${src}" cd "${cwd}/${src}" rm -rf dist/ ls src/environments echo "build project.." npm install -g @angular/cli@11.0.1 npm install ng build -c "${env}" echo "copy output to firebase public.." cp -R "${cwd}/${src}/dist/new-app/" "${project_root}/enviroments/firesbase/public"/ rm -rf "${cwd}/${src}"
e2eの実行
e2e実行用のymlは以下のようになります。
execute-e2e: runs-on: ubuntu-20.04 needs: deploy-to-dev-for-e2e steps: - uses: actions/checkout@master - name: Decrypt secret run: ./decrypt_secret.sh serviceAccount.json working-directory: ./.github/scripts env: LARGE_SECRET_PASSPHRASE: ${{ secrets.LARGE_SECRET_PASSPHRASE }} - name: Move secret run: mv $HOME/secrets/serviceAccount.json ./e2e - name: Create videos and screenshots directory working-directory: ./e2e/cypress run: | mkdir videos && \ mkdir screenshots - name: Cypress Run uses: cypress-io/github-action@v2 with: working-directory: e2e start: npm run cypress:run wait-on: https://new-app-dev-edb1d.web.app browser: chrome record: true env: CYPRESS_RECORD_KEY: ${{ secrets.CYPRESS_RECORD_KEY }} FIREBASE_TOKEN: ${{ secrets.SERVICE_ACCOUNT }} GITHUB_HEAD_REF: ${{ github.head_ref }} GITHUB_REF: ${{ github.ref }} - uses: actions/upload-artifact@v1 if: failure() with: name: cypress-screenshots path: e2e/cypress/screenshots - uses: actions/upload-artifact@v1 if: failure() with: name: cypress-videos path: e2e/cypress/videos
基本的にはcypresの公式を参考にしています。
needs: deploy-to-dev-for-e2e
は前のジョブが終わってから行うという意味の記述です。これがないと並列で動いてしまいます。
Decrypt secret
、Move secret
では予め暗号化した、serciceAccount.json
を複合化し、所定のディレクトリに移動させています。このファイルはcypressがfirestoreなどを触るために必要な認証情報です。
Create videos and screenshots directory
ではcypressがテスト時にscreenshotやvideoを記録するためのディレクトリ を作成しています。(公式では記述を見つけることができなかった、これがないとe2eがコケるので作成しました。)
Cypress Run
ではテストを実際に実行しています。 npm run cypress:run
でテストが実行できるよう、e2eディレクトリのpackage.json
に以下のように記述しています。
"cypress:run": "cross-env cypress run"
その後の部分はテストが失敗した際にvideosとscreenshotsをアップロードする設定をしています。
stagingへのデプロイ
以下がstaginへのリリース部分です。
deploy-to-staging: runs-on: ubuntu-20.04 needs: execute-e2e steps: - name: Checkout Repo uses: actions/checkout@v2 - uses: actions/setup-node@v1 - run: npm install -g firebase-tools - name: Decrypt secret run: ./decrypt_secret.sh environment.staging.ts working-directory: ./.github/scripts env: LARGE_SECRET_PASSPHRASE: ${{ secrets.LARGE_SECRET_PASSPHRASE }} - name: Move secret run: mv $HOME/secrets/environment.staging.ts ./tuning-front/src/environments - name: Build and Deploy to staging run: ./build_and_deploy.sh staging shell: bash working-directory: ./enviroments/firesbase env: FIREBASE_TOKEN: ${{ secrets.SERVICE_ACCOUNT }}
needs: execute-e2e
により、execut-e2eが終わった後に実行されます。またe2eが失敗するとこのジョブは実行されません。
まとめと感想
Cypress × Firebase × Angular の組み合わせのE2EをGitHub Actionsで実行する方法を解説しました。改善点として、ファイルを暗号化してアップロードしている部分をできれば環境変数でなんとかでしたいなぁという感じです。もしいい方法あれば是非お願いします!