Travis CI で "npm ci" コマンドを使う


npm ci とは

皆さんご存知かもしれませんが、 [email protected] から npm ci コマンドが使えるようになりました。

npm ci コマンドを簡単に説明すると、こんな感じです。

$ rm -rf node_modules && npm install

ただし、上記のコマンドと違って npm ci コマンドは package-lock.json ファイルを更新しません。
npm install コマンドは package-lock.json ファイルを更新することがあります)

package-lock.json ファイルに書かれてあるパッケージのみをインストールするので、テストやデプロイなどの再現性が要求される環境で非常に有用です。
また、npm cinpm install で実行されるいくつかのステップを省略しているので、高速化されています。

Travis CI での使い方

Travis CI は npm ci コマンドに公式に対応しています。以下、マニュアルからの抜粋です。

If a package-lock.json or npm-shrinkwrap.json exists and your npm version supports it, Travis CI will use npm ci instead of npm install.

もし package-lock.json または npm-shrinkwrap.json が存在し、あなたの npm バージョンがサポートしていれば、Travis CI は npm install の代わりに npm ci を使うでしょう。

Building a JavaScript and Node.js project - Travis CI

なので、最新の Node.js バージョンを Travis CI で実行するように指定していれば、 .travis.yml ファイルに特に何も書かなくても npm ci が使われます。

キャッシュ

さらに、キャッシュの設定をすれば Travis CI のビルドを速くすることができます。

.travis.yml
cache:
  directories:
    - "$HOME/.npm"

この方法は npm ci ドキュメントに記載してあります。

Travis CI のドキュメントには、少し違った方法が記載されていますが、

.travis.yml
cache:
  directories:
    - "node_modules"

npm ci コマンドは node_modules ディレクトリを削除するので、このキャッシュ指定はあまり意味がないです。

より簡単な方法(2018-11-08)

以下のようなシンプルな方法がサポートされていました!

.travis.yml
cache: npm

This caches $HOME/.npm precisely when npm ci is the default script command. (See above.)

参考: https://docs.travis-ci.com/user/languages/javascript-with-nodejs/#caching-with-npm

npm のバージョンが古いとき

現在の Node.js 8.x 最新版では、npm のバージョンが少し古いです。その場合、npm ci が使えないので、before_install セクションで最新の npm をインストールするよう設定します。

.travis.yml
before_install: npm i -g npm@latest

もし複数バージョンの Node.js を使用している場合は、npm のバージョンが古い場合にだけ npm のアップデートすることをおすすめします。
(npm のアップデートはネットワークアクセスを発生させるので、その分ビルド時間が遅くなります)

.travis.yml
node_js:
  - 10
  - 8

before_install: if [[ $TRAVIS_NODE_VERSION == 8 ]]; then npm i -g npm@latest; fi

[[ $TRAVIS_NODE_VERSION == 8 ]] の部分は、以下のような npm バージョンのチェックをした方がより確実なのですが、

if [[ $(npm -v) < 5.7 ]]; then ...; fi

npm -v5.7.0 のような semver を返すので、単純な Bash ワンライナーでは実現できませんでした。
今後なにか良い方法が見つかれば、この部分を更新します。

特別な方法が不要に(2018-11-18)

現在の Node.js 8.x 環境では、デフォルトで npm ci コマンドをサポートした npm がインストールされるようになりました。
したがって、 npm i -g npm@latest コマンドを実行する必要はありません!

Ruby を使う(2018-06-08)

Travis CI には Ruby がデフォルトでインストールされているので、 ruby コマンドのワンライナーで semver の比較を行うことができました。

if ruby -e "exit(1) if Gem::Version.new(ARGV[0]) >= Gem::Version.new('5.7.0')" $(npm -v); then
  npm i -g npm@latest;
fi

実際の .travis.yml の設定は、こうなります。

.travis.yml
before_install:
  # `npm ci` is available since [email protected]
  - if ruby -e "exit(1) if Gem::Version.new(ARGV[0]) >= Gem::Version.new('5.7.0')" $(npm -v); then
      npm i -g npm@latest;
    fi

https://travis-ci.org/ybiquitous/ybiq/jobs/389717667#L443

https://travis-ci.org/ybiquitous/ybiq/jobs/389717668#L443

実際の使用例

私の個人プロジェクトでの使用例です。

.travis.yml
language: node_js

node_js:
  - 10
  - 8

cache:
  directories:
    - "$HOME/.npm"

before_install:
  # `npm ci` is available since [email protected]
  - if [[ $TRAVIS_NODE_VERSION == 8 ]]; then npm i -g npm@latest; fi

script:
  - commitlint-travis
  - npm test

以下のビルドログを見ると、npm ci が実際に使われていることが分かります。

Node 10:
https://travis-ci.org/ybiquitous/ybiq/jobs/387010580#L449

Node 8:
https://travis-ci.org/ybiquitous/ybiq/jobs/387010581#L453

まとめ

CI では npm install の代わりに npm ci を積極的に使っていきましょう!