はじめに
GitHub Actionsを使ってCI/CD環境を構築します。
今回の目標はFirebaseにGatsbyで作った静的なWebサイトを自動的にビルド、デプロイする環境の構築です。
GitHub Actionsとは
GitHubのリポジトリにCI(継続的インテグレーション)とCD(継続的デプロイメント)の機能を直接作ることができる機能。
CIについては公式ドキュメントで丁寧に解説されています。
継続的インテグレーションについて
GitHub Actionsのことば
では早速CI/CD環境を構築していきたいのですが、私はCI/CDツールに関するリテラシーがまだまだ低いので、GitHub Actionsで登場する概念を学びつつ進めましょう。
ワークフローとは
GitHub Actionsで言うワークフローとは、任意のプロジェクトをビルドしたり、テストしたり、デプロイしたり…という様々な作業のまとまりです。
実際にはプロジェクトのルートに.github/workflows
というディレクトリを作り、その中に*.yml
ファイルを作り、このファイルに具体的な処理を記述します。*.yml
ファイルが1つのワークフローとして定義され、プロジェクトで複数のワークフローを定義できます。
ジョブとは
ワークフローの中で実行される処理のひとまとまりをジョブと呼んでいます。ジョブはワークフローに複数定義することができ、それぞれのジョブは並行して処理されます。
また、ジョブはそれを実行するマシンを指定できます。(例えばMacとかWindowsとか)
ステップとは
ジョブの中で実行される一連のタスク。ワークフローを構成する様々なタスクの最小単位という感じ。
npm install
を実行する、npm run build
を実行する、くらい具体的なタスクです。
アクションとは
ステップとして実行されるタスクの中で再利用可能なコードの単位をアクションと呼びます。
例えば『Firbaseにデプロイする』という一連のタスクを再利用可能な形にしているもののことです。
なんとなくGitHub Actionsの概念を掴んできたところで、次は実際のワークフローを定義している*.yml
ファイルを見てみます。結局は見たほうがわかりますしね。
GitHub Action for Firebaseを使ってみる
GitHubのMarketplaceには様々なアクションが用意されています。今回はその中からGitHub Action for Firebaseを使ってワークフローを作ります。
ちなみにこのときのプロジェクトはこんな構成です。
root
├── .github
│ └── workflows
│ └── firebase.yml
├── node_modules
├── public
│ └── ビルドされたファイルはここにできる
├── src
│ └── ソースファイルたくさん
├── .firebaserc
├── .gitignore
├── package.json
├── yarn.lock
├── その他諸々
このプロジェクトに、GitHub Action for FirebaseのExampleを参考にワークフローを作成します。
↓これはExampleを私の環境で使うために少し手を加えたワークフローです。
name: Build and Deploy
on:
push:
branches:
- master
jobs:
build:
name: Build
runs-on: ubuntu-latest
steps:
- name: Checkout Repo
uses: actions/checkout@master
- name: Install Dependencies
run: npm install
- name: Build
run: npm run build
- name: Archive Production Artifact
uses: actions/upload-artifact@master
with:
name: public
path: public
deploy:
name: Deploy
needs: build
runs-on: ubuntu-latest
steps:
- name: Checkout Repo
uses: actions/checkout@master
- name: Download Artifact
uses: actions/download-artifact@master
with:
name: public
path: public
- name: Deploy to Firebase
uses: w9jds/firebase-action@master
with:
args: deploy
env:
FIREBASE_TOKEN: ${{ secrets.FIREBASE_TOKEN }}
私は今回CI/CDツール初体験ですが、こうしてコードで見るとこのワークフローが何をしようとしているのか、なんとなくわかりますね。
それではこのワークフローをちゃんと理解するため、順番にコードを読んでいきましょう。
name: Build and Deploy
on:
push:
branches:
- master
# 以下略
この部分はワークフローの名前と、このワークフローをトリガーするイベントが記述されています。
今回の例だと、master
ブランチに新しいコードがプッシュされた時にトリガーされます。なんてわかりやすいんでしょう。
続いてBuildジョブの部分を読んでみます。
Buildジョブ
jobs:
build:
name: Build
runs-on: ubuntu-latest
steps:
- name: Checkout Repo
uses: actions/checkout@master
- name: Install Dependencies
run: npm install
- name: Build
run: npm run build
- name: Archive Production Artifact
uses: actions/upload-artifact@master
with:
name: public
path: public
Buildジョブがどのように定義されているか順に読んでいきます。
jobs.build
build
はこのジョブを一意に特定するためのIDです。英数字、-
、_
のみ使えます。
jobs.build.name
このジョブの名前です。読んだまんまですね。たぶんこの値はそんなに重要じゃないのかなーと思います。
jobs.build.runs-on
このジョブを実行するマシンを定義します。
ここでMacやWindowsも選べますが、今回はUbuntuの最新版にしています。これも読んだまんまという感じです。
jobs.build.steps.name
ここからステップを定義していきます。まずは名前です。はい、読んだまんまですね。
jobs.build.steps.uses
ここでアクションを使うことができます。
uses: actions/checkout@master
というのは、GitHubのactions/checkout
というパブリックリポジトリのアクションのmaster
ブランチのコードを使わせていただく、という意味です。
このアクションでは、チェックアウトするリポジトリや、Gitの参照を指定できるのですが、今回は何も指定していません。デフォルトで、このワークフローのあるリポジトリのmaster
ブランチにチェックアウトしてくれます。
jobs.build.steps.run
シェルコマンドを実行できます。
シェルを明示的に指定することもできますが、デフォルトではこのジョブを実行しているマシンのシェルで実行するので、今回は明示していません。
成果物をジョブ間でビルドしたファイルを受け渡す
Buildジョブではもちろんビルドするわけですが、この後デプロイするためにはこのビルドしたアプリを次のDeployジョブに受け渡す必要があります。そのためのアクションがactions/upload-artifact
とactions/download-artifact
です。また、このビルドして生成されたファイルを成果物と呼びます。
npm run build
で成果物を生成し、これをactions/upload-artifact
でGitHub上のストレージに保存します(この成果物は90日間保存されます)。
Buildジョブはこれで完了です。続いてDeployジョブも読んでみましょう。
Deployジョブ
jobs:
# ここにBuildジョブ
deploy:
name: Deploy
needs: build
runs-on: ubuntu-latest
steps:
- name: Checkout Repo
uses: actions/checkout@master
- name: Download Artifact
uses: actions/download-artifact@master
with:
name: public
path: public
- name: Deploy to Firebase
uses: w9jds/firebase-action@master
with:
args: deploy
env:
FIREBASE_TOKEN: ${{ secrets.FIREBASE_TOKEN }}
Buildジョブとは違う部分だけ見ていきましょう。
jobs.deploy.needs
ジョブが複数ある場合、それらは基本的に並行して処理されますが、ジョブが処理される順序を設定することもできます。
jobs.deploy.needs
にジョブIDを設定すると、そのジョブが完了するまで待機させることができます。このワークフローでは、DeployジョブはBuildジョブが完了してから実行するようにしています。
w9jds/firebase-action
このアクションのjobs.deploy.steps.with.args
にfirebase CLIのコマンドを引数として渡すことで、fiebase-toolsを使えるようになります。
ただし、Firebase CLIをCIツールで使うためには認証に必要なトークンを取得し、jobs.deploy.steps.env
でFIREBASE_TOKEN
にわたす必要があります。
Firebase CLIの認証トークンを設定
まずはトークンを取得します。
ローカルのプロジェクトルートでfirebase login:ci
を実行すると、ブラウザが起動してログインを促されるのでGoogleアカウントでログインします。
ログインが成功したらシェルにトークンが表示されます。
Waiting for authentication...
✔ Success! Use this token to login on a CI server:
***************************************** // ここにトークン
Example: firebase deploy --token "$FIREBASE_TOKEN"
これでトークンの用意ができました。これをGitHubのリポジトリに設定できる環境変数に設定します。
暗号化された環境変数を使う
GitHubはリポジトリに対して暗号化された環境変数を設定することができます。
リポジトリのSettings > サイドバーのSecrets > New secret > NameとValueを入力 > Add secret
という感じで非常に簡単かつ安全にトークンを保存できます。
そしてここで設定した環境変数をsecrets.FIREBASE_TOKEN
という形で呼び出すことができます。
実行してみる
これでCI/CD環境は構築できたので、早速このワークフローを試してみましょう。適当にコミットしてmaster
ブランチにプッシュします。
するとリポジトリのActionsタブから、実行されたワークフローのログを確認できます。
見事全てのジョブが完了しました
参考
GitHub Actionsのワークフロー構文
成果物を使用してワークフローデータを永続化する
GitHub Action for Firebase
Firebase CLI リファレンス #CI システムで CLI を使用する
暗号化されたシークレットの作成と保存