だれも聞いていないと思って歌え

dance as if no one’s watching, sing as if no one’s listening, and live everyday as if it were your last.

Circle CI 2.0 へ移行してみた

爆速と噂の CircleCI 2.0 の β が取れたので、テストに時間が掛かっていたこともあり早速移行してみました。
結論を先に言うと、テスト時間が半分に短縮されました。

Circle CI とは

GitHub と連携させることで、リモートリポジトリへの Push を検知してテストの自動実行などを行う Web サービスです。
設定を行うことで、静的解析、自動ビルド、自動デプロイなども行うことができます。
この他にも Jenkins 、 Travis CI などの CI ツールがあります。

2.0 になることで何が変わったか

engineer.crowdworks.jp

上記記事から引用させていただきました。

カスタムビルドイメージ

1.0では多くの言語やツールがあらかじめインストールされたUbuntuイメージをCIのビルドイメージとして利用します。しかし、イメージに含まれていないものを利用する場合はCIの中でインストールする必要がありました。 2.0ではDockerイメージをベースにしてCIを実行できるようになりました。これにより、必要なライブラリなどをあらかじめセットアップしたDockerイメージを利用できるようになります。

ネイティブDockerのサポート

1.0でもDockerサポートは存在しています。しかし、CircleCI自身がベースコンテナとしてLXCを利用していることの影響で、独自パッチを当てた古いバージョンに限定されています。1 2.0では仮想マシンが起動して純粋なDockerを動かせるようになったため、最新の機能がフルに利用できるようになりました。CIのベースとしてこの仮想マシンを起動することも、Dockerイメージをベースにしつつ、途中でこの仮想マシンを起動することもできます。2

柔軟なジョブ構成

1.0では自動的にプロジェクトの種別を推測して、最適なセットアップとテストのコマンドが実行されます。CIの実行は明確なフェーズに分かれており、キャッシュをリストア・保存するタイミングも、コマンドを実行するタイミングも固定されています。実行コマンドをカスタマイズしたい場合はフェーズごとにコマンドを上書きしたり、フェーズの前後にコマンドを差し込んだりします。 2.0ではプロジェクト種別の推測はされず、決められたフェーズもなくなり、自分でステップを定義します。また、キャッシュも任意のタイミングで柔軟に行えるようになりました。

こちらの 柔軟なジョブ構成 の変更により、設定ファイルの書き方が大きく変わりました。

移行してみた

このあたりを参考にしました。

circleci.com

circleci.com

github.com

Circle CI 1.0 では、 PJ 直下にこのような circle.yml ファイルを置いていました。

machine:
  timezone: Asia/Tokyo
  node:
    version: 6.11.1
  post:
    - npm install -g npm@4
test:
  pre:
    - mkdir -p $CIRCLE_TEST_REPORTS/reports
    - eslint --config .eslintrc.yml src --format=junit --output-file $CIRCLE_TEST_REPORTS/reports/eslint.xml
  override:
    - karma start --reporters junit

Circle CI 2.0 では、 .circleci/config.yml へ設定を書くように変更になりました。

version: 2

jobs:
  build:
    docker:
      - image: node:6.11.1
    steps:
      - checkout
      - run:
          name: update npm
          command: |
            mkdir -p ~/tmp
            cd ~/tmp
            npm install npm@4
            rm -rf /usr/local/lib/node_modules
            mv node_modules /usr/local/lib/
            cd
      - restore_cache:
          key: dependency-cache-{{ checksum "package.json" }}
      - run: npm install
      - save_cache:
          key: dependency-cache-{{ checksum "package.json" }}
          paths:
            - ./node_modules
      - run:
          name: lint
          command: |
            mkdir -p /reports
            ./node_modules/.bin/eslint --config .eslintrc.yml src --format=junit --output-file ./reports/eslint.xml
      - run:
          name: test
          command: ./node_modules/.bin/karma start --grep ''
          environment:
            JUNIT_REPORT_PATH: ./reports
      - store_test_results:
          path: ./reports
      - store_artifacts:
          path: ./reports

変更点にもあった 最適なセットアップとテストのコマンド である checkout やテストの実行も package.json の設定を解析して自動的に実行していました。(上記の場合はレポートへの出力を行うため override していますが、出力しない場合は override は不要です)
しかし、それらを全て自分で定義する必要があるため、同じことをさせるだけでも設定内容は増えています。

Node.js のマルチバージョンビルドを行うときなどは、利便性はかなり向上しているように思います。

teppeis.hatenablog.com

躓いたところ

node.js と npm のバージョンの指定

サンプルのように Docker Image に circleci/node:latest と設定すると使用できる node.js のバージョンが v8 となります。
ただし、 v6 を使いたかったので circleci/node:6circleci/node:6.11.1 と設定するとエラーが発生。

#!/bin/bash -eo pipefail
$ npm install
module.js:471
    throw err;
    ^

Error: Cannot find module 'semver'
    at Function.Module._resolveFilename (module.js:469:15)
    at Function.Module._load (module.js:417:25)
    at Module.require (module.js:497:17)
    at require (internal/module.js:20:19)
    at Object.<anonymous> (/usr/local/lib/node_modules/npm/lib/utils/unsupported.js:2:14)
    at Module._compile (module.js:570:32)
    at Object.Module._extensions..js (module.js:579:10)
    at Module.load (module.js:487:32)
    at tryModuleLoad (module.js:446:12)
    at Function.Module._load (module.js:438:3)
Exited with code 1

次に、Docker Image を CicleCI が公開しているものではなく node が公開している node:6.11.1 を使用しても内容は違うけれどエラーが発生して npm install が失敗……

#!/bin/bash -eo pipefail
$ sudo npm install -g npm@4
/bin/bash: sudo: command not found
Exited with code 127

sudo が付いていたりいなかったりするのは、苦戦していた結果です。

なかなか原因と解決方法が分からず諦めかけていましたが、同僚も一緒に調べてくれてその結果からアドバイスをもらい npm のインストール方法を変更しました。

- run: 
    name: update npm
    commands: | 
      cd ~
      npm install npm@4
      rm -rf /usr/local/lib/node_modules
      mv node_modules /usr/local/lib/

自分では気付けなかったのですが、 npm の issue に対策とともに公開されていました。

github.com

レポート出力

1.0 では $CIRCLE_TEST_REPORTS という環境変数があらかじめ用意され、こちらを利用することにより Test Summary へ表示を行えるようになります。 ↓こっちでやりました。

38fanjia.hatenablog.com

ただし、 2.0 ではこの環境変数はなくなりました。出力先を自分で指定し、 reports と artifacts のパスを指定します。

- run:
    name: lint
    command: |
      mkdir -p /reports
      ./node_modules/.bin/eslint --config .eslintrc.yml src --format=junit --output-file ./reports/eslint.xml
- store_test_results:
    path: ./reports
- store_artifacts:
    path: ./reports

これで、これまでと同じようにエラー結果と結果のファイルを参照できるようになりました。

circleci.com