Gruntによる継続的なビルド環境を求めて 〜 package.jsonと0.4.0のこと

安定したビルド環境

夏前に、nodeでビルドってなんかナウい(∩´∀`)∩ワーイって使い始めて、秋から現職のプロジェクトで実践してみた結果、そんな当たり前な視点を忘れないようにしなければ、と強く思った次第。

今回は下記の2点を紹介します。

  • Gruntと永く付き合うためのノウハウとして、package.jsonを使った管理について
  • 賞味期限の短いノウハウとして、Grunt 0.4.0への移行に関して

Gruntイイヨーの続きとして、今後付き合っていくために必要なことを改めておさらい。

1. package.jsonを利用してビルド環境を管理

gruntというよりもnpmの話ですが、package.jsonにビルド環境がどのように構成されているかを必ず記録しておくべきです。Gruntで使っているプラグイン(npmからインストールしたタスク類)の依存情報を保存しておくと、ビルド環境をnpm linkで便利に初期化できます。

# package.jsonでビルド環境を管理しているプロジェクトのrepoをclone
% git clone git@github.com:ahomu/hoge-project.git ./

# ビルド環境を初期化
% npm link

このようにすると、リポジトリで管理されたpackage.jsonを元に、依存パッケージを読み取って./node_modules以下に自動でインストールして環境を初期化してくれます。

では、package.jsonを作成して管理してみましょう。

1-1. package.jsonの作成

npm initで対話的に作成することもできますが、ここでは以下のような内容のテキストをpackage.jsonとして保存することにします。

{
  "name": "プロジェクト",
  "description": "プロジェクトのビルド環境定義",
  "version": "1.0.0",
  "author": "あほむ(∩´∀`)∩だよ",
  "dependencies": {

  },
  "devDependencies": {

  }
}

これは最小限のテンプレで、ただの下地作りです。プロジェクト名や説明などは、自分の環境に合わせて書き換えましょう。

1-2. 依存パッケージの記録

上に示したpackage.jsonのテンプレにもありますが、jsonを見ると、dependenciesやdevDependenciesなどの項目があります。大まかには以下のような感じです。

  • dependencies - パッケージ(≒プロジェクト)の実行に必要なパッケージの定義
  • devDependencies - パッケージの開発に必要なパッケージの定義
  • optionalDependencies - インストールできる限り使うパッケージの定義

開発中のプロジェクトが、npmのパッケージとして成立していない(nodeプロジェクトでない)のであれば、dependenciesのみに記録しても大丈夫です。

nodeプロジェクトだったり、よりお行儀良く管理するのであれば、gruntのプラグインなど開発環境系は、devDependenciesとして管理すると良いでしょう。

これから依存パッケージの情報を保存していきますが、手動でパッケージ名やバージョン番号を書く必要はありません。gruntのプラグインをインストールするときに、下記のようなオプションをつけてください。

# devDependencies に grunt-contrib-coffee への依存情報を保存
% npm install --save-dev grunt-contrib-coffee

# dependencies に grunt-contrib-stylus への依存情報を保存
% npm install --save grunt-contrib-stylus

npm install時に適切なオプションをつけるだけで、package.jsonへの保存が行われます。

# devDependencies に grunt-contrib-coffee への依存情報を削除
% npm remove --save-dev grunt-contrib-coffee

# dependencies から grunt-contrib-stylus への依存情報を削除
% npm remove --save grunt-contrib-stylus

npm removeで削除するときも同様のオプションをつければ、package.jsonから削除されます。

1-3. gitのリポジトリから直接入れるとき

ここまでは依存関係について説明を行いましたが、同様にGitHubで公開されている最新のバージョンや、開発中ブランチのプラグインを直接利用したい場合もあると思います。ここでは、それらの指定方法について説明します。

例えば「gruntjs/grunt-contrib-stylus · GitHubのmasterバグってるので、自分でforkした修正版のdevelブランチに依存したい!」場合はこのようにします。

% npm install --save-dev git://github.com/ahomu/grunt-contrib-stylus.git#devel

簡単ですね。パッケージ名の代わりに、gitのリポジトリを指定すればOKです。#以降がなければ順当にmasterあたりがインストールされますが、#develのようにブランチ名を指定すれば、特定のブランチをパッケージとして取得できます。

{
  "name": "プロジェクト",
  "description": "プロジェクトのビルド環境定義",
  "version": "1.0.0",
  "author": "あほむ(∩´∀`)∩だよ",
  "devDependencies": {
    "grunt-contrib-stylus": "git://github.com/ahomu/grunt-contrib-stylus.git#devel"
  }
}

package.jsonには、こんな感じで記録されます。#のところにはブランチ名だけでなく、コミットハッシュを直接指定しても大丈夫です。詳しくはドキュメントを参照のこと。

grunt-contrib-stylusの名誉のために補足すると、本家のdevelブランチに最近、クールなPull Requestが取り込まれたので、じきに恥ずかしいバグが修正されたバージョンがnpmにも登録されるはず。きっと。

1-4. grunt本体のバージョンを特定しておきたいとき

稀かもしれませんが、たとえば先取りしてgrunt@0.4.0aに依存している場合、どこに示しておくべきかという問題が残ります。grunt本体はグローバルにインストールすべきパッケージなので、dependencies類だとちょっと違います。

で、以下のような対処が妥当かな、という所。

  1. README.mdに書いておく(ノーマル)
  2. package.jsonに項目をねつ造して無理やり記録する
"otherDependencies": {
  "grunt": "git://github.com/cowboy/grunt.git#9d966f189870e2d3f7d4a1e1a2d12d9974731a4c"
}

後者のねつ造だと、こんな感じですね。本当にこの微妙なリビジョンに依存していた時期がありました…。

2. v0.4.0へのバージョンアップに備える

ぼちぼち落ち着くと思うのですが、既存のgruntは以下のように分割してリポジトリ管理されるようになり、パッケージ構成に変化が訪れています。

  • grunt - Gruntのコアファイル
  • grunt-cli - コマンドラインインターフェース部分
  • grunt-contrib-xyz - ビルトインタスクのgrunt-contrib-xyz化

ちょうど過渡期ということもありますが、この波を乗り切らなければ既存の0.3系のGruntで構成されたビルド環境は、そのままメンテされない廃屋と化してしまうかもしれません。こまる。

変更点

ということで設定ファイルとタスクの中身について、変更を追いつかせていきましょう。

現在masterブランチにある0.3系から、もうじき移行するであろう0.4系からのUpgradingについては、上記のWikiに示されています。際どい変更だけピックアップしてみましょう。

Gruntfile.jsに設定ファイルがリネーム
されました。そんだけ。Gruntfile.coffeeでも大丈夫です。
grunt.registerHelperの廃止
バッサリと使えなくなりました。タスク側の修正自体は容易なのですが、野良タスクがメンテされないまま放置された場合、それらの多くはnpm上でパッケージ名だけ占有しながら腐り果てる惨事へ。
grunt.registerTaskのエイリアス記法が変更
grunt.registerTask('default', ['jshint', 'nodeunit', 'concat']);という感じで、第2引数が配列になってます。
コンフィグの記述変更
<config:prop.subprop> → <%= prop.subprop %>
<json:file.json> → grunt.file.parseJSON('file.json')
<file_template:file.js> → grunt.template.process(grunt.file.read('file.js'))
それぞれ変更されました。特殊な記述が減って、JSとして素直になって良い。
その他タスク内APIの変更
grunt-contrib-coffeeが、grunt-lib-contribの後方互換用APIを利用しつつうまくAPI変更に対応しているので参考にすると良さそう。

以上のあたりを踏まえていけば、大体は移行できるはずです。

0.3から0.4へのアップデートには、非常に重要で大きい変更が多く含まれます。これが0.4から0.5になるときも訪れるならちょっと考え直した方が良いのかしら…と思わないでもないわけですが、もうしばらく辛抱強く見守っていきたい所存。

余談ですが、registerHelperについては何事もなく利用できるfallback用のパッケージとかできませんかね。ヘルパ関係のAPIをそれっぽいfunctionで埋めてあげれば、野良タスクくらいは何とかなるんじゃないかと。

そんなかんじで

ビルド環境は継続的なメンテナンスが必要であり、複数人でプロジェクトにあたっているのであれば正しく共有されるべきということで、この辺りもぜひ参考にしていただければ幸い。