Nuxt と Firebase と TravisCI でざっと開発準備する 

# 概要

Nuxtのテンプレート画面をFirebaseにデプロイして、CIを導入するまでの流れをまとめました。
おまけでFirebase Analyticsも有効化します。
下の順番で実行して行きます。

- Nuxt, Vuetify
- Firebase(Hosting, Firestore, Functions, Strage)
- git, GitHub
- Travis CI(Firebase自動デプロイ, slack通知)
- Firebase Analytics

## version

```terminal
> node --version
v13.12.0
> firebase --version
8.0.2
> git --version
git version 2.21.0 (Apple Git-122)
> ruby --version
ruby 2.7.1p83 (2020-03-31 revision a0c7c23c9c) [x86_64-darwin19]
```

## 最終的なnpmはこちら

```terminal
> npm list --depth=0
project_name@1.0.0 
├── @nuxtjs/dotenv@1.4.1
├── @nuxtjs/eslint-config@2.0.2
├── @nuxtjs/eslint-module@1.1.0
├── @nuxtjs/firebase@5.0.7
├── @nuxtjs/vuetify@1.11.0
├── babel-eslint@10.1.0
├── eslint@6.8.0
├── eslint-config-prettier@6.10.1
├── eslint-plugin-nuxt@0.5.2
├── eslint-plugin-prettier@3.1.2
├── firebase@7.14.0
├── nuxt@2.12.2
├── prettier@1.19.1
└── typescript@3.8.3
```

# Nuxt, Vuetify

Nuxtが用意していくれているコマンドで、プロジェクトに合わせて雛形を作成します。
(ユーザー名は`user_name`プロジェクト名は`project_name`としています)
https://ja.nuxtjs.org/guide/installation/

```terminal
> npx create-nuxt-app project_name
```

<details><summary>ログ</summary><div>

```terminal
> npx create-nuxt-app project_name

create-nuxt-app v2.15.0
✨  Generating Nuxt.js project in project_name
? Project name project_name  <--そのままEnter
? Project description My glorious Nuxt.js project  <--そのままEnter
? Author name user_name  <--そのままEnter
? Choose programming language
❯ JavaScript  <--JavaScriptで記述する
  TypeScript
? Choose the package manager
  Yarn
❯ Npm  <--Npm派なので今回もNpm選択
? Choose UI framework
  None
  Ant Design Vue
  Bootstrap Vue
  Buefy
  Bulma
  Element
  Framevuerk
  iView
  Tachyons
  Tailwind CSS
  Vuesax
❯ Vuetify.js  <--マテリアルデザインのコンポーネント集
? Choose custom server framework
❯ None (Recommended)  <--使用しない
  AdonisJs
  Express
  Fastify
  Feathers
  hapi
  Koa
  Micro
? Choose Nuxt.js modules
 ◯ Axios
 ◯ Progressive Web App (PWA) Support
❯◉ DotEnv  <--多分後で使うので選択
? Choose linting tools
 ◉ ESLint  <--jsのリンタ＋フォーマッタ、いつも使う
❯◉ Prettier  <--強力なフォーマッタ、これもいつも使う
 ◯ Lint staged files
 ◯ StyleLint
? Choose test framework (Use arrow keys)
❯ None  <--テストは今はNoneで
  Jest
  AVA
? Choose rendering mode
  Universal (SSR)
❯ Single Page App  <--今回のアプリはSPA
? Choose development tools  <--使わない
 ◯ jsconfig.json (Recommended for VS Code)
❯◯ Semantic Pull Requests
```
</div></details>

プロジェクトができたらテンプレート画面を表示してみます

```terminal
cd project_name
npm run dev
```

<details><summary>ログ</summary><div>

```terminal
> npm run dev

> project_name@1.0.0 dev /.../project_name
> nuxt


   ╭──────────────────────────────────────────╮
   │                                          │
   │   Nuxt.js v2.12.2                        │
   │   Running in development mode (spa)      │
   │                                          │
   │   Listening on: http://localhost:3000/   │
   │                                          │
   ╰──────────────────────────────────────────╯

ℹ Preparing project for development                                                                                                                                        01:43:01
ℹ Initial build may take a while                                                                                                                                           01:43:01
✔ Builder initialized                                                                                                                                                      01:43:01
✔ Nuxt files generated                                                                                                                                                     01:43:01

✔ Client
  Compiled successfully in 12.70s

ℹ Waiting for file changes                                                                                                                                                 01:43:16
ℹ Memory usage: 355 MB (RSS: 465 MB)                                                                                                                                       01:43:16
ℹ Listening on: http://localhost:3000/
```

</div></details>

`http://localhost:3000/`に接続するといい感じの画面が生成されています！

<img width="1280" alt="スクリーンショット 2020-04-11 1.49.14.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/202481/ca3b5e6b-163c-afa7-c429-72acaec4a9db.png">

# Firebase

## プロジェクト追加

Firebaseのコンソールから「プロジェクトを追加」します。
https://console.firebase.google.com/
<img width="1280" alt="スクリーンショット 2020-04-11 2.04.33.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/202481/85c48cd6-22e2-f69f-4237-aed5b3c7974c.png">
- プロジェクト名: project-name
- アナリティクス: ON
- アナリティクスの構成: Default Account for Firebase

プロジェクトが生成できたら「プロジェクトを設定」から以下も設定

- リソースロケーション: asia-northeast1 (Firestor設定で必要)
- サポートメール: google mail アカウント設定 (Authで利用)

## Firestoreモードを切り替える

この操作を行わないとFirebase initでエラーとなる模様です。(2020/4/11)
Database → Cloud Platformに移動 → ネイティブモードに切り替える → モードを切り替え
とクリックして、Firestoreを利用できる状態にします。
<img width="1280" alt="スクリーンショット 2020-04-11 3.00.36.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/202481/9f91bc28-d5c7-1af5-a6a0-3d86e80ff75c.png">
<img width="1279" alt="スクリーンショット 2020-04-11 2.58.40.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/202481/f88ea0b5-2c8e-3ce2-64b2-f267045c54f5.png">


## Firebase init

コンソールからログインします。

```terminal
> Firebase login --reauth
```

<details><summary>ログ</summary><div>

```terminal
> Firebase login
i  Firebase optionally collects CLI usage and error reporting information to help improve our products. Data is collected in accordance with Google's privacy policy (https://policies.google.com/privacy) and is not used to identify you.

? Allow Firebase to collect CLI usage and error reporting information? No  <--情報をFirebaseに提供するか

Visit this URL on this device to log in:
https://accounts.google.com/o/oauth2/auth?client_id=xxxxxxxx(略)

Waiting for authentication...

✔  Success! Logged in as address@gmail.com
```

</div></details>

Firebase initを実行して利用するサービスを選択します。
complete! とでたら完了です。

 ```terminal
Firebase init
```

<details><summary>ログ</summary><div>

```terminal
> firebase init

     ######## #### ########  ######## ########     ###     ######  ########
     ##        ##  ##     ## ##       ##     ##  ##   ##  ##       ##
     ######    ##  ########  ######   ########  #########  ######  ######
     ##        ##  ##    ##  ##       ##     ## ##     ##       ## ##
     ##       #### ##     ## ######## ########  ##     ##  ######  ########

You're about to initialize a Firebase project in this directory:

  .../project_name

? Which Firebase CLI features do you want to set up for this folder? Press Space to select features, then Enter to confirm your choices.
 ◯ Database: Deploy Firebase Realtime Database Rules
 ◉ Firestore: Deploy rules and create indexes for Firestore
 ◉ Functions: Configure and deploy Cloud Functions
 ◉ Hosting: Configure and deploy Firebase Hosting sites
❯◉ Storage: Deploy Cloud Storage security rules
 ◯ Emulators: Set up local emulators for Firebase features

=== Project Setup

First, let's associate this project directory with a Firebase project.
You can create multiple project aliases by running firebase use --add,
but for now we'll just set up a default project.

? Please select an option: (Use arrow keys)
❯ Use an existing project
  Create a new project
  Add Firebase to an existing Google Cloud Platform project
  Don't set up a default project
? Select a default Firebase project for this directory:
❯ project_name (project_name)

=== Firestore Setup

Firestore Security Rules allow you to define how and when to allow
requests. You can keep these rules in your project directory
and publish them with firebase deploy.

? What file should be used for Firestore Rules? firestore.rules

Firestore indexes allow you to perform complex queries while
maintaining performance that scales with the size of the result
set. You can keep index definitions in your project directory
and publish them with firebase deploy.

? What file should be used for Firestore indexes? firestore.indexes.json

=== Functions Setup

A functions directory will be created in your project with a Node.js
package pre-configured. Functions can be deployed with firebase deploy.

? What language would you like to use to write Cloud Functions?
❯ JavaScript
  TypeScript
? Do you want to use ESLint to catch probable bugs and enforce style? (y/N) y
✔  Wrote functions/package.json
✔  Wrote functions/.eslintrc.json
✔  Wrote functions/index.js
✔  Wrote functions/.gitignore
? Do you want to install dependencies with npm now? (Y/n) Y

> protobufjs@6.8.9 postinstall /Users/suzukikou/work/vimc_generator/nvimc_generator/functions/node_modules/protobufjs
> node scripts/postinstall

npm notice created a lockfile as package-lock.json. You should commit this file.
added 367 packages from 263 contributors and audited 1116 packages in 15.459s

31 packages are looking for funding
  run `npm fund` for details

found 0 vulnerabilities


=== Hosting Setup

Your public directory is the folder (relative to your project directory) that
will contain Hosting assets to be uploaded with firebase deploy. If you
have a build process for your assets, use your build's output directory.

? What do you want to use as your public directory? dist
? Configure as a single-page app (rewrite all urls to /index.html)? (y/N) y

=== Storage Setup

Firebase Storage Security Rules allow you to define how and when to allow
uploads and downloads. You can keep these rules in your project directory
and publish them with firebase deploy.

? What file should be used for Storage Rules? storage.rules

i  Writing configuration info to firebase.json...
i  Writing project information to .firebaserc...

✔  Firebase initialization complete!
```

</div></details>

## Functions

デプロイ時のエラーを避けるために、`functions/index.js`を編集しておきます。

```diff:project_name/functions/index.js
- const functions = require('firebase-functions');
-
- // // Create and Deploy Your First Cloud Functions
- // // https://firebase.google.com/docs/functions/write-firebase-functions
- //
- // exports.helloWorld = functions.https.onRequest((request, response) => {
- //  response.send("Hello from Firebase!");
- // });
+ const functions = require('firebase-functions')
+
+ // Create and Deploy Your First Cloud Functions
+ // https://firebase.google.com/docs/functions/write-firebase-functions
+
+ exports.helloWorld = functions.https.onRequest((request, response) => + {
+   response.send('Hello from Firebase!')
+ })
```

# git, GitHub

Githubにプロジェクトを作成します。https://github.com/

- Repository name: `project_name`
- `Public`
- Initialize this repository with a README: `チェックは付けない`

リポジトリができらた以下のコマンドでpushします。

```terminal
git add README.md
git commit -m "first commit"
git remote add origin https://github.com/user_name/project_name.git
git push -u origin master
```

<details><summary>ログ</summary><div>

```terminal
> git push -u origin master
Enumerating objects: 3, done.
Counting objects: 100% (3/3), done.
Delta compression using up to 4 threads
Compressing objects: 100% (2/2), done.
Writing objects: 100% (3/3), 454 bytes | 454.00 KiB/s, done.
Total 3 (delta 0), reused 0 (delta 0)
To https://github.com/user_name/project_name.git
 * [new branch]      master -> master
Branch 'master' set up to track remote branch 'master' from 'origin'.
```

</div></details>

# Travis CI

Travis CIを使って自動デプロイを行います。
Travis CIを選んだのは記述量が少なくて済むためです。

## 基本設定

Travis CIからGitHubのActive化します。
https://travis-ci.org/account/repositories

Sync account → 検索BOXに「project_name」 → 対象レポジトリのトグルボタンにチェックするだけです。
<img width="1054" alt="スクリーンショット 2020-04-11 3.22.30.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/202481/ea52e86a-3ca6-ce73-7cea-08ca2a90ae02.png">

Travis CIの設定ファイルを作成します。

```project_name/.travis.yml
nguage: node_js
node_js:
  - v13.12.0
script:
  - npm --prefix "./functions" ci
  - npm run build
cache: npm
```
これでTravis CIがビルドまでしてくれるようになりました。

## Firebaseへのデプロイ自動化

Firebaseのci用のトークンを取得します。
ログインの時と同じようにブラウザ画面が立ち上がりますので、ログインします。
Success!の後に並んでいる文字列がトークンです。

```terminal
> firebase login:ci

Visit this URL on this device to log in:
https://accounts.google.com/o/oauth2/auth?client_id=xxxxxxxx(略)

Waiting for authentication...

✔  Success! Use this token to login on a CI server:

yyyyyyyy(略)

Example: firebase deploy --token "$FIREBASE_TOKEN"
```

これをこのままGitHubにあげるとセキュリティ的によろしくないので、暗号化します。

gemでtravisコマンドをインストールします。

```terminal
gem install travis
```

Firebaseのトークンを暗号化します。
下では省略していますが、かなり長い文字列が吐き出されます。

```terminal
> travis encrypt yyyyyyyy(略)
/.../.rbenv/versions/2.7.1/lib/ruby/gems/2.7.0/gems/highline-1.7.10/lib/highline.rb:624: warning: Using the last argument as keyword parameters is deprecated
Please add the following to your .travis.yml file:

  secure: "zzzzzzzz(略)"

Pro Tip: You can add it automatically by running with --add.
```

これを含めて`.travis.yml`に以下を追記します。

```diff:project_name/.travis.yml
nguage: node_js
node_js:
  - v13.12.0
script:
  - npm --prefix "./functions" ci
  - npm run build
cache: npm
+ deploy:
+   provider: firebase
+   skip_cleanup: true
+   token:
+    secure: "zzzzzzzz(略)"
```

# slack通知

Travis CIの実行結果をSlackに通知するように設定します。

まずはslackのアプリにTravis CIを追加します。
https://slack.com/apps
<img width="1081" alt="スクリーンショット 2020-04-11 9.21.49.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/202481/f03748bf-0557-8e78-4364-97b1a8bb0992.png">
利用するワークスペースと通知先Channellを入力します。

Travis CIの設定編集画面まで来ることができたら、「セットアップ手順」を開きます。
<img width="518" alt="スクリーンショット 2020-04-11 10.01.38.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/202481/b3cf4ae1-45ac-ea65-b8f6-4d46eb336619.png">

「認証情報の暗号化」の項目にあるコマンドを実行しましょう。

```terminal
travis encrypt "account:token" --add notifications.slack
```

自動で`.travis.yml`が書き換えられます。

```diff:project_name/.travis.yml
nguage: node_js
node_js:
  - v13.12.0
script:
  - npm install -g firebase-tools@8.0.2
  - npm --prefix "./functions" ci
  - npm run build
cache: npm
deploy:
  provider: firebase
  skip_cleanup: true
  token:
    secure: "zzzzzzzz(略)"
+ notifications:
+   slack:
+     secure: "ssssssss(略)"
```

これでslackに実行結果を通知してくれます。

## 実行

GitHubのmasterブランチにpushすることで自動でFirebaseへデプロイ、スラック通知が実行されます。
pushして動作確認してみてください！

```terminal
git add .
git commit -m "kitting"
git push origin HEAD
```
https://travis-ci.org/

# Firebase Analytics

ビュー数などを詳しく見たいのでFirebase Analyticsを有効にします

## Firebase

そういえばアプリを作成していなかったので作成します。
プロジェクトの設定から`</>`ボタンをクリック
<img width="1280" alt="スクリーンショット 2020-04-11 11.05.34.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/202481/a92d1a82-a2b4-e9e9-5c9f-f6ea0b3a882b.png">

- アプリのニックネーム: `project-name-app`
- Hostingも設定します: チェックをつける

残りの項目はいったん無視して「次へ」アプリの登録を完了させます。

## env

envファイルを設定します。　　

作成したアプリの「Firebase SDK snippet」→「構成」から必要な情報をコピーして、
以下のようにenvファイルを作成します。

```progect_name/.env
FIREBASE_API_KEY="apiKeyの値"
FIREBASE_AUTH_DOMAIN="authDomainの値"
FIREBASE_DATABASE_URL="databaseURLの値"
FIREBASE_PROJECT_ID="projectIdの値"
FIREBASE_STORAGE_BUCKET="storageBucketの値"
FIREBASE_MESSAGING_SENDER_Id="messagingSenderIdの値"
FIREBASE_APP_ID="appIdの値"
FIREBASE_MEASUREMENT_ID="measurementIdの値"
```

nuxt.conf.jsで利用できるようにenvファイルを読み込む設定を書き足しておきます。

```diff:progect_name/nuxt.conf.js
import colors from 'vuetify/es5/util/colors'
+ require('dotenv').config()

export default {
(略)
```

Travis CIにもenvの情報を渡してあげましょう。
Travis CI webページ、対象リポジトリのsettingページから設定することができます。
<img width="968" alt="スクリーンショット 2020-04-11 11.48.08.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/202481/4ba2f7e4-a51b-c05e-f87e-1f14286c1f67.png">



## Nuxt Firebase
`Nuxt Firebase`が便利なのでこちらを利用してAnalyticsを設定します。
https://firebase.nuxtjs.org/

まずは必要なモジュールをインストールします

```terminal
npm install firebase @nuxtjs/firebase
```

次にnuxt.con.jsに以下のように記述します。

```diff:progect_name/nuxt.conf.js
  modules: [
    // Doc: https://github.com/nuxt-community/dotenv-module
-     '@nuxtjs/dotenv'
+     '@nuxtjs/dotenv',
+     '@nuxtjs/firebase'
  ],
+
+   firebase: {
+     config: {
+       apiKey: process.env.FIREBASE_API_KEY,
+       authDomain: process.env.FIREBASE_AUTH_DOMAIN,
+       databaseURL: process.env.FIREBASE_DATABASE_URL,
+       projectId: process.env.FIREBASE_PROJECT_ID,
+       storageBucket: process.env.FIREBASE_STORAGE_BUCKET,
+       messagingSenderId: process.env.FIREBASE_MESSAGING_SENDER_ID,
+       appId: process.env.FIREBASE_APP_ID,
+       measurementId: process.env.FIREBASE_MEASUREMENT_ID
+     },
+     services: {
+       analytics: true
+     }
+   }

```

GitHubにpushします。

```terminal
git add .
git commit -m "analytics"
git push origin HEAD
```

FirebaseのStreamViewからアクセスしている様子が見えればOKです。
※少しラグがあるようなので、反映されない時は少し時間を置いてみてください。
<img width="1280" alt="スクリーンショット 2020-04-11 17.14.47.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/202481/6d9fbb98-52fc-fd19-ba41-7e24867b372c.png">

# 最後に

フロントエンド、サーバーサイド、CIツールをざっと構築してみました。
これで後はサービス開発に集中するだけですね！
良い開発ライフを！

# 参考

<details><summary>nuxt.config.js全文</summary><div>

```project_name/next.config.js
import colors from 'vuetify/es5/util/colors'
require('dotenv').config()

export default {
  mode: 'spa',
  /*
   ** Headers of the page
   */
  head: {
    titleTemplate: '%s - ' + process.env.npm_package_name,
    title: process.env.npm_package_name || '',
    meta: [
      { charset: 'utf-8' },
      { name: 'viewport', content: 'width=device-width, initial-scale=1' },
      {
        hid: 'description',
        name: 'description',
        content: process.env.npm_package_description || ''
      }
    ],
    link: [{ rel: 'icon', type: 'image/x-icon', href: '/favicon.ico' }]
  },
  /*
   ** Customize the progress-bar color
   */
  loading: { color: '#fff' },
  /*
   ** Global CSS
   */
  css: [],
  /*
   ** Plugins to load before mounting the App
   */
  plugins: [],
  /*
   ** Nuxt.js dev-modules
   */
  buildModules: [
    // Doc: https://github.com/nuxt-community/eslint-module
    '@nuxtjs/eslint-module',
    '@nuxtjs/vuetify'
  ],
  /*
   ** Nuxt.js modules
   */
  modules: [
    // Doc: https://github.com/nuxt-community/dotenv-module
    '@nuxtjs/dotenv',
    '@nuxtjs/firebase'
  ],
  /*
   ** vuetify module configuration
   ** https://github.com/nuxt-community/vuetify-module
   */
  vuetify: {
    customVariables: ['~/assets/variables.scss'],
    theme: {
      dark: true,
      themes: {
        dark: {
          primary: colors.blue.darken2,
          accent: colors.grey.darken3,
          secondary: colors.amber.darken3,
          info: colors.teal.lighten1,
          warning: colors.amber.base,
          error: colors.deepOrange.accent4,
          success: colors.green.accent3
        }
      }
    }
  },

  firebase: {
    config: {
      apiKey: process.env.FIREBASE_API_KEY,
      authDomain: process.env.FIREBASE_AUTH_DOMAIN,
      databaseURL: process.env.FIREBASE_DATABASE_URL,
      projectId: process.env.FIREBASE_PROJECT_ID,
      storageBucket: process.env.FIREBASE_STORAGE_BUCKET,
      messagingSenderId: process.env.FIREBASE_MESSAGING_SENDER_ID,
      appId: process.env.FIREBASE_APP_ID,
      measurementId: process.env.FIREBASE_MEASUREMENT_ID
    },
    services: {
      analytics: true
    }
  },
  /*
   ** Build configuration
   */
  build: {
    /*
     ** You can extend webpack config here
     */
    extend(config, ctx) {}
  }
}
```

</div></details>