【CI/CD】RailsアプリケーションにCI/CDを構築

# 0. 前提
- Dockerで構築したRailsアプリケーションをHerokuでデプロイしていること

https://qiita.com/rexid/items/9ba6b1bb338a3e2b5d8b

手動デプロイを毎回行うのが面倒なので、**CI/CDを導入しデプロイを自動化する**

## CIとは
- 継続的インテグレーション（Continuous Integration）
    - ソースコードを変更してGitHubにアップする度にビルドやテストを自動実行し、コードの品質を一定に保つ方法

## CDとは
- 継続的デリバリー（Continuous Delivery）
    - テストを通った変更→レビューOK→マージされたらデプロイを自動的に行う仕組み

## CircleCIとは
- CI/CDを構築するためのサービス
    - ビルド・テスト・デプロイをCloud上で行ってくれる

# 1. コードをGitHubにアップする
- Git
    - コードはGitでバージョンを管理
        - 元に戻せる
        - 変更履歴が分かる
- GitHub
    - Gitで管理しているソースコードをオンライン上で共有するためのサービス

---
**GitHub上でリポジトリ作成**
- GitHubのYour Repositoriesにアクセス→newボタン押下
- Ownerを選択し、Repository nameには[ここ](https://qiita.com/rexid/items/9ba6b1bb338a3e2b5d8b)までで作ってきたHerokuのアプリケーション名を入力
- Publicを選択し、Create Repositoryボタン押下

---
**Gitでコードを管理**
```terminal
$ cd ~/Documents/rails_docker/ # アプリケーションディレクトリに移動
$ git init # Gitの管理下（リポジトリ）におくコマンド
```
`リポジトリ`・・・ディレクトリやファイルの状態を記録する場所

---
**`.gitignore`ファイルを修正**
- `.gitignore`・・・バージョン管理する必要の無いファイルをGitのバージョン管理下から外すことができる
    - データベースの接続先情報など、GitHubにアップすると情報漏洩の危険性があるもの
    - `bundle install`でダウンロードしてくるファイルのコードも変更履歴を管理する必要がない
    - `.gitignore`ファイルに管理不要なファイルを指定しておくことで、そのファイルをバージョン管理から外すことができる
    - `.gitignore`ファイルはRailsがデフォルトで用意してくれている

**1. `.gitattributes`と`.gitignore`の2ファイルが現在srcファイル内にあるが、プロジェクトのディレクトリ直下に移動させる**
    ```terminal
    $ mv src/.gitignore .
    $ mv src/.gitattributes .
    ```

**2. `.git`を削除**
- `git init`をすると作られるが、こいつがsrcディレクトリ配下にもあり二重管理でエラーになってしまうので`.git`ファイルを削除する
```terminal
$ rm -rf src/.git/
```
- `-rf`・・・指定したディレクトリ以下全部のファイルを消す

**3. ファイルを修正**
- すべてにsrcを付ける
    - srcディレクトリに入ってしまっている為、ディレクトリの構造を合わせる
- `src/db/mysql_data`も管理下から外す
    - 	mysqlの実行中のデータで、ソースコードとは関係がないファイルのため`.gitignore`に追記する
    - 	`src/db/mysql_data`は`docker-compose`ファイルのvolumesで指定している

```:.gitignore
src/.bundle

src/log/*
src/tmp/*
!src/log/.keep
!src/tmp/.keep

src/tmp/pids/*
!src/tmp/pids/
!src/tmp/pids/.keep

src/storage/*
!src/storage/.keep

src/public/assets
src/.byebug_history

src/config/master.key

src/public/packs
src/public/packs-test
src/node_modules
src/yarn-error.log
src/yarn-debug.log*
src/.yarn-integrity
```

**4. add, commit, push**

```terminal:例
$ git status
$ git add .
$ git commit -m "first commit"
$ git branch -M main
$ git remote add origin https://github.com/Ryo-exid/rails-docker-gucci.git
$ git push -u origin main
```
:::note warn
pushの際にUsernameとPasswordの入力を求められる場合、Passwordにはアクセストークンを入力すよう注意する
:::


**<INS>GitHubへのアップ完了！</INS>👏**

# 2. CI
**テストコードを記載**

```rb:src/test/controllers/homes_controller_test.rb
require "test_helper"

class HomesControllerTest < ActionDispatch::IntegrationTest
  test "the truth" do
    assert true
  end
end
```

---
**CircleCIに登録**

https://circleci.com/ja/signup/

:::note info
「GitHubで登録」を選ぶ
:::

- プロジェクトの登録も一緒に行う

---
**configを設定**

- プロジェクトディレクトリ直下に`.circleci`ディレクトリを作成し、その配下に`config.yml`を作成
```yml:.circleci/config.yml
version: 2.1
orbs:
  ruby: circleci/ruby@1.1.2
  heroku: circleci/heroku@1.2.3

jobs:
  build:
    docker:
      - image: circleci/ruby:2.7
    working_directory: ~/rails-docker-gucci/src
    steps:
      - checkout:
          path: ~/rails-docker-gucci
      - ruby/install-deps

  test:
    docker:
      - image: circleci/ruby:2.7
      - image: circleci/mysql:5.5
        environment:
          MYSQL_ROOT_PASSWORD: password
          MYSQL_DATABASE: app_test
          MYSQL_USER: root
    environment:
      BUNDLE_JOBS: "3"
      BUNDLE_RETRY: "3"
      APP_DATABASE_HOST: "127.0.0.1"
      RAILS_ENV: test
    working_directory: ~/rails-docker-gucci/src
    steps:
      - checkout:
          path: ~/rails-docker-gucci
      - ruby/install-deps
      - run:
          name: Database setup
          command: bundle exec rails db:migrate
      - run:
          name: test
          command: bundle exec rake test

workflows:
  version: 2
  build_and_test:
    jobs:
      - build
      - test:
          requires:
            - build
```

- **解説**
    - `orbs:`は事前に定義した`jobs:`をシェアできるもの
    - 今回は`build:`と`test:`という`jobs:`を定義
    - `workflows:`で今回`build_test_and_deploy:`というものを作っている
    - 構造としては`workflows:`が中心で、`workflows:`で定義された順番に`jobs:`の処理を行う

---
**`database.yml`のhostの値を書き換える**

- CircleCIだとdocker-composeを使っていない為、`'db'`という指定の仕方ができない
    - 環境変数のAPP_DATABASE_HOSTに値がsetされていたらそのままhostの値として使い、なかった時はdbを使用
    - ローカルではdocker-composeを使用する為、dbを取るようにしている

```yaml:database.yml
test:
  <<: *default
  database: app_test
  host: <%= ENV.fetch("APP_DATABASE_HOST") { 'db' } %>
```

---

**環境変数を設定**
- configディレクトリ配下に`master.key`があるのでそのファイルの中身の値を環境変数として設定
    - ソースコードを動かす上で必要な値が記されているが、流出するとまずいため`.gitignore`に記載されている
```terminal
$ heroku config:add RAILS_MASTER_KEY='master.keyから値をコピペ' -a アプリ名
```

---
**GitHubにプッシュ**
```terminal:例
$ git switch -c ci
$ git branch # ciブランチにいることを確認
$ git add .
$ git commit -m "CircleCIが動くように、configファイルを設定"
$ git push origin ci
```

---
- main←ciでプルリクを作成し、テストが通ったらマージ
![スクリーンショット 2022-09-23 18.45.24.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2689074/c90db2c3-7d93-67bf-5024-972b61960c51.png)

- mainブランチにpull
```terminal
$ git switch main
$ git pull origin main
```

**🎉🎉🎉pushされる度に自動テストが行われるCIが完成！！🎉🎉🎉**

# 3. CD

**トピックブランチ作成＆移動**
```terminal
$ git switch -c cd
$ git branch -d ci # ciブランチはもう使わないので削除
$ git branch # cdにいること
```

---
**configを修正**
```yml:.circleci/config.yml
version: 2.1
orbs:
  ruby: circleci/ruby@1.1.2
  heroku: circleci/heroku@1.2.3

jobs:
  build:
    docker:
      - image: cimg/ruby:2.7.5
    working_directory: ~/rails-docker-gucci/src
    steps:
      - checkout:
          path: ~/rails-docker-gucci
      - ruby/install-deps

  test:
    docker:
      - image: cimg/ruby:2.7.5
      - image: circleci/mysql:5.5
        environment:
          MYSQL_ROOT_PASSWORD: password
          MYSQL_DATABASE: app_test
          MYSQL_USER: root
    environment:
      BUNDLE_JOBS: "3"
      BUNDLE_RETRY: "3"
      APP_DATABASE_HOST: "127.0.0.1"
      RAILS_ENV: test
    working_directory: ~/rails-docker-gucci/src
    steps:
      - checkout:
          path: ~/rails-docker-gucci
      - ruby/install-deps
      - run:
          name: Database setup
          command: bundle exec rails db:migrate
      - run:
          name: test
          command: bundle exec rake test

  deploy:
    docker:
      - image: circleci/ruby:2.7.5
    steps:
      - checkout
      - setup_remote_docker:
          version: 19.03.13
      - heroku/install
      - run:
          name: heroku login
          command: heroku container:login
      - run:
          name: push docker image
          command: heroku container:push web -a $HEROKU_APP_NAME
      - run:
          name: release docker image
          command: heroku container:release web -a $HEROKU_APP_NAME
      - run:
          name: database setup
          command: heroku run bundle exec rake db:migrate RAILS_ENV=production -a $HEROKU_APP_NAME

workflows:
  version: 2
  build_test_and_deploy:
    jobs:
      - build
      - test:
          requires:
            - build
      - deploy:
          requires:
            - test
          filters:
            branches:
              only: main

```
- `orbs:`に`heroku: circleci/heroku@1.2.3`を追加
- `workflows`に`deploy:`を追加
- `jobs:`に`deploy`を追加

---
**環境変数を設定**
- circleciにアクセス
`Projects`→`rails-docker-gucci（今回のプロジェクト）`→`Project Settings`→`Environment Variables`→`Add Environment Variable`
    - config.ymlで使っていた`HEROKU_APP_NAME`を設定
![スクリーンショット 2022-09-23 18.55.33.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2689074/fbe72da3-1578-fe48-02ed-f7c6f93746bd.png)
    - <font color="Magenta">**動かす上でもうひとつ`HEROKU_API_KEY`も設定**</font>
    - Name：HEROKU_API_KEY
    - Value：[herokuにログイン](https://id.heroku.com/login)→`Account Settings`→少し下にスクロールし、`API Key`をコピーしてValueに貼り付け

---

**ローカルで起動するかどうか確認**
```terminal
$ docker-compose up -d
```
- http://localhost:3000/
    - 変更が上手く反映されていることを確認

---
**GitHubにプッシュ→プルリク作成→マージ**
```terminal
$ git add .
$ git commit -m "CD用の設定を追加"
$ git push origin cd
```

- マージされたらデプロイされているかどうかを確認
    - CircleCI→Dashboard→All Pipelines→rails-docker-gucci
![スクリーンショット 2022-09-23 19.03.38.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2689074/d4935ae6-ddb5-a2f8-6684-e2b863e69423.png)

:::note
CircleCI上記ページ上の`Rerun workflow from start` （こんな感じのボタン↩️）を押すともう一度build, test, deployが行われる
:::

:::note warn
CircleCIでdeployがSuccessになってもHerokuへの反映には時間がかかる
その際はログ出力コマンドで状況を確認することも可能
:::

- ログ出力コマンド

```terminal
$ heroku logs -t -a アプリ名
```

---

**ブラウザから確認**
```terminal
$ heroku open -a アプリ名
```

![スクリーンショット 2022-09-23 17.04.09.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2689074/8b0c9e9e-bc5c-7e0b-86c4-030049d6f7ae.png)

**🎉🎉🎉自動テスト＆自動デプロイ完了🎉🎉🎉**