---
title: Concourse CIを使ってSpring Bootのバージョンアップを自動化する
tags: ["Concourse CI", "Spring Boot"]
categories: ["Dev", "CI", "ConcourseCI"]
date: 2019-03-17T18:18:09Z
updated: 2019-03-18T01:57:40Z
---

ソフトウェアのバージョンアップの追従は、一つ一つはそれほどの作業でなくとも、対象の数が増えてくると大変です。
更新作業自体もそうですが、バージョンをアップをウォッチし続けるというのも言うほど容易ではないです。
気づいたら使っているバージョンが大分古くなってしまっていた、というのはよくありえるケースだと思います。

ソフトウェアの日頃のバージョンアップを疎かにすると、いざ本当にバージョンアップが必要なタイミングでジャンプアップが必要になって多大な労力をかけることになりえます。
昨今はソフトウェアのバージョンアップは頻繁で、これらについていくには自動化が必要です。

この記事ではSpring BootのバージョンアップをConcourseを使って自動化する方法を紹介します。汎用的な方法なので、Spring Bootのバージョンアップ以外にも応用可能です。

バージョンアップの対象アプリはこちらです。

[https://github.com/making/hajiboot-security](https://github.com/making/hajiboot-security)

Concourseを使って、Spring Bootのバージョン更新を検知し、PullRequestを送信し、PullRequestに対してユニットテストを行うというパイプラインを構築します。

<!-- toc -->

### 自動PullRequestの作成

Github APIを使ってPullRequestを送るスクリプトはこちらです。

[https://github.com/making/ci-utils/blob/master/scripts/generate-pr.sh](https://github.com/making/ci-utils/blob/master/scripts/generate-pr.sh)

このスクリプトを使って、Spring Bootの新しいバージョンがリリースされたらPullRequestを送るようにします。

Spring Bootの新しいバージョンのチェックを行うには

* [Github Release Resource](https://github.com/concourse/github-release-resource) (GithubのReleaseから最新バージョンを取得)
* [Maven Resource](https://github.com/nulldriver/maven-resource) (Maven Repositoryから最新バージョンを取得)
* [Dynamic Metalink Resource](https://github.com/dpb587/dynamic-metalink-resource) (任意の方法(スクレイピング等)で最新バージョンを取得)

のどちらかで行えます。実際に新しいバージョンが利用可能になるのはMaven Centralに公開後なので、Maven Resourceでバージョンをチェックした方が確実です。

#### pipeline.ymlの作成

自動更新対象のプロジェクトに`ci`ディレクトリを作って`pipeline.yml`を作成します。

```
mkdir ci
touch ci/pipeline.yml
```

`pipeline.yml`に次の内容を記述してください。

```yaml
resource_types:
# Maven Centralに公開されているSpring Bootの最新バージョンを取得するため、3rd partyリソースのMaven Resourceを使用
- name: maven
  type: docker-image
  source:
    repository: nulldriver/maven-resource
    tag: 1.3.6
resources:
# バージョン更新対象のレポジトリ(自分のレポジトリに変更してください)
- name: repo
  type: git
  source:
    uri: git@github.com:making/hajiboot-security.git
    private_key: ((github-private-key))
# PullRequestを送るスクリプトを含むユーティリティ
- name: utils
  type: git
  source:
    uri: https://github.com/making/ci-utils.git
    branch: master
# Spring Bootの最新バージョンを含むMavenレポジトリ上のファイル(チェック間隔は30分に1回にする)
- name: spring-boot
  type: maven
  check_every: 30m
  source:
    url: https://repo1.maven.org/maven2
    artifact: org.springframework.boot:spring-boot-dependencies:pom
jobs:
- name: check-spring-boot-version
  plan:
  - aggregate:
    - get: pom
      resource: spring-boot
      trigger: true
    - get: repo
    - get: utils
  - task: update-pom
    params:
      GIT_EMAIL: ((git-email))
      GIT_NAME: ((git-name))
      GIT_SSH_KEY: ((github-private-key))
      GITHUB_API_TOKEN: ((github-access-token))
    config:
      platform: linux
      image_resource:
        type: registry-image
        source:
          repository: maven
      inputs:
      - name: pom
      - name: repo
      - name: utils
      outputs:
      - name: updated-repo
      run:
        path: bash
        args:
        - -c
        - |
          set -e
          # Pull Requestを送る関数を定義したスクリプトを読み込み
          source utils/scripts/generate-pr.sh
          # 現在使用使用しているバージョン
          CURRENT_VERSION=`grep -A 1 spring-boot-starter-parent repo/pom.xml | grep version | sed 's|<[/]*version>||g' | sed 's/ //g' | tr -d "\t"`
          # 最新のバージョン
          NEW_VERSION=`ls pom/*.pom | sed 's|pom/spring-boot-dependencies-||' | sed 's|.pom||'`
          echo "Current: $CURRENT_VERSION"
          echo "New    : $NEW_VERSION"
          cd repo
          # pom.xmlを更新
          sed -i "s/$CURRENT_VERSION/$NEW_VERSION/g" pom.xml
          git diff | cat
          # Pull Request送信 (自分のレポジトリに変更してください)
          # generate_pull_request <pull reqを送るユーザー名> <更新されたコンポーネント名> <新しいバージョン> <owner名/repo名> <PR対象のbranch名>
          generate_pull_request "making-bot" "spring-boot" "${NEW_VERSION}" "making/hajiboot-security" "master"
```

#### credentials.ymlの作成

`ci/credentials.yml`を作成して、パラメータを定義します。

```yaml
git-email: makingx+bot@gmail.com
git-name: CI Bot
github-private-key: |
  -----BEGIN RSA PRIVATE KEY-----
  **** ~/.ssh/concourseの内容 ****
  -----END RSA PRIVATE KEY-----
github-access-token: ********************************
```

`github-private-key`と`github-access-token`は次の手順で取得します。

##### Concourseからgit pushするためのdeploy keyを作成

```
ssh-keygen -t rsa -f ~/.ssh/concourse
```
キーフレーズは空にしてください(エンターを2回押す)。

`~/.ssh/concourse.pub`をGithubのDeploy Keyに登録してください。書き込み権限を与えることを忘れないでください。

![image](https://user-images.githubusercontent.com/106908/54494423-7c4fe200-491d-11e9-9e06-86f5aa88ba43.png)

`~/.ssh/concourse`の内容を`credentials.yml`に設定してください。

##### Github APIのアクセストークンを取得

[https://github.com/settings/tokens](https://github.com/settings/tokens)からpersonal access tokenを作成してください。"Generate new token"ボタンをクリックしてトークンを生成してコピーし、`credentials.yml`に設定してください。

![image](https://user-images.githubusercontent.com/106908/54494483-19127f80-491e-11e9-987a-040accdc394b.png)

`repo`スコープをつけてください。

![image](https://user-images.githubusercontent.com/106908/54495443-0b152c80-4927-11e9-8aaa-67a3c39c4265.png)

#### Pipelineの設定

前提として`fly login`済み。target名は何でも良いですが、説明上は`making`をtarget名にします。

パイプラインの設定とアンポーズします。

```
fly -t making sp -p hajiboot-security -c ci/pipeline.yml -l ci/credentials.yml 
fly -t making up -p hajiboot-security
```

しばらくすると、`check-spring-boot-version`ジョブがTriggerされます。

![image](https://user-images.githubusercontent.com/106908/54494302-fed7a200-491b-11e9-9c0b-65cd12ce0502.png)


![image](https://user-images.githubusercontent.com/106908/54494752-f5046d80-4920-11e9-87fa-5ca2d2784b54.png)

成功するとPullRequestが送られます。

![image](https://user-images.githubusercontent.com/106908/54494761-11080f00-4921-11e9-89fb-40a1b7d8b83a.png)

![image](https://user-images.githubusercontent.com/106908/54494910-6a247280-4922-11e9-9d65-e09bac1c3638.png)

実際のPullRequestはこちらです。

[https://github.com/making/hajiboot-security/pull/1](https://github.com/making/hajiboot-security/pull/1)

今後もSpring Bootがバージョンアップする度にPullRequestが自動で送られます。

### PullRequestに対してユニットテストを実行

次に送られてきたPullRequestに対してユニットテストを実行します。

PullRequestのチェックには[Github PR resource](https://github.com/telia-oss/github-pr-resource)が使えます。

`pipeline.yml`を次のようにアップデートしてください。

```yaml
resource_types:
- name: maven
  type: docker-image
  source:
    repository: nulldriver/maven-resource
    tag: 1.3.6
# PullRequestの3rd Party Resource
- name: pull-request
  type: docker-image
  source:
    repository: teliaoss/github-pr-resource
    tag: v0.11.0
resources:
- name: repo
  type: git
  source:
    uri: git@github.com:making/hajiboot-security.git
    private_key: ((github-private-key))
- name: utils
  type: git
  source:
    uri: https://github.com/making/ci-utils.git
    branch: master
- name: spring-boot
  type: maven
  check_every: 30m
  source:
    url: https://repo1.maven.org/maven2
    artifact: org.springframework.boot:spring-boot-dependencies:pom
# PullRequestの定義
- name: repo-pr
  type: pull-request
  check_every: 10m
  source:
    repository: making/hajiboot-security
    access_token: ((github-access-token))
jobs:
- name: check-spring-boot-version
  plan:
  - aggregate:
    - get: pom
      resource: spring-boot
      trigger: true
    - get: repo
    - get: utils
  - task: update-pom
    params:
      GIT_EMAIL: ((git-email))
      GIT_NAME: ((git-name))
      GIT_SSH_KEY: ((github-private-key))
      GITHUB_API_TOKEN: ((github-access-token))
    config:
      platform: linux
      image_resource:
        type: registry-image
        source:
          repository: maven
      inputs:
      - name: pom
      - name: repo
      - name: utils
      outputs:
      - name: updated-repo
      run:
        path: bash
        args:
        - -c
        - |
          set -e
          source utils/scripts/generate-pr.sh
          CURRENT_VERSION=`grep -A 1 spring-boot-starter-parent repo/pom.xml | grep version | sed 's|<[/]*version>||g' | sed 's/ //g' | tr -d "\t"`
          NEW_VERSION=`ls pom/*.pom | sed 's|pom/spring-boot-dependencies-||' | sed 's|.pom||'`
          echo "Current: $CURRENT_VERSION"
          echo "New    : $NEW_VERSION"
          cd repo
          sed -i "s/$CURRENT_VERSION/$NEW_VERSION/g" pom.xml
          git diff | cat
          generate_pull_request "making-bot" "spring-boot" "${NEW_VERSION}" "making/hajiboot-security" "master"
# Pull Requestのブランチに対してユニットテストを実施するジョブ
- name: unit-test-pr
  plan:
  - aggregate:
    - get: repo
      resource: repo-pr
      trigger: true
  # テスト実施中はPullRequestをpending状態にする
  - put: repo-pr
    params:
      path: repo
      status: pending
  - task: mvn-test
    config:
      platform: linux
      image_resource:
        type: registry-image
        source:
          repository: openjdk
          tag: 11-slim
      inputs:
      - name: repo
      caches:
      - path: repo/m2
      run:
        path: bash
        args:
        - -c
        - |
          set -e
          cd repo
          rm -rf ~/.m2
          ln -fs $(pwd)/m2 ~/.m2
          ./mvnw test
    on_success:
      do:
      # テスト成功後はPullRequestをsuccess状態にする
      - put: repo-pr
        params:
          path: repo
          status: success
    on_failure:
      do:
      # テスト失敗後はPullRequestをsuccess状態にする
      - put: repo-pr
        params:
          path: repo
          status: failure
```

パイプラインを更新します。

```
fly -t making sp -p hajiboot-security -c ci/pipeline.yml -l ci/credentials.yml 
```

しばらくすると`unit-test-pr`ジョブが

![image](https://user-images.githubusercontent.com/106908/54495304-0308bd00-4926-11e9-9a14-e889df0c0b47.png)


テスト中はPullRequestがPendingになります。

![image](https://user-images.githubusercontent.com/106908/54495575-27fe2f80-4928-11e9-9b24-ed56ba2be02f.png)

テストが成功すれば、PullRequestがSuccessになります。

![image](https://user-images.githubusercontent.com/106908/54495781-65fc5300-492a-11e9-96a3-5d2fc875ec23.png)

![image](https://user-images.githubusercontent.com/106908/54495751-16b62280-492a-11e9-9774-30ed74f17f29.png)

Github上でMergeすればバージョンアップ完了です。

Merge後ブランチ（ここでは`master`）も当然テストしてください。

PullRequestに対するテストtaskと`master`ブランチに対するテストtaskは同じになるので、YAMLのAnchor & Aliasが利用できます。

`pipeline.yml`は次のようになります。

```yaml
configs:
  mvn-test: &MVN_TEST_CONFIG
    platform: linux
    image_resource:
      type: registry-image
      source:
        repository: openjdk
        tag: 11-slim
    inputs:
    - name: repo
    caches:
    - path: repo/m2
    run:
      path: bash
      args:
      - -c
      - |
        set -e
        cd repo
        rm -rf ~/.m2
        ln -fs $(pwd)/m2 ~/.m2
        ./mvnw test
resource_types:
- name: maven
  type: docker-image
  source:
    repository: nulldriver/maven-resource
    tag: 1.3.6
- name: pull-request
  type: docker-image
  source:
    repository: teliaoss/github-pr-resource
    tag: v0.11.0
resources:
- name: repo
  type: git
  source:
    uri: git@github.com:making/hajiboot-security.git
    private_key: ((github-private-key))
- name: utils
  type: git
  source:
    uri: https://github.com/making/ci-utils.git
    branch: master
- name: spring-boot
  type: maven
  check_every: 30m
  source:
    url: https://repo1.maven.org/maven2
    artifact: org.springframework.boot:spring-boot-dependencies:pom
- name: repo-pr
  type: pull-request
  check_every: 10m
  source:
    repository: making/hajiboot-security
    access_token: ((github-access-token))
jobs:
- name: check-spring-boot-version
  plan:
  - aggregate:
    - get: pom
      resource: spring-boot
      trigger: true
    - get: repo
    - get: utils
  - task: update-pom
    params:
      GIT_EMAIL: ((git-email))
      GIT_NAME: ((git-name))
      GIT_SSH_KEY: ((github-private-key))
      GITHUB_API_TOKEN: ((github-access-token))
    config:
      platform: linux
      image_resource:
        type: registry-image
        source:
          repository: maven
      inputs:
      - name: pom
      - name: repo
      - name: utils
      outputs:
      - name: updated-repo
      run:
        path: bash
        args:
        - -c
        - |
          set -e
          source utils/scripts/generate-pr.sh
          CURRENT_VERSION=`grep -A 1 spring-boot-starter-parent repo/pom.xml | grep version | sed 's|<[/]*version>||g' | sed 's/ //g' | tr -d "\t"`
          NEW_VERSION=`ls pom/*.pom | sed 's|pom/spring-boot-dependencies-||' | sed 's|.pom||'`
          echo "Current: $CURRENT_VERSION"
          echo "New    : $NEW_VERSION"
          cd repo
          sed -i "s/$CURRENT_VERSION/$NEW_VERSION/g" pom.xml
          git diff | cat
          generate_pull_request "making-bot" "spring-boot" "${NEW_VERSION}" "making/hajiboot-security" "master"
- name: unit-test-pr
  plan:
  - aggregate:
    - get: repo
      resource: repo-pr
      trigger: true
  - put: repo-pr
    params:
      path: repo
      status: pending
  - task: mvn-test
    config:
      <<: *MVN_TEST_CONFIG
    on_success:
      do:
      - put: repo-pr
        params:
          path: repo
          status: success
    on_failure:
      do:
      - put: repo-pr
        params:
          path: repo
          status: failure
# masterブランチに対してユニットテストを実施するジョブ
- name: unit-test-master
  plan:
  - aggregate:
    - get: repo
      trigger: true
  - task: mvn-test
    config:
      <<: *MVN_TEST_CONFIG
```

![image](https://user-images.githubusercontent.com/106908/54495842-166a5700-492b-11e9-97d6-c00176ad0e3b.png)

本記事のテーマから外れるので割愛しますが、test後はpackageとdeployを行ってください。
続きは[こちら](https://github.com/Pivotal-Japan/concourse-workshop)のハンズオン資料を見てください。

---

このブログのSpring Bootバージョンアップは↑のやり方で行っています。Spring Cloudのバージョンアップも行っているので参考にしてください。

[https://github.com/categolj/blog-api/blob/develop/ci/pipeline.yml](https://github.com/categolj/blog-api/blob/develop/ci/pipeline.yml)

自動化でバージョンアップの追従を楽にしてください。そしてバージョンアップの追従を諦めないでください。
