Firebase の快適 GitHubActions まとめ

だいたい毎回追加してる Workflow の雛形紹介。

## Firestore
### Test Security Rules
```..github/workflows/firestore_security_rules_test.yaml
name: Firestore_Security_Rules_Test

on:
  pull_request:
    types: [opened, synchronize]
    paths:
      - firestore/rules/**
      - firestore/test/**
      - firestore/*.json
      - .github/workflows/firestore_security_rules_test.yaml
  push:
    branches:
      - master
    paths:
      - firestore/rules/**
      - firestore/test/**
      - firestore/*.json
      - .github/workflows/firestore_security_rules_test.yaml

jobs:
  firestore_security_rules_test:
    runs-on: ubuntu-latest
    timeout-minutes: 5
    defaults:
      run:
        working-directory: firestore
    steps:
      - uses: actions/checkout@v2
        with:
          fetch-depth: 1
      - uses: actions/setup-node@v1
        with:
          node-version: '12.x'
      - run: |
          npm install
          npm i -g firebase-tools
          firebase emulators:exec --only firestore 'npm test'
```

### Deploy Security Rules
```..github/workflows/firestore_security_rules_deploy.yaml
name: Firestore_Security_Rules_Deploy

on:
  push:
    branches:
      - master
    paths:
      - firestore/rules/**
      - firestore/*.json
      - firestore/generate_rules.js
      - .github/workflows/firestore_security_rules_deploy.yaml

jobs:
  firestore_security_rules_deploy:
    runs-on: ubuntu-latest
    timeout-minutes: 5
    defaults:
      run:
        working-directory: firestore
    steps:
      - name: start deployment
        uses: bobheadxi/deployments@master
        id: deployment
        with:
          step: start
          token: ${{ secrets.GITHUB_TOKEN }}
          env: Firestore_Security_Rules-dev
      - uses: actions/checkout@v2
        with:
          fetch-depth: 1

      # See: https://medium.com/firebase-developers/imports-for-firestore-security-rules-are-the-best-26f0770ad23c
      #- name: generate rules
      #  run: npm run generate

      - uses: w9jds/firebase-action@master
        with:
          args: deploy --only firestore:rules -P dev
        env:
          FIREBASE_TOKEN: ${{ secrets.FIREBASE_DEV_TOKEN }}
          PROJECT_PATH: firestore
      - name: update deployment status
        uses: bobheadxi/deployments@master
        if: always()
        with:
          step: finish
          token: ${{ secrets.GITHUB_TOKEN }}
          status: ${{ job.status }}
          deployment_id: ${{ steps.deployment.outputs.deployment_id }}
          env_url: https://example.com
```

## Deploy Indexes
```..github/workflows/firestore_indexes_deploy.yaml
name: Firestore_Indexes_Deploy

on:
  push:
    branches:
      - master
    paths:
      - firestore/firestore.indexes.json
      - .github/workflows/firestore_indexes_deploy.yaml

jobs:
  firestore_indexes_deploy:
    runs-on: ubuntu-latest
    timeout-minutes: 5
    defaults:
      run:
        working-directory: firestore
    steps:
      - name: start deployment
        uses: bobheadxi/deployments@master
        id: deployment
        with:
          step: start
          token: ${{ secrets.GITHUB_TOKEN }}
          env: Firestore_Indexes-dev
      - uses: actions/checkout@v2
        with:
          fetch-depth: 1
      - uses: w9jds/firebase-action@master
        with:
          args: deploy --only firestore:indexes -P dev
        env:
          FIREBASE_TOKEN: ${{ secrets.FIREBASE_DEV_TOKEN }}
          PROJECT_PATH: firestore
      - name: update deployment status
        uses: bobheadxi/deployments@master
        if: always()
        with:
          step: finish
          token: ${{ secrets.GITHUB_TOKEN }}
          status: ${{ job.status }}
          deployment_id: ${{ steps.deployment.outputs.deployment_id }}
          env_url: https://example.com
```

ちなみに、手元の json 更新自体は `firebase firestore:indexes > ./firestore.indexes.json` ってやると早いです。

## Functions
### Unite Test
```..github/workflows/functions_unit_test.yaml
name: Functions_Unit_Test

on:
  pull_request:
    types: [opened, synchronize]
  push:
    branches:
      - master

jobs:
  functions_unit_test:
    runs-on: ubuntu-latest
    timeout-minutes: 5
    defaults:
      run:
        working-directory: functions
    steps:
      - uses: actions/checkout@v2
        with:
          fetch-depth: 1
      - uses: actions/setup-node@v1
        with:
          node-version: '12.x'
      - run: |
          npm ci
          npm i -g firebase-tools
          nyc --reporter=lcovonly mocha lib/test/ --recursive --timeout=10000 --reporter spec
      - uses: codecov/codecov-action@v1
        with:
          token: ${{secrets.CODECOV_TOKEN}}
          file: functions/coverage/lcov.info
```

### Deploy
```..github/workflows/functions_deploy.yaml
name: Functions_Deploy

on:
  push:
    branches:
      - master

jobs:
  functions_deploy:
    runs-on: ubuntu-latest
    timeout-minutes: 5
    defaults:
      run:
        working-directory: functions
    steps:
      - name: start deployment
        uses: bobheadxi/deployments@master
        id: deployment
        with:
          step: start
          token: ${{ secrets.GITHUB_TOKEN }}
          env: Functions-dev
      - uses: actions/checkout@v2
        with:
          fetch-depth: 1
      - uses: actions/setup-node@v1
        with:
          node-version: '10'
      - name: build
        run: npm ci && npm run build
      - uses: w9jds/firebase-action@master
        with:
          args: deploy --only functions -P dev
        env:
          FIREBASE_TOKEN: ${{ secrets.FIREBASE_DEV_TOKEN }}
          PROJECT_PATH: functions
      - name: update deployment status
        uses: bobheadxi/deployments@master
        if: always()
        with:
          step: finish
          token: ${{ secrets.GITHUB_TOKEN }}
          status: ${{ job.status }}
          deployment_id: ${{ steps.deployment.outputs.deployment_id }}
          env_url: http://example.com
```

## 共通
### TypeScript Format
```..github/workflows/format.yaml
name: Format

on:
  push:
    branches:
      - master
    paths:
      - '**.ts'
  pull_request:
    types: [opened, synchronize]
    paths:
      - '**.ts'

jobs:
  format_on_firestore:
    runs-on: ubuntu-latest
    defaults:
      run:
        working-directory: firestore
    steps:
      - uses: actions/checkout@v2
        with:
          fetch-depth: 1
      - uses: actions/setup-node@v1
        with:
          node-version: '12.x'
      - run: npm install
      - run: npm run formatOnCI
  format_on_functions:
    runs-on: ubuntu-latest
    defaults:
      run:
        working-directory: functions
    steps:
      - uses: actions/checkout@v2
        with:
          fetch-depth: 1
      - uses: actions/setup-node@v1
        with:
          node-version: '12.x'
      - run: npm ci
      - run: npm run formatOnCI
```

npm の `formatOnCI` には、`prettier --check './**/*.ts'` を設定してある。

### Update Packages Cron
```..github/workflows/update_dependencies.yaml
name: Update_Dependencies

on:
  schedule:
    - cron: '0 0 * * *' # UTC

# https://www.npmjs.com/package/npm-check-updates を使う
jobs:
  update_dependencies_for_firestore:
    runs-on: ubuntu-latest
    timeout-minutes: 10
    steps:
      - uses: actions/checkout@v2
        with:
          fetch-depth: 1
          ref: master
      - uses: actions/setup-node@v1
        with:
          node-version: '12.x'
      - name: update
        working-directory: firestore
        run: |
          npm install
          npm install -g npm-check-updates
          ncu -u
          npm install
      - name: create PR
        uses: peter-evans/create-pull-request@v2
        with:
          commit-message: '[Scheduled] update dependencies for Firestore'
          title: '[Scheduled] update dependencies for Firestore'
          body: |
            Auto-generated by [create-pull-request][1]

            Why is PR author User? Why not Bot?
            -> See: https://github.com/peter-evans/create-pull-request/blob/master/docs/concepts-guidelines.md#triggering-further-workflow-runs

            [1]: https://github.com/peter-evans/create-pull-request
          branch: cron_update_dependencies_for_firestore
          base: master
          token: ${{ secrets.REPO_SCOPED_TOKEN }}

  update_dependencies_for_functions:
    runs-on: ubuntu-latest
    timeout-minutes: 10
    steps:
      - uses: actions/checkout@v2
        with:
          fetch-depth: 1
          ref: master
      - uses: actions/setup-node@v1
        with:
          node-version: '10'
      - name: update
        working-directory: functions
        run: |
          npm ci
          npm install -g npm-check-updates
          ncu -u
          npm install
      - name: create PR
        uses: peter-evans/create-pull-request@v2
        with:
          commit-message: '[Scheduled] update dependencies for Functions'
          title: '[Scheduled] update dependencies for Functions'
          body: |
            Auto-generated by [create-pull-request][1]

            Why is PR author User? Why not Bot?
            -> See: https://github.com/peter-evans/create-pull-request/blob/master/docs/concepts-guidelines.md#triggering-further-workflow-runs

            [1]: https://github.com/peter-evans/create-pull-request
          branch: cron_update_dependencies_for_functions
          base: master
          token: ${{ secrets.REPO_SCOPED_TOKEN }}
```

## 関連
script とか関連する公式ドキュメントのリンクを置いておきます。

* [Cloud Functions の単体テスト](https://firebase.google.com/docs/functions/unit-testing)
* [Cloud Functions Getting Start with TypeScript](https://github.com/firebase/functions-samples/tree/master/typescript-getting-started)
* [オンラインテストサンプル](https://github.com/firebase/functions-samples/blob/master/quickstarts/uppercase/functions/test/test.online.js)
* [Emulator Suite](https://firebase.google.com/docs/emulator-suite)
* [Firestore Emulator quickstart](https://github.com/firebase/quickstart-nodejs/tree/master/firestore-emulator/typescript-quickstart)