Travis で並列テストが終わってから deploy する

Travis では複数のジョブを並列で実行できる。複数の処理系のバージョンでテストを回したりするのはよく行われている。

また、Travis は後処理でデプロイも行える。専用の設定をする項目があり、一部の有名所のデプロイ先については雛形が用意されていてオプションの項目を埋めるだけだ。

私は今まで Travis から https://www.npmjs.com/ へ npm パッケージをデプロイする設定をしていたのだけど、普通に設定しているだけだと並列で実行されている全てのジョブがデプロイを実行するので、多重デプロイが発生していることに気付いた。

幸い www.npmjs.com は同じバージョンのデプロイをエラーとして弾いてくれていたのでデプロイ自体は問題なく行われていたが、いかんせん無駄だし、気持ち悪い。また、この設定だと一部のジョブでテストが失敗しても、どれか1つが成功してしまえばデプロイされてしまう。これは問題がある。

そこでなんとかする方法を調べた。

Build Stages

Travis には Build Stages というまさに今回の問題のための機能があり、これを使えば問題が解決することがわかった。

全てのジョブはいずれかのステージに紐付けられており、各ステージではステージ内の全てのジョブを並列に実行し、かつそれぞれステージはステージ内のジョブが全て成功してから次のステージに進む。

実は何もしなくても、通常の設定のジョブは全てデフォルトである test ステージに属していることになっている。

今まで行っていた設定は以下のような感じ(一部改変)。

language: node_js
node_js:
  - "node"
  - "6.0"
  - "6"
  - "7.0"
  - "7"

sudo: false

cache:
  directories:
    - node_modules

before_install:
  - npm install -g codecov

script:
  - npm run lint
  - npm run coverage  # テストしつつカバレッジを取る

after_success:
  - codecov

deploy:
  provider: npm
  skip_cleanup: true
  email: thinca+npm@gmail.com
  api_key:
    secure: {travis encrypt コマンドで設定した値}
  on:
    tags: true

これを以下のようにした。前半は全く同じで、最後だけ違う。

language: node_js
node_js:
  - "node"
  - "6.0"
  - "6"
  - "7.0"
  - "7"

sudo: false

cache:
  directories:
    - node_modules

before_install:
  - npm install -g codecov

script:
  - npm run lint
  - npm run coverage  # テストしつつカバレッジを取る

after_success:
  - codecov

jobs:
  include:
    - stage: npm release
      if: tag IS present
      node_js: "node"
      cache: {}
      before_install: skip
      install: skip
      script: skip
      after_success: true  # Don't skip: To trigger the deploy
      deploy:
        provider: npm
        email: thinca+npm@gmail.com
        api_key:
          secure: {travis encrypt コマンドで設定した値}
        on:
          tags: true

まず前提として、これは Git のタグを push した時にだけデプロイをする設定だ。if: tag IS present で、タグが存在する場合にのみこのジョブを実行する。このジョブは stage: npm release によって npm release ステージに属しており、これはデフォルトの test ステージの次に実行される。

jobs.include 以下の各ジョブは、トップレベルの設定を引き継ぐ。しかし、デプロイ時に再度テストを実行する意味はない。そこで各ステップを skip として飛ばすようにしている。この辺りは、テストの設定も jobs.include に書いてしまえば解決するのかもしれないけど、試していない。

そして次がハマったところで、どうも after_successskip してしまうと、デプロイ処理自体行われなくなってしまう模様。ドキュメントにも特に記載が見つけられず、かなり試行錯誤した。なのでここは skip せず、適当に何もせず成功するコマンド、今回の場合は true コマンドでトップレベルの設定を上書きした。

という感じで、無事Travis から npm パッケージのリリースに成功した。

これで今後は npm version patch 等でバージョンコミット&タグを作成したあと git push origin master --tags でタグを push するだけで npm パッケージがリリースできる。ラクチン。