nuxt.js + GitHub Actions + Firebase hosting / functions でJAMstackを目指して

~~最初からmicrocms / gatsby / netlifyつかっときゃ良かった~~

### 自白

2年前にnuxt.jsでお客様が記事書くWebサイトを構築しました。
そしてSSRで記事編集して公開できるようにしました。

それから１年半してJAMstack(SSG)に手を出したらSSRより圧倒的に早いじゃないですか。。。
作ってから時間がたって、スマホだと遅いサイトだなとつくづく実感しました。

PCだとまだましだけどスマホだと完全に遅い。
お客様にも悪いので既存のSSRからSSGに乗り換える提案をするために調査と実験を繰り返しました。

### 対象読者
・nuxt.js使っててSSGに移行したい人
・でもサイトの編集機能があるからDBの編集機能にも対応したい人
・firebase functions遅いかなって感じてる人

### 解説しないこと
・nuxt.jsで編集画面作る方法
・firebaseにデプロイする方法
・gitの使い方

### 本題

既存のfirebaseの設定にGitHubの設定を追加

```bash
firebase init hosting:github
```

色々聞かれるけど[この記事](https://qiita.com/ikkei12/items/0f0c2d95bdd3b54d6bac)参考にこたえていけばOKだと思います。

`firebase-hosting-merge.yml`, `firebase-hosting-pull-request.yml`が作成されるのでnodeのバージョンとnpmをyarnに変えました。
※本番に導入するならテストの設定もしましょう。（ここでは飛ばす）

```firebase-hosting-merge.yml
# This file was auto-generated by the Firebase CLI
# https://github.com/firebase/firebase-tools

name: Deploy to Firebase Hosting on merge
'on':
  push:
    branches:
      - main
jobs:
  build_and_deploy:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v2
      - name: Use Node.js 12 # nodeのバージョンを12にしてます
        uses: actions/setup-node@v1
        with:
          node-version: 12
      - run: yarn install && yarn generate # npm → yarnに変えてます
      - uses: FirebaseExtended/action-hosting-deploy@v0
        with:
          repoToken: '${{ secrets.GITHUB_TOKEN }}'
          firebaseServiceAccount: '${{ secrets.FIREBASE_SERVICE_ACCOUNT_PROJECT_X }}'
          channelId: live
          projectId: project-x
        env:
          FIREBASE_CLI_PREVIEWS: hostingchannels
```
この`firebase-hosting-pull-request.yml`をコピーして`firebase-hosting-manual.yml`を作成します。
`firebase-hosting-manual.yml`ではworkflow_dispatchという機能でWeb API経由でデプロイできるようにします。

```firebase-hosting-manual.yml
name: Manually triggered workflow
on:
  workflow_dispatch: # workflow_dispatchを設定（引数はいらない）
jobs:
  build_and_deploy:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v2
      - name: Use Node.js 12
        uses: actions/setup-node@v1
        with:
          node-version: 12
      - run: yarn install && yarn generate
      - uses: FirebaseExtended/action-hosting-deploy@v0
        with:
          repoToken: '${{ secrets.GITHUB_TOKEN }}'
          firebaseServiceAccount: '${{ secrets.FIREBASE_SERVICE_ACCOUNT_PROJECT_X }}'
          channelId: live
          projectId: project-x
        env:
          FIREBASE_CLI_PREVIEWS: hostingchannels
```

GitHubのmainレポジトリにcommit/pushしてmergeによるfirebaseへのdeployが走ることを確認してください。
うまくいかない場合は`firebase.json`を見直してください。
参考になるかもしれないので私のやつ置いておきます。

```firebase.json
{
  "rulesFile": "database.rules.json",
  "functions": {
    "source": "functions",
    "predeploy": [
      "npm --prefix ./ run build",
      "rm -rf functions/nuxt",
      "cp -r .nuxt/ functions/nuxt/",
      "cp ./nuxt.config.js functions/"
    ]
  },
  "hosting": {
    "public": "dist",
    "rewrites": [
      {
        "source": "**/admin/**", # admin以下の部分は編集画面なのでSSGしない
        "function": "ssrapp"
      }
    ],
    "ignore": [
      "firebase.json",
      "**/.*",
      "**/node_modules/**"
    ]
  },
  "storage": {
    "rules": "storage.rules"
  }
}
```

ここまででCI/CDできましたがお客様がCRUD処理をしてもサイトが更新されなくなりました。

[この記事](https://docs.github.com/en/free-pro-team@latest/rest/reference/actions#list-repository-workflows)参考にしてworkflow idを取得します。

```bash
curl \
  -H "Accept: application/vnd.github.v3+json" \
  https://api.github.com/repos/{github-user-name}/{repository-name}/actions/workflows
```

また[この記事](https://docs.github.com/ja/free-pro-team@latest/github/authenticating-to-github/creating-a-personal-access-token)参考にしてGithubのpersonal access tokenも準備してください。

で次のコマンドを実行するとgithubのワークフローが走りデプロイされることを確認してください。

```bash
curl  -XPOST -H "Authorization: token $YOUR_GITHUB_TOKEN" https://api.github.com/repos/{github-user-name}/{repository-name}/actions/workflows/{workflow-id}/dispatches -d '{"ref": "main"}}'
```

ここまでくればあと一息。
Firebase Functionsに記事作成で起動されるトリガー関数を登録するだけです。

以下の関数をfirebase functionsにデプロイしてください。

```functions/index.js
const functions = require('firebase-functions')
const axios = require('axios').default

const GITHUB_TOKEN = functions.config().setting.github_token
const GITHUB_WORKFLOW_ID = functions.config().setting.workflow_id

exports.postGithubAction = functions.database.ref('articles/{articleId}')
  .onWrite(async (change, context) => {
  axios({
    url: `https://api.github.com/repos/${github-user-name}/${repository-name}/actions/workflows/${GITHUB_WORKFLOW_ID}/dispatches`,
    method: 'POST',
    data: {
      ref: 'main'
    },
    headers: {
      Authorization: `token ${GITHUB_TOKEN}`
    }
  })
})

// 以下SSRの関数が続く
```

記事を更新してGithub Actionが実行されれば成功です！