create-react-appを使ってCI

create-react-appとReact.jsの練習を兼ねてmacのメモっぽい何かを作ってみます。
Facebookのライセンスまわりが炎上しているようですが・・・。
Reactに限らずJavaScriptを使って開発しようとするとwebpack, gulp, babel, Jest等など開発環境整えるのまじ面倒くさい。面倒くさいので一度作るとずっとそのままでいつの間にか古くなっています。
Create React Appはその面倒な環境を一発で整えてしかも自分で作るよりもビルドがなんか速い！
そして、テストツールのJestが組み込まれているのでせっかくなのでCIする環境を構築してみました。

## ソース他
### GitHub
https://github.com/taichi0529/memo

### GitHub Pages
https://taichi0529.github.io/memo/

### heroku
https://memoo.herokuapp.com/

## 1. アプリ仕様
macのメモアプリっぽいなにかを作る。
データはlocalStorageへ保存。

## 2. 構成
変わったことはしないでできるだけ分かりやすく。
ビルドしたアプリはheroku上で見られるようにします。

![ci.png](https://qiita-image-store.s3.amazonaws.com/0/183301/13438d9d-53e3-1acf-a7f1-3dff09525b86.png)

## 3. 開発環境構築

### 3.1. node.jsのインストール

公式サイトに行ってv8.2.1をダウンロードしてインストールしました。
npmが5になっています。lockファイルを作る様になっています。

### 3.2. yarnのインストール
npmが5になっていましたがパッケージ管理ツールはyarnを使います。だってcreate-react-appもyarnもFacebook謹製だし。

```
npm install -g yarn
```

### 3.3. Create React Appのインストール
これもコマンド一発。

```
npm install -g create-react-app
```

### 3.4. アプリを作る

```
create-react-app memo
```

これだけで基本的になにもやる必要なし！で、


```
cd memo
yarn start
```

をおこなうと、このように表示されます。
<img width="822" alt="yarn-start.png" src="https://qiita-image-store.s3.amazonaws.com/0/183301/00fad995-7d69-02b9-2597-17d73af26f68.png">


そうするとブラウザ上でこの様に表示されます。
![localhost.png](https://qiita-image-store.s3.amazonaws.com/0/183301/5e6f2317-e205-3add-6499-9df6639dc0d5.png)


ローカルマシン上でWebサーバーが起動して更新がリアルタイムで反映されるようになります。webpack-dev-serverが動いている様ですが、ここまで設定するのに一からやったらめっちゃ大変です。

`src/index.js`が起点となっています。

```src/index.js
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import registerServiceWorker from './registerServiceWorker';

ReactDOM.render(<App />, document.getElementById('root'));
registerServiceWorker();
```
`src/App.js`なんかをいじると直ぐに反映されます。Reactで書かれていますがReact知らなくても直感的に分かるとおもいます。
cssもimportをインポートしていてビルドするとcssファイルとして出力されます。なにがどうやっているのかは分かりませんがとにかく便利！
ちなみに別にReact使わなくてもいいので開発環境として使うのもありかなと思います。

## 4. テスト
Jestが入っているので直ぐにテストが書けます。

```
yarn test
```

これで実行出来ます。
サンプルが`src/App.test.js`として入っています。`ファイル名.test.js`のファイルがテストファイルとして見なされます。もしくは`src/__tests__`に入っているjsファイルです。
また、モック用のディレクトリ`src/__mocks__`内のファイルもテスト用のファイルと見なされてテスト対象に含まれなくなります。

package.json内の`"test": "react-scripts test --env=jsdom"`の部分を`"test": "react-scripts test --env=jsdom --coverage"にするとカバレッジ率を出せます。

<img width="822" alt="coverage.png" src="https://qiita-image-store.s3.amazonaws.com/0/183301/4ad5a282-216d-469c-e343-caa2187bccba.png">

## 5. ビルド

```
yarn build
```

buildディレクトリが出来てその中にhtml, js, css含めた一式が出力されます。
<img width="822" alt="build.png" src="https://qiita-image-store.s3.amazonaws.com/0/183301/f64783f7-3d49-e33e-319e-9088c06a8132.png">

serveが入っていない場合はserveいれて〜みたいにいわれます。
yarnなりnpmなりでserveを入れて書いてあるように`serve -s build`でビルド後のファイルをローカルで確認出来ます。
github pagesのことも書かれていますが下の方で解説します。

またjsを動かしているだけならindex.htmlをブラウザに放り込んで確認してもいいのですが、その場合jsやcssなんかのパスの問題が出てきます。index.htmlをいじっただけではだめで、css内の画像へのリンクなんかも問題がでます。その場合package.json内に

```
{
    "homepage": "./"
｝
```
を追加してビルドすればOKでした。


### 5.1 ファイルのハッシュが嫌な場合

ビルドするとmain.jsとmain.cssが作られるのですが`main.9fdf0e48.js`の様にそれぞれのファイルのハッシュ値がファイル名につきます。ブラウザキャッシュのことを考えると有用なのですが要らないときもあります。そう言う場合は何かしらハッシュ値をつけないオプションがあるのかなーと探しましたがどうも無いみたいです。ハッシュを削除するには力技でシェルスクリプトで削除するのが良さそうです。

package.jsonのscriptsへpostbuildを追加してそこで削除するシェルスクリプトを実行すればいけます。ファイルをmvしてhtmlをsedする感じでしょうか。

```
{
  "scripts": {
    "start": "react-scripts start",
    "build": "react-scripts build",
    "postbuild": "bash deleteHash.sh",
    "test": "react-scripts test --env=jsdom",
    "eject": "react-scripts eject"
  }
}
```

preつけたりpostつけたりするとそれぞれの動作の前後で実行してくれます。


あと`yarn eject`を行うと　create-react-appの管理から抜け出してnode_modules以下に必要なモジュールが全てインストールされて単独で動くようになります。
そうしたら、ハッシュをつけているところを探し出して書き換えればいいんじゃないかなーみたいな。




## 6. アプリの作成
開発環境がこれで出来たのでmacのメモアプリもどきを作っていきます。
React使うの面倒くさくなって一部jqueryで書いてしまっています・・・。


## 7. GitHub Pages
herokuへデプロイするのでGitHub Pagesなくてもいいのですがメチャクチャ便利だったので。用途によってはこっちだけで十分です。
GitHub PagesについてのCreate React Appのドキュメントは[こちら](https://github.com/facebookincubator/create-react-app/blob/master/packages/react-scripts/template/README.md#github-pages)に書いてあってめっちゃ簡単なのですが少々解説を。

GitHub PagesはGitHubで行っている静的ファイルの公開サービスです。
今回使っているのは
https://taichi0529.github.io/memo/
の様に`https://ユーザー名.github.io/リポジトリ名/`で見ることが出来るサービスのほうです。
リポジトリにgh-pagesってブランチ作ってそこにファイルを置くだけでみることができます。
ただ、ブランチがそのままルートディレクトリになるのでビルドしたファイルでそっくり置き換える必要があります。それをgh-pagesモジュールが全自動でやってくれます。

### 7.1. gh-pagesモジュールのインストール

```
yarn add gh-pages
```
### 7.2. gh-pagesのブランチをgithub上に作成する
github上でどうやってブランチ作るのかわかりにくかったですが、簡単でした。
<img width="544" alt="branch.png" src="https://qiita-image-store.s3.amazonaws.com/0/183301/0c548bf1-ad04-df20-1b78-11fc81790a44.png">


### 7.3. package.jsonの設定
手動でビルドした後にgh-pagesでもいいのですが折角なので。

```diff:package.json
{
  "scripts": {
    "start": "react-scripts start",
    "build": "react-scripts build",
    "test": "react-scripts test --env=jsdom",
    "eject": "react-scripts eject",
+   "preghpages": "yarn build",
+   "ghpages": "gh-pages -d build"
  },
+ "homepage": "./"
}
```
package.jsonのhomepageは`./`を入れておけば動くと思いますが動かなかったらフルパスで。

### 7.4. 起動
```
yarn ghpages
```
これでGitHub Pagesに公開されます。

## 8. heroku

Circle CIからherokuへデプロイするのですがローカルからデプロイして動くようにしておいた方が便利なのでそうします。


### 8.1. herokuアプリの作成
Create New Appで新しいAppを作成します。そうするとこんな画面が出てきます。

<img width="851" alt="deploy.png" src="https://qiita-image-store.s3.amazonaws.com/0/183301/f2a8c53b-2dd6-3bc4-1ade-8deea5eb049a.png">


この通りやれば簡単にデプロイできるのですが今回はCircle CIからデプロイしたいので少々変えます。herokuコマンドめっちゃ便利なんですけどね。

書いてあるとおりにやるとherokuへのgitがhttps経由になってしまいます。そうするとCircle CI上にもherokuコマンドを使える環境を構築しないといけないので。ただ、Create React Appのドキュメントにあるリンク先はherokuコマンドを使うやり方が書いてありました。

### 8.2. gitへリモートリポジトリの登録

heroku上のリポジトリへpushすることでデプロイされるのでherokuをリモートリポジトリとして登録します。

```
git remote add heroku git@heroku.com:[App名].git
```

.git/configを確認するとherokuが登録されているはずです。

### 8.3. キーペアの作成及び設定
公開鍵と秘密鍵を作ってherokuに公開鍵を登録します。また、gitでpushするときはもう片方の秘密鍵を使うことがが出来る設定をします。

#### 8.3.1 キーペアの作成
オプションはテキトーに。

```
ssh-keygen -t rsa -b 4096
```
`~/.ssh/heroku`という名前で鍵ファイルを保存したことにします。

#### 8.3.2. ~/.ssh/configの設定

```diff:~/.ssh/config
+ Host heroku.com
+   HostName heroku.com
+   User git
+   IdentityFile ~/.ssh/heroku
```

### 8.4. package.jsonの設定
herokuはpackage.jsonがあるかどうかでpushされたアプリケーションがnodeだと認識しているっぽいです。とりあえずnodeのバージョンを指定しておけば動きます。

```diff:package.json
{
+ "engines": {
+   "node": ">=8.2.0 <8.3.0"
+ },
}
```

### 8.5. リモートへpush

```
git push heroku master
```

これでビルドが開始されます。問題無く終われば`https://アプリ名.herokuapp.com`でアクセスできます。

## 9. Circle CI
下記の4つを出来るようにします。

- テスト
- ビルド
- ビルドしたソースのダウンロード
- herokuへのデプロイ


CircleCI2.0を使います。gitのリポジトリへpushしたらとにかく動くようになっています。
workflowをつかったりブランチで動作を分けたりも出来ますがここではシンプルにしてあります。
CircleCIの使い方自体は省きます。

**CircleCIのWebUIからherokuとの連携が必要です。**
CircleCI2.0だとyml上で設定するだけでデプロイするなんてことは出来ないのですが連携しておけば公開鍵がherokuへ登録されるので自前でキーペア作ってherokuとCircleCIの両方に設定とか面倒なことしなくてすみます。

### 9.1. 設定ファイル

```yaml:.circleci/config.yml
version: 2
jobs:
  build:
    docker:
      # CircleCI2はdockerコンテナで動くので動作させるイメージファイルを指定します。
      # DockerHub上にあるイメージ指定します。nodeだと https://hub.docker.com/r/circleci/node/
      - image: circleci/node:8.2.1

    # コンテナ上での作業ディレクトリです。
    working_directory: ~/repo
    steps:
      # githubからチェックアウト
      - checkout

      # キャッシュの読み込み。下の方でnode_modules/以下をキャッシュする設定している。
      # キャッシュの区別にpackage.jsonじゃなくてyarn.lockファイルを使った方がいい気もする。
      - restore_cache:
          keys:
          - v1-dependencies-{{ checksum "package.json" }}
          # fallback to using the latest cache if no exact match is found
          - v1-dependencies-
          
      - run: yarn install

      # yarn installでインストールしたモジュールをnode_modulesごとキャッシュ。
      - save_cache:
          paths:
            - node_modules
          key: v1-dependencies-{{ checksum "package.json" }}

      # テスト
      - run: yarn test

      # ビルド
      - run: yarn build
      
      # 自作のスクリプトでダウンロード用にbuildディレクトリをtar.gzファイルにしている。
      - run : bash .circleci/create-artifacts.sh
      
      # ビルドしたファイルをダウンロードできるようにする。
      # ビルド後にArtifactsというタブができるのでそこからダウンロードできる。
      - store_artifacts:
          path: ~/repo/artifacts
          destination: artifact-file


      # herokuへの接続設定
      # CircleCI上で'HEROKU_APP_NAME'という名前でherokuのApp名を設定しておく。
      - run: bash .circleci/setup-heroku.sh
      
# これ書かなくてもCircleCI上の秘密鍵は全部入るっぽい？
#      - add_ssh_keys:
#          fingerprints:
#            - "ce:e9:5d:a1:3e:e4:f6:36:d0:b7:2b:4a:0d:64:91:3a"

      # masterブランチの場合だけherokuへpush
      - deploy:
          name: Deploy Master to Heroku
          command: |
            if [ "${CIRCLE_BRANCH}" == "master" ]; then
              git push heroku master
            fi


```

シェルスクリプトが使えるしsshでコンテナに入ることも出来るし頑張れば何でも出来そうですｗ

### 9.2. Slack
SlackとのインテグレーションはWebUIの設定画面にあるChat Notificationsから簡単にできます。

以上で一通り動きますのでプロジェクトによって色々カスタマイズしていければなと思います。