SpringアプリケーションをCircleCIでCI/CDする(Heroku)

## 概要
初投稿です。
SpringアプリケーションをCircleCIを使ってCI/CDしてみたいと思います。デプロイ先にはHerokuを使用します。

## 環境
- Java 11
- Spring Boot 2.2.8

### 前提条件
- GitHubのアカウントがあり、CLIからpushなどができる
- Herokuのアカウントがあり、CLIからcreateなどができる

## Spring プロジェクトの作成

まず[Spring Initializr](https://start.spring.io/)を使用してSpringアプリケーションを作成します。Springアプリケーションの雛形を作成してくれる便利なサイトです。

以下の画像のように設定をして`GENERATE`をクリックします。`Dependencies`に`Spring Web`を追加することを忘れないでください。
![Spring Initializr](https://i.gyazo.com/3451108b156c48bfa1e9fe5d86f404e8.png)

`demo.zip`というようなファイルがダウンロードされるので好きな場所に展開してください。

## テスト作成
次にシンプルなControllerの実装と、テストを作成します。

まず、`demo/src/main/java/com/example/demo/controller/HelloController.java`を作成します。

```java
package com.example.demo.controller;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class HelloController {

    @GetMapping(path = "/hello")
    public String hello() {
        return "hello";
    }

}
```
とてもシンプルな、`/hello`にアクセスすると`"hello"`と返してくれるだけのControllerです。

次に、`demo/src/test/java/com/example/demo/controller/HelloControllerTest.java`を作成します。

```java
package com.example.demo.controller;

import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.web.context.WebApplicationContext;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;

@SpringBootTest
@AutoConfigureMockMvc
class HelloControllerTest {
    @Autowired
    private MockMvc mockMvc;

    @Autowired
    WebApplicationContext webApplicationContext;

    @BeforeEach
    void setUp() {
        this.mockMvc = MockMvcBuilders.webAppContextSetup(webApplicationContext).build();
    }

    @Test
    void helloページにアクセスしたらhelloが返ること() throws Exception {
        final var mvcResult = this.mockMvc.perform(get("/hello"))
                .andDo(print()).andExpect(status().isOk()).andReturn();
        String content = mvcResult.getResponse().getContentAsString();
        assertEquals(content, "hello");
    }
}
```
先ほどのシンプルなControllerを`MockMvc`というライブラリを使用してテストしています。環境が整っている方はここでテストを実行すると`GREEN`になるはずです。

## CIできるようにする
続いて、先ほど作成したテストをGitHubにPushするたびにCircleCIで自動で走るようにしていきます。`.circleci/config.yml`を作成します。

```yml
version: 2.1

workflows:
  java-ci:
    jobs:
      - test

jobs:
  test:
    docker:
      - image: circleci/openjdk:11-jdk

    working_directory: ~/repo
    environment:
      JVM_OPTS: -Xmx3200m
      TERM: dumb

    steps:
      - checkout
      - restore_cache:
          keys:
            - v1-dependencies-{{ checksum "build.gradle" }}
            - v1-dependencies-
      - run: gradle dependencies
      - save_cache:
          paths:
            - ~/.gradle
          key: v1-dependencies-{{ checksum "build.gradle" }}
      - run: ./gradlew test
```
`circleci/openjdk:11-jdk`のimageを使用して`./gradlew test`を走らせるようにしています。

GitHub上にRepositoryを作成して、ここまでの変更をPushします。

続いて、[CircleCIのLoginページ](https://circleci.com/vcs-authorize/)から`Log In With GitHub`を選択してLoginします。GitHubのアカウント情報を入力した後に`Select an organization`の画面が表示されたら、自分のGitHubアカウントを選択します。

`Projects`のページが表示されたら、今回作成したRepositoryの`Set Up Project`を選択します。以下のような画面が表示されたら、今回はすでに`.circleci/config.yml`を作成してあるので、`Add Manually`を選択します。
![Add Manually](https://i.gyazo.com/7ea4af5bfc420db6d6d44431bb0ac174.png)

これで`Pipelines`のページに遷移し、テストが実行されればCIは完了です。

CircleCIのビルドの始め方の詳しい情報は[公式ページ](https://circleci.com/docs/2.0/getting-started/)も参考にしてみてください。

## CDできるようにする
最後に、アプリケーションを継続的にデプロイし続けられるようにしていきます。`.circleci/config.yml`を以下のようにします。[circleci/heroku](https://circleci.com/orbs/registry/orb/circleci/heroku)というOrbsを使用してHerokuにデプロイできるようにします。

```yml
version: 2.1

orbs:
  heroku: circleci/heroku@1.0.1

workflows:
  java-ci-cd:
    jobs:
      - test
      - start_deploy:
          type: approval
          requires:
            - test
      - heroku/deploy-via-git:
          requires:
            - start_deploy

jobs:
  test:
    docker:
      - image: circleci/openjdk:11-jdk

    working_directory: ~/repo
    environment:
      JVM_OPTS: -Xmx3200m
      TERM: dumb

    steps:
      - checkout
      - restore_cache:
          keys:
            - v1-dependencies-{{ checksum "build.gradle" }}
            - v1-dependencies-
      - run: gradle dependencies
      - save_cache:
          paths:
            - ~/.gradle
          key: v1-dependencies-{{ checksum "build.gradle" }}
      - run: ./gradlew test
```

次にHeroku側の作成を行います。今回作成しているSpringアプリケーションのルートディレクトリでHerokuのAppsを作成します。

```console
$ heroku create
```
createの後に任意の文字列を渡してお好きなドメインにしても構いません。

これによって作成された`https://xxx.herokuapp.com/`の`xxx`の部分をメモしておきます。
また、[HerokuのAccountページ](https://dashboard.heroku.com/account)の真ん中辺りにある`API Key`の値もメモしておきます。

この2つの値を、CircleCIの`Environment Variables`に設定します。
`Pipelines `のページから、`Project Settings`→`Environment Variables`です。以下の画像のように、ドメインの1部分を`HEROKU_APP_NAME`として、API Keyの値を`HEROKU_API_KEY`として追加します。
![add env](https://i.gyazo.com/ab07c249b96b0063edba52c2330e3715.png)

![add env](https://i.gyazo.com/f1e149a158bc2333afbc8ceb28fc8821.png)

最後に、Heroku用の設定ファイルを追加します。

ルートディレクトリに`Procfile`を追加します。

```
web: java -jar build/libs/demo-0.0.1.jar --server.port=$PORT
```

さらに、ルートディレクトリに`system.properties`を追加します。

```
java.runtime.version=11
```

これでここまでの差分をGitHubにPushします。

すると`Pipelines`のページでtest成功後にJobが止まるので、`On_hold`を選択して`Approve Job`をクリックするとデプロイが走ります。
![onhold](https://i.gyazo.com/7800a77ec90d126cade0e2f641321797.png)

最終的に`heroku/deploy-via-git`までSuccessになればCDは完了です。

[こちら](https://github.com/projectormato/spring-hello)に今回作成したコードがありますので良ければ参考にしてみてください。