Kitsune Gadget

気になったことをつらつらと

Firebase利用プロジェクトを GitHub Actions でデプロイ自動化した


f:id:kitsune-gadget:20200804012756p:plain

自分のリポジトリGitHub Pages へのビルドを自動化してしまおうと思い立ち。GitHub Actions を使ってみました。 プロジェクトは Firebaseストレージを利用しているので認証データの扱いを考える必要がありました。

今回は CI(継続的インテグレーション)側のことはやらず、ビルド&デプロイ自動化のみで、込み入ったことはやっていません。

ビルド環境

  • Node.js 12.x
  • VueCLI 4.x

デプロイ先

ビルドする環境ですが、環境変数の扱いがフレームワークで異なる場合があるのでそれぞれ調べてみてください。 VueCLI の場合は環境変数VUE_APP_ という接頭辞をつけると vue.config 外のスクリプトからも呼び出すことができるようになっています。

Firebase Config の管理

まず初めに、Firebase Hosting 以外の場所に Firebase の機能を利用するページを扱う場合は、発行されている認証とURL群のConfigデータが必要となります。 このデータは Javascript 直書きも有りなためか公開前提となっており、漏れても問題は無いものですが、 Gitにおいては固有の認証情報になってしまうので不必要なものです。

Firebaseでは例外的な部分がありますが

すべてのサービスにおいて認証情報をリモートレポジトリにpushするのはとても危険です。

誤ってpushして公開してしまったら、大変なことになります。(個人ならまだしも、チームプロジェクトの場合は早急な対応をする必要がある。戻せない場合はレポジトリ削除も…) 基本的にスクリプトに直書きするのはやめましょう。

しっかりと確認して .gitignore に指定したjson等のファイルとして保管するか、ローカル環境変数を利用します。

今回は Github Actions を利用するために、ローカル環境変数としてconfigのデータを扱います。

Firebaseはあくまで例外的なので、認証情報管理で最初にやると良い練習になるかもしれないですね?

環境変数の読み込み

Firebaseのスクリプトindex.html にscriptタグで読み込む方法もありますが、npmやyarn経由でモジュール利用するほうが一般的だと思われます。 main.js などで環境変数を引っ張ってきて Firebase Config にあるそれぞれの認証情報をひとつずつ環境変数名で読み込みます。

.env.local
VUE_APP_FIREBASE_apiKey=xxxxxxxxx
VUE_APP_FIREBASE_authDomain=xxxxxxxxx
VUE_APP_FIREBASE_databaseURL=xxxxxxxxx
VUE_APP_FIREBASE_projectId=xxxxxxxxx
VUE_APP_FIREBASE_storageBucket=xxxxxxxxx
VUE_APP_FIREBASE_messagingSenderId=xxxxxxxxx
VUE_APP_FIREBASE_appId=xxxxxxxxx
VUE_APP_FIREBASE_measurementId=xxxxxxxxx
main.js
import firebase from 'firebase/app'

const firebaseConfig = 
{
  "apiKey": process.env.VUE_APP_FIREBASE_apiKey,
  "authDomain": process.env.VUE_APP_FIREBASE_authDomain,
  "databaseURL": process.env.VUE_APP_FIREBASE_databaseURL,
  "projectId": process.env.VUE_APP_FIREBASE_projectId,
  "storageBucket": process.env.VUE_APP_FIREBASE_storageBucket,
  "messagingSenderId": process.env.VUE_APP_FIREBASE_messagingSenderId,
  "appId": process.env.VUE_APP_FIREBASE_appId,
  "measurementId": process.env.VUE_APP_FIREBASE_measurementId
}

firebase.initializeApp(firebaseConfig);

認証をテストしてエラーが起きないことを確認します。

リポジトリの Secrets を設定する

プロジェクトをデプロイするリポジトリの Settings から Secrets を選択、New Secret から Actionsで利用できる外部公開させないシークレット環境変数をセットすることができます。 f:id:kitsune-gadget:20200803221155p:plain Firebase Config のデータをそれぞれSecretとして設定します。変数名はわかりやすいほうが良いですね。変数名はすべて大文字になるようです。最大100個まで追加することができます。

workflowファイルを作成する

肝の部分である workflow の YAML を書きます。

docs.github.com

新しい workflow を作るときには、テンプレート選ぶページになります。 スキップすると、 Simple workflow のテンプレートから始まるようです。

ここで YAML の書き方を詳しくは説明しません。

docs.github.com

Secrets に設定した環境変数を取り出します。 buildコマンドのあるstepで env以下にローカル環境で設定したものと同じ変数名をつけて読み込めるようにします。

- name: Use Node.js
      uses: actions/setup-node@v1
      with: 
        node-version: '12.x'

- name: build
      run: |
        npm install
        npm run build
      env: 
        VUE_APP_FIREBASE_apiKey: ${{ secrets.FIREBASE_APIKEY }}
        VUE_APP_FIREBASE_authDomain: ${{ secrets.FIREBASE_AUTHDOMAIN }}
        VUE_APP_FIREBASE_databaseURL: ${{ secrets.FIREBASE_DATABASEURL }}
        VUE_APP_FIREBASE_projectId: ${{ secrets.FIREBASE_PROJECTID }}
        VUE_APP_FIREBASE_storageBucket: ${{ secrets.FIREBASE_STORAGEBUCKET }}
        VUE_APP_FIREBASE_messagingSenderId: ${{ secrets.FIREBASE_MESSAGINGSENDERID }}
        VUE_APP_FIREBASE_appId: ${{ secrets.FIREBASE_APPID }}
        VUE_APP_FIREBASE_measurementId: ${{ secrets.FIREBASE_MEASUREMENTID }}

上記ドキュメントの警告にもあるように、シークレット値を確認の為にconsole.logやechoなどでくれぐれも表示しないようにしてださい。(ログ出力されてしまったシークレットは自動的に表示されないように削除されるようですが。)

Personal Access Token の利用

docs.github.com

リポジトリの権限にアクセスするために GITHUB_TOKEN を利用できます。 リポジトリ固有の権限でこのトークンを利用して、リポジトリの内容を読み書きできます。 ただし、今回はトップのGitHub Pagesにデプロイしたいので、自分の他のリポジトリにpushさせる必要があります。 workflow内のトークンでは、実行中のリポジトリの情報しか取得できません。

docs.github.com

そこで、GitHubアカウントの設定から発行できるPAT(Personal Access Token)を利用します。 PATを発行する前にトークンが許可できる権限を選択します。今回はリポジトリの基本操作だけなので Repo の欄だけをチェックして発行します。 f:id:kitsune-gadget:20200804005116p:plain 発行したトークンは、発行時の最初のみしか表示されません。 ページを離れると二度と表示できないので、ここでコピペして先程と同様に開発リポジトリのSecretsにセットします。 変数名はGH_PATとしました。 初めはGITHUB_という接頭辞にしてしまったのですが、この接頭辞はおそらく既に予約されてるものなのでSecretsでは利用できません。

このPATではSSHは利用できず、HTTPS接続のみになります。 gitでコマンドを実行してからトークンをあとからパスワードとして入力することで認証できますが、Basic認証をベースとした認証方式であるため、リポジトリURLの前に@を使ってPATを挿入することでそのフェーズを短縮できます。

git push 'https://${{ secrets.GH_PAT }}@github.com/foo/bar.git' master

テストページへのデプロイ

実際に行ったのは GitHub Pages へのデプロイですが、 今回は検証用に開発用ブランチが更新されるとテストページへデプロイされるようにも作りました。

簡単にNetlifyで見れるようにしたかったので、適当なレポジトリを作成しそこにプッシュさせます。

on:
  push:
    branches: [ develop ]
  pull_request:
    branches: [ develop ]

jobs:
  build:
    runs-on: ubuntu-latest

    # 省略...
    
    steps:
      - name: Git Push
        if: success()
        run: |
          cd dist
          git config --global user.name 'kitsunegadgetCD'
          git config --global user.email 'kitsunegadget@example.com'
        
          git init
          git add -A
          git commit -m 'devCD deploy'
          echo "success git commit"
        
          git push -f 'https://${{ secrets.GH_PAT }}@github.com/foo/testpage.git' master
          cd -

Netlifyからは、そのリポジトリのルートをdistにし、buildコマンド無しにするとそのまま公開できます。

これでテスト環境でページをみることができます。ここでFirebaseの認証に問題がある場合もテストページで検証できるようになります。

運用ページへのデプロイ

masterリポジトリが更新されるとデプロイされるようにもう一つのworkflowも作成します。 ただしpush先は公開するページのリポジトリです。 テストと同じようにすればOK

これでGitHub Pagesへの自動デプロイを実装することができました!

まとめ

Secretの機能があるおかげで、認証が必要なプロジェクトでもActionsを利用して自動化することができました。 ローカルテストが成功したらメインにpushして自動で運用ページが更新されるのは 今までビルドを見届けてたのが無くなって悲しくもなりそうですが、 そのちょっとした時間を別のことに使えます。

エラーが起きた場合、登録されているメールに通知が来るので、デプロイの失敗はすぐにわかります。 workflow内で別のデバイス(SlackやIoT機器)などにビルド結果を通知できれば面白くかつ確認性がより上がりそうです。

YAML構文なので基本的な書き方は同じだと思いますが、あまり書いたことが無いので、workflowの理解に少し時間がかかりました。stepsの配列はそれぞれの作業に名前をつけてログを見やすくし、ifステートメントでは前作業が成功したかで次作業を実行するかの分岐も扱えます。

workflowには豊富な"アクション"があり、今回はsetup-nodeのアクションを利用しています。 これらのアクションはDockerコンテナのパッケージかJavascriptコードです。

git push においてもアクションが既にありますが、コマンドを順に書いたほうがわかりやすいと思って利用はしませんでした。gitはビルトインで利用できるようですので。

リポジトリの認証については様々な方法がありますが、今回はGitHubの操作だけで完結できるものを選択しました。

GitHub Actionsのworkflowは環境に対応したテンプレートが多くあるので、様々なデプロイ先で利用できそうです。

参考

git - Push to origin from GitHub action - Stack Overflow

Push built files to repo using Actions - GitHub Actions - GitHub Support Community