CircleCI 2.0におけるGitタグ・ジョブ実行

CircleCI 1.0では正常動作したGitタグ・ジョブ実行が、CircleCI 2.0ではうまく動きませんでした。どうにかしようと四苦八苦したログをここに残します。

# 結論

GitタグをpushしてもCircleCIジョブが起動しませんでした。なので、masterブランチにpushされたときに最後のGitタグを取得してデプロイ処理を実行するようにしました。

動作確認用に[u6k/sample-circleci-2.0](https://github.com/u6k/sample-circleci-2.0)リポジトリを作成しましたので、こちらの`.circleci/config.yml`をご覧ください。

# Gitタグ・ジョブ実行の関連スレッド

CircleCIコミュニティや公式ドキュメントの関連スレッドを挙げます。

- [Git tag deploys in 2.0 - CircleCI 2.0 / 2.0 Support - CircleCI Community Discussion](https://discuss.circleci.com/t/git-tag-deploys-in-2-0/9493/24)

このスレッドによると、2017/8にGitタグ・ジョブ実行が有効になったとのこと。

- [Orchestrating Workflows - CircleCI](https://circleci.com/docs/2.0/workflows/#git-tag-job-execution)

「Git Tag Job Execution」にジョブ(というかワークフロー)の設定方法が説明されていますが、この通りに設定してもそもそもGitタグのpushでCircleCIジョブが起動しませんでした。

# `config.yml`の説明

GitタグのpushでCircleCIジョブが起動しないので、しかたないのでmasterブランチのpushで必要な処理を実行するようにしました。以下の`config.yml`が設定例です。

```
version: 2
jobs:
    build:
        docker:
            - image: docker:17.07.0-ce-git
        steps:
            - checkout
            - setup_remote_docker:
                version: 17.07.0-ce
            - run:
                name: Build docker image
                command: docker build -t u6kapps/sample-circleci-2.0 .
            - run:
                name: Test docker image
                command: docker run u6kapps/sample-circleci-2.0
            - run:
                name: Save docker image
                command: |
                    mkdir -p ~/caches
                    docker save u6kapps/sample-circleci-2.0 -o ~/caches/image.tar
            - save_cache:
                key: docker-{% raw %}{{ .Revision }}{% endraw %}
                paths:
                    - ~/caches/image.tar
    push:
        docker:
            - image: docker:17.07.0-ce-git
        steps:
            - checkout
            - setup_remote_docker:
                version: 17.07.0-ce
            - restore_cache:
                key: docker-{% raw %}{{ .Revision }}{% endraw %}
                paths:
                    - ~/caches/image.tar
            - run:
                name: Load docker image
                command: docker load -i ~/caches/image.tar
            - run:
                name: Push docker image
                command: |
                    TAG=`git describe --abbrev=0`
                    docker login -u ${DOCKER_USER} -p ${DOCKER_PASS}
                    docker tag u6kapps/sample-circleci-2.0 u6kapps/sample-circleci-2.0:${TAG}
                    docker push u6kapps/sample-circleci-2.0
workflows:
    version: 2
    build-and-push:
        jobs:
            - build
            - push:
                requires:
                    - build
                filters:
                    branches:
                        only: master
```

要所を説明します。

```
docker:
    - image: docker:17.07.0-ce-git
```

ジョブで使用するDockerイメージを指定します。この時、`-git`のイメージを使用しないと、`git`コマンドが使用できません。

```
- setup_remote_docker:
    version: 17.07.0-ce
```

`setup_remote_docker`ステップは隔離されたDocker環境を構築するためのステップです。ここでバージョンを指定しない場合、`docker image`で指定したバージョンとは異なるデフォルトのバージョン(17.03?)が割り当てられ、multi stage buildが使えません。

なので、無難に`docker image`で指定したバージョンと同じバージョンを指定しておきます。

```
- run:
    name: Save docker image
    command: |
        mkdir -p ~/caches
        docker save u6kapps/sample-circleci-2.0 -o ~/caches/image.tar
- save_cache:
    key: docker-{% raw %}{{ .Revision }}{% endraw %}
    paths:
        - ~/caches/image.tar
```

```
- restore_cache:
    key: docker-{% raw %}{{ .Revision }}{% endraw %}
    paths:
        - ~/caches/image.tar
- run:
    name: Load docker image
    command: docker load -i ~/caches/image.tar
```

ジョブをまたいだファイルの使用は、キャッシュを経由する必要があります。なので、ビルド・ジョブでDockerイメージをtarに出力してキャッシュを保存、pushジョブでキャッシュからtarを取得してDockerイメージを復元します。

```
- run:
    name: Push docker image
    command: |
        TAG=`git describe --abbrev=0`
        docker login -u ${DOCKER_USER} -p ${DOCKER_PASS}
        docker tag u6kapps/sample-circleci-2.0 u6kapps/sample-circleci-2.0:${TAG}
        docker push u6kapps/sample-circleci-2.0
```

CircleCI 1.0では`${CIRCLE_TAG}`という環境変数にGitタグが設定されていましたが、`env`コマンドで確認したところGitタグのような環境変数はありませんでした。しかたないので`git describe`で最後のGitタグを取得して、それをDockerタグに設定します。

```
workflows:
    version: 2
    build-and-push:
        jobs:
            - build
            - push:
                requires:
                    - build
                filters:
                    branches:
                        only: master
```

pushジョブをmasterブランチの時のみ実行するように設定しています。CircleCI 1.0の時はGitタグがpushされた時のみ、という設定ができましたが。

# 実際の動作

developブランチとmasterブランチでファイルを修正してGit pushしたところ、CircleCIでは以下のように実行されました。

![circleci-001](/assets/img/2017-10-03-circleci-2.0-git-tag-job-execution/circleci-001.png)

developブランチのビルドではbuildジョブだけ動作していることに対して、masterブランチの場合はbuildジョブとpushジョブが動作しました。

![circleci-002](/assets/img/2017-10-03-circleci-2.0-git-tag-job-execution/circleci-002.png)

pushジョブでは最後のGitタグを取得して`docker push`が成功していることが分かります。

# おわりに

CircleCI 2.0にするだけでビルドが早くなった！という記事をよく見ますが、まだ落とし穴が多く、移行はもう少し様子を見てからにします。

# Link

- Author
    - [u6k- GitHub](https://github.com/u6k)
    - [u6k.Blog](https://blog.u6k.me/)
    - [u6k - Qiita](https://qiita.com/u6k)
- Source
    - [2017-10-03-circleci-2.0-git-tag-job-execution.md](https://github.com/u6k/blog/blob/master/_posts/2017-10-03-circleci-2.0-git-tag-job-execution.md)