developとトピックブランチだけで運用してたらmasterを作ってなかったので作った

GitHubを使った開発フローで、本流であるdevelopと、プルリクを送るためのトピックブランチを使ってサイクルを回していました。

しばらくコミットを重ね、ようやくリリースに至り、masterブランチにdevelopをマージしようとしたところ、masterブランチを作ってなかったことに気づきました。

そんな時にやった手順。

$ git clone リポジトリのURL
$ git rm {すべてのファイル・フォルダ}
$ git checkout --orphan master
$ git commit --allow-empty "first commit"
$ git push origin master
  • 作業場所と別の場所にリポジトリをcloneします。
  • checkout時に --orphan オプションをつけると、空ブランチを作ることができます。
    ただ、既存(コミット済み)のファイルはunstageなファイルとして残るので、ブランチ作成前に削除しています(別の場所にcloneしたのはこのため)
  • 空コミットは --allow-empty オプションをつけて作成します。

改めてブログ記事にするとなんか面倒なことをしてる気もしますが、ともかくこれで、 "first commit" という1つの空コミットだけがある master ブランチをリモートに作成できます。

--orphan オプション便利ですね。GitHub Pagesを作る時なんかにも役立ちそうです。

Boot Campで起動したWindows 7上でUSBメモリが認識しない

(注)プログラムと関係ないですが、ハマった話を書いておきます。

タイトルの通りで、Boot Campで起動したWindows 7上でUSBメモリが認識しない現象に遭遇しました。

この手の症状は検索すると色んな対処法が出てくるのですが、どれを試しても解決に至らず困っていたのですが、ようやく解決しました。

環境

現象

  • USBメモリを挿しても、コンピュータのハードディスク一覧に表示されない
  • [コンピュータの管理] → [ディスクの管理] で見ても、ボリュームが表示されていない
  • 手持ちのUSBメモリ何本かで試したが、症状は同じ(=USBメモリの問題ではない)
  • USB HDD自体は振動しているので、電気信号は通っている模様
  • USBメモリを挿すポートを変えても、症状は同じ
  • デバイスマネージャのユニバーサルシリアルバスコントローラを確認しても、異常なし
  • Windows 7をインストールしてからずっと同じ症状

試したけど効果がなかったこと

原因と対処

結局ドライバの問題でした…。

  1. デバイスマネージャの表示がデフォルトで、「デバイス(種類別)」になっているので、「デバイス(接続別)」に表示を切り替え
  2. 「ACPI x64-based PC」→「PCIバス」→「インテル(R) USB 3.0 eXtensibleホスト・コントローラー」→「インテル(R) USB 3.0 ルートハブ」と辿る
  3. すると「不明なデバイス」が存在(!)
  4. 上記デバイスのプロパティを開き、ドライバーを更新(最新版を自動検索)

ドライバーの更新が終わると、ドライバーインストール後にUSBメモリが認識されるようになりました。よかったよかった。
デバイスマネージャの表示を切り替えないと「不明なデバイス」が現れないのが罠ですね。

GitHub FlowとPull Request駆動開発をYWTでふりかえり

最近関わったプロジェクトでは、GitHub FlowとPull Requestベースの開発を行いました。
個人的には、まともにこのスタイルで開発するのは初めてだったということもあり、YWT(やったこと、わかったこと、次にやること)形式でふりかえった結果を紹介していきます*1

モダンな開発現場では当たり前に行われていることなので、「今更?」と思うかもしれませんが、「本やWebを真似しても、小難しいコマンドがたくさん出てくるばかりで、本当に開発効率が上がってるのか分からない゚・(ノД`)・゚・。」みたいな人もいるのではと思います(私がそうでした)。

ちなみに、プロジェクト開始前の自分のGit/GitHubスキルは以下のとおりでした。

  • GitHubは使っていたが、一人でmasterブランチに対して「add->commit->push」を行うだけ
  • 書籍「GitHub実践入門」を通読した

というわけで、「実践」してみて分かった内容として、重箱の隅っぽいところを紹介していきたいと思います。
なお本エントリはGitやGitHubGitHub Flowそのものについての解説記事ではないのであしからず。。

以下、プルリクエストはPRと略します。

Y: やったこと

Y1) ごく普通のGitHub Flow

  • masterブランチとPR用のトピックブランチのみの運用
  • masterに対してpushは厳禁
  • トピックブランチはレビュー後にmasterにマージ

また、hubSourceTreeなどは使わず、Git CLIGitHubのUIのみ使用。

Y2) レビュー依頼はチャットで

PRを誰がレビューするかは、GitHubのAssigneeは使わずに、HipChatでメンションする形式でした。
規模が小さいから可能な運用かもしれないです。

Y3) PullRequestはフォーマットを決めて

テンプレートについては、『Pull Request のフォーマットを決めるとレビューの効率が3倍よくなる(Crocos Engineering Blog)』を参考にしました。

テンプレート自体は社内のWikiにあって、コピペして、PRを送信する、という流れです(記事と同じですね)。

開発当初は、上記の記事で紹介されているフォーマットをほぼそのまま使っていました。
ただ、テンプレートを埋める時間が結構な手間になっていたので、最終的には以下の通り、かなりシンプルなテンプレートになりました。

## 概要
## 詳細(主に技術的変更点)
## 使い方・確認方法(設定変更やコマンドが必要ならばそれも書く)
## 保留した項目・TODOリスト
## その他(レビューで見てもらいたい点、不安な点、参考URLなど)

Y4) レビューする時は git stash で今の作業を退避させる

プロジェクトの特性上、レビューはGitHub上でdiffを眺めるだけでなく、手元で動かして確認することが多かったです。

$ git stash
$ git checkout master
$ git fetch
$ git checkout feature-to-review
  • git stash
    • 作業中ブランチの変更を一時的に退避
  • git checkout master
    • masterブランチに移動(1つ前のブランチに戻るのと同じことなので git checkout - でも可)。
  • git fetch
  • git checkout feature-to-review
    • レビュー対象のブランチ(feature-to-review)に移動

これで手元にレビュー対象のコードが置かれたことになるので、実際に動かして確認します。実際に動かすために、PRのテンプレートにある「使い方・確認方法」の項目が役立ちます。
もちろん、動くだけでなくコードそのもののレビューも一緒に行います。

レビューが終わったら、感謝と賞賛の気持ちを込めて LGTM :+1: し、以下の通り、元の作業に戻ります。

$ git checkout my-feature
$ git stash apply
  • git checkout my-feature
    • 作業中ブランチ(my-feature)に戻る。
  • git stash apply
    • 退避した内容を戻す。

Y5) マージはGitHub画面から

トピックブランチのマージは画面から行います。マージするのはレビュアーでなく依頼者(コード書いた人)。

GitHubの画面からマージできない=コンフリクトが起きている、なので、マージする人の責任でコンフリクトを解消します。

Y6) PullRequestのラベルを活用

なるべくPRがクローズされるまでのサイクルは短くすべきですが、それでも溜まる時は溜まってしまいます。

一覧で見た時に状況がぱっと見で分かるように、ラベルを活用しました。
作ったラベルは以下です。

  • WIP
  • レビュー待ち
  • 緊急・重要
  • 緊急・重要じゃない
  • 緊急じゃない・重要
  • 緊急じゃない・重要じゃない

下4つは「7つの習慣」にある時間管理マトリクスからですね。

Y7) コマンドでGitを使う場合、git-prompt, git-completionが便利

コマンドでGitを操作していると、以下のあたりが作業効率を下げます。

  • git status を何度も使う。
  • 作業ブランチ名を確認するために、 git branch を何度も使う。
  • トピックブランチの名前はなるべく特徴を表す名前にするため、ブランチに移動するのに長いブランチ名を毎回入力する。

git-prompt, git-completionを使うと、この辺の問題を解決できます。 調べるといっぱい出てくるので、手順は割愛しますが、要するに、プロンプトに各種情報・作業ブランチ名を表示させたり、ブランチ名で補完を効かせる仕組みです。

W: わかったこと

W1) レビューする側の心理

なるべく早くレビューしてあげたいというのが人の心理だと思いますが、レビュー依頼を受けてすぐレビューすると、コンテキストスイッチが発生します。
そこで、毎日この時間(昼休み明け)にレビューする、みたいに自分ルールを決めてレビューするのがよいかなと思います。 「☓☓はいついつにレビューします」「△△は急ぎでレビューしてください」などを朝会・夕会で共有するのも重要ですね。

W2) Gitコマンドは意味を確認しながら動かすと理解が深まる

ネットに載っているコマンドをそのまま打っているだけの時はなかなかGitの操作に馴染めなかったですが、一つ一つ調べながら確認していくと理解が深まっていったと思います。

W3) レビュー中のトピックブランチから次のトピックブランチを作ることの是非

レビュー中のトピックブランチ b1 がある時に、b1 の変更を元にしたトピックブランチ b2 を開発していきたい、というケースでどうするのがベストなのか、悩ましい問題でした。

b1 のレビューで指摘が入ると、b2 側でもそれに伴って rebase/merge したり、修正自体が無駄になることがありますよね。

そもそも、 b1のレビューが完了し、 b1 -> master へのマージが行われて b1 がリモートリポジトリから削除されると、b1 から派生した b2 はどうなるんだ?みたいな問題があります。

先に b2 -> master ないしは b2 -> b1 へのPRを作ってレビューしてもらう、というのも変ですしね。

割とよくあるケースな気もするのですが、他のプロジェクトってどうしてるんでしょう?

T: 次にやること(やりたいこと)

T1) PRの状況をHubotで通知

PRの状況を見に行くのに、毎回GitHubの画面にアクセスしなくても済むように、Hubot等で通知してくれるようにしたいと思います。

T2) PRの作成を楽にする

やったことで述べたとおり、PRのフォーマットは社内のWikiからコピペしていたのですが、これが結構面倒でした。

リンククリックなど、ワンステップでテンプレート付きのPRが作成できるようにしたいです。

GitHub Issueの場合、これが可能です。  https://github.com/{account}/{project}/issues/new?title=aaa&body=bbb みたいなURLで、クエリパラメータにタイトルや本文を渡せるので、これをプロジェクトの README.md に書いておけば、そのリンクをクリックしてテンプレート付きのIssueが作成画面に遷移できるようになります。

同じことをPRでもしたいのですが、PRの場合、URLにマージ元ブランチ名が必要であり、そのブランチ名が分からないので、決まったURLを使えないのですよね。

調べた感じだと、ChromeのExtensionや、hubコマンドで使える仕組みがあるみたいですが。。

おわりに

というわけで、実践してみて色々とわかったことがありました。

  • レビュー中のトピックブランチから次のトピックブランチを作りたい場合の運用
  • ワンステップでテンプレートからPRを作成する方法

上記2点をはじめ、今後も改善しながら続けていきたいです。

*1:念のためですが、プロジェクトのふりかえりも開発チーム内で行いました。

IonicとCordovaのバージョンについて整理してみる

ここ最近はIonicを使ったハイブリッドアプリを開発してました(個人でなく、会社でのプロジェクトです)。

さて、こちらの記事の通り、Cordova Android脆弱性が見つかり、修正版がリリースされました。

IonicではCordovaのAPIを利用してネイティブの機能を使うので、今のプロジェクトでも「脆弱性の影響ないかな?」「そもそも、今のCordovaのバージョンなんだっけ?」と思ったのですが、調べていく過程で色々と混乱してしまっていたので、整理がてらまとめておきます。

Cordova iOS, Cordova Androidのバージョンを調べる

最初に結論なんですが、Ionic(Cordova)プロジェクト内のCordovaのバージョンを調べたい場合、プロジェクトの直下で

# cordova platform list

とすればよいです(Ionicの場合はionic platform listでもOKですね)。

サンプル出力は以下。

Installed platforms: android 4.0.2, ios 3.8.0
Available platforms: amazon-fireos, blackberry10, browser, firefoxos

Cordova Androidが4.0.2、Cordova iOSが3.8.0であることが分かります。

紛らわしいなーと感じた点は、あるハイブリッドアプリについて、以下のバージョンがどれも違うところです(カッコ内は手元の環境におけるバージョンです)

そのため、本エントリにおける以下の説明ではこれらは使い分けていきます。

ionicとionic-cliのバージョンは別物

バージョンを調べたい時は、慣れで

# ionic --version

としてしまいますが、ここで返ってくる結果はionicではなく、ionic-cliのバージョンです。
手元の環境だと1.4.5という結果が返ってきますが、Ionic自体はつい先日1.0がリリースされたばかりなので、Ionicが1.4.5というのはおかしいですしね。

ionicの諸々のバージョンを調べたい場合は、

# ionic info

とすると、以下のような結果が返ります。

Your system information:

Cordova CLI: 5.0.0
Ionic Version: 1.0.0
Ionic CLI Version: 1.4.5
Ionic App Lib Version: 0.0.22
ios-deploy version: Not installed
ios-sim version: Not installed
OS: Mac OS X Mavericks
Node Version: v0.12.4
Xcode version: Xcode 6.2 Build version 6C131e

通常、Ionicはグローバルにインストール( npm install -g ionic )すると思いますが、これでインストールされるのは、正確にはionicではなくionic-cliのようです。

ionic-cliはその名の通りコマンドラインツールであり、ionic-cliにおけるモジュールの依存関係的には ionic-cli -> ionic-app-lib -> cordova-lib となっていて、ionicには依存しません。
ionic-app-libはプロジェクトのジェネレータ、cordova-libはCordovaモジュールです。
というわけで、ionicとionic-cliは直接の依存関係はないし、cordova-libとCordova iOS/Cordova Androidにも直接の依存関係はない、ようです。

じゃあ、Ionicのバージョンはいつ、どこで決まってるのか?

…という疑問が湧いてきたので調べました。

どうやら、ionic startを実行する時(=プロジェクトを作成する時)に決まり、{PROJECT_ROOT}/www/lib/ionic/version.json で定義されるようです*1

流れとして、Ionicプロジェクトを作る場合、ionic start コマンドで雛形を生成することが多いと思いますが、プロジェクトのテンプレートに依らず、基底の雛形である ionic-app-baseというのがいて、その中でIonicのバージョンが指定されています。

つまり、ionic-cliをバージョンアップしなくても、ionic start を実行した時点で最新のIonicに基づくプロジェクト雛形が生成される、ようです(試してないので確証はないですが…)

なお、既存のプロジェクトのIonicをバージョンアップしたい場合は、プロジェクトで

# ionic lib update

とします。この辺りを知っておけば、プロジェクト作成後もionicのバージョンを自分達で管理することができます。

Cordova iOS, Cordova Androidのインストール

各プラットフォーム(Cordova iOS, Cordova Android)は、

# cordova platform add ios
# cordova platform add android

を実行することで、 platforms の下に ios, android ディレクトリが作られ、インストールされます。

プロジェクトのプラットフォーム情報は、package.json および platforms/platforms.json に書き込まれます。

package.json では以下のようにインストールされているプラットフォームが列挙されています(抜粋)。

"cordovaPlatforms": [
  "ios",
  "android"
 ]

platforms/platforms.json には以下のように、プラットフォームとバージョンが記載されています。

{
    "ios" : "3.8.0",
    "android" : "4.0.0"
}

自分達の環境では、IonicプロジェクトをGitでバージョン管理しているのですが、platforms 以下はダウンロードされている依存ライブラリということで .gitignore してます。
インストールするためには

# ionic state restore

とすると、package.json の中身を見てプラットフォームやプラグインをよしなにインストールしてくれます。

バージョンを指定してCordova iOS, Cordova Androidをインストール

2015.5.30時点で、 ionic platform add android を実行しても、Cordova Androidのバージョンとして(リリースされたばかりの)4.0.2ではなく4.0.0がインストールされていまいました…。

そこで、4.0.2をインストールするべく、明示的にバージョンを指定してインストールします。
なお、すでにプラットフォームがインストールされている場合は、一度削除してから追加した方が無難です。

# ionic platform rm android
# ionic platform add android@4.0.2

これでめでたくCordova Android 4.0.2が入りました。package.json

"cordovaPlatforms": [
  "ios",
  {
    "platform": "android",
    "version": "4.0.2",
    "locator": "android@4.0.2"
  }
]

となります(抜粋)。

まとめ

用途 コマンド
ionic, ionic-cli, etcのバージョン確認*2 ionic info
Cordovaのバージョン確認 ionic platform list
ionicのバージョンアップ ionic lib update
Cordovaのバージョンアップ ionic platform rm {ios|android} (インストール済の場合のみ)
ionic platform add {ios|android}@{version}

*1:version.json自体は単なるJSONファイルであり、プロジェクトにおけるIonic本体は、{PROJECT_ROOT}/www/lib/ionic 以下に配置されています。

*2:Ionicプロジェクト以外のディレクトリで実行すると、ionicのバージョンは表示されません。グローバルにインストールされているionic-cli等のバージョンだけが確認できます。

社内LTでゲームフレームワークPhaserについて話をしました

久しぶりのエントリになってしまいました。内容はどうあれアウトプットも大事にしていきたいのですが、ここ最近は社内のBacklogに閉じてしまっているのが悩ましいところです。

さて先日、社内でLT大会がありました。
テーマに縛りがなかったため、色んな話があって盛り上がり、面白かったです。

そこで自分はゲームフレームワークPhaserフェイザー)について話しました。
持ち時間は5分間なので、簡単に紹介して面白いよー、って話だったんで、内容は薄めです。そもそも最近ちょっと手を出しただけなので、深いことを話す知識は持ち合わせていないのですが…。

スライドは以下のURLで公開してます(実際に使ったものから少し修正を加えています)。

http://tq-jappy.github.io/slides/phaser.html

今回、新しい試みとして、プレゼンのスライド自体もPhaserを使って作りました(動作確認はChromeでのみ行っています)。

簡単な操作メモ。

ページ 操作 内容
全体 [←]キー 前のスライド
[→]キー 次のスライド
P.6 アニメーション [H][J][K][L]キー ガイコツが歩きます
(viのキーバインドと同じです)
[Z]キー かぼちゃの回転速度を上げる
(押す度に高速回転します)
[X]キー かぼちゃの回転速度をリセット
P.7 物理エンジン [Z]キー 物理エンジンを適用

※ スライド中の画像は公式のロゴ以外、ぴぽや倉庫様のものを使わせていただきました。

Phaser自体がHTMLとJavaScriptさえあれば動く環境なので、スライドもHTMLとJavaScriptで作っておくと、スライドとデモをシームレスに見せることができ、なおかつ、いつでも誰でもデモをその場で試せるのがポイントですね。
かなり昔のRuby会議で星一さんがStar Rubyについて話をした時に、スライドをご自身のゲームライブラリであるStar Rubyで作っていて、面白い手法だなぁと思っていたので、一度やってみたいと思っていたのでした。

ただ、急ぎで作ったのでバグが結構あります。

時間内に話さなかったことをQA形式にて。

Q. UnityとかCocos2d-xとかenchant.jsにしなかったのはなぜ?
A. 各サイトをざっと見て、Phaserが一番楽しそうだったので。また、UnityやCocos2d-xは日本語でもいっぱい情報があり、聴講者に知っている人がほとんどいなそうなものを紹介したかった、という意図もあります。

Q. UnityとかCocos2d-xとかenchant.jsとは何が違うの?
A. そこまで詳しくないので変に比較はできないですが、以前Pygameを触っていた時と比べると、Phaserの場合、複雑なゲームにも対応できる豊富なAPIを備えている一方で、初学者が楽しくゲーム開発するためのハードルも低いのが強みなのかなぁと思います。

「実戦での Scala ~ 6つの事例から知る Scala の勘所~」に参加してきた #jissenscala

スマートニュース様にて開催された「実戦での Scala 〜 6つの事例から知る Scala の勘所〜 - 実戦での Scala | Doorkeeper」に参加してきました。
簡単にですが、感想を書きたいと思います。

私とScala

いきなり「お前なんでここに来たんだ」という感じですが、実は私、Scalaは一行も書いたことがありません…。

Javaは仕事で使ってきたので、"Better Java"として(?)のScalaの最新動向をキャッチアップしたいという思いで、一線で活躍されている方々の生の知見を吸収すべく、参加した次第。

おかげで、今日の勉強会を通して、Scalaを勉強したいという意欲が沸々と高まってきたので、これから色々と学んでいきたいと思いました。まずはコップ本と逆引きレシピを購入するところからですね。

勉強会まとめ

細かいメモはしていましたが、複数人のスピーカーの方が共通して話していたので印象に残った部分に絞ってまとめました。

1. Scalaを学ぶ上でのポイント

大きいものは以下の2つなのかなと。

  • Future
  • Option

他にもいっぱいあるとは思うのですが、特にこの2つはきちんと身につけないとならないんだろうなぁと感じました。
並列処理を書く上で、forの内側でFutureを作るとパラレルのつもりがシリアルになってしまうなど、色々とハマリポイントもある事がわかり、参考になりました。

2. twirl

ScalaデファクトとなっているPlayのテンプレートエンジンです(読み方は皆さん悩まれてました)。ほとんどのお話の中で話題に挙がった印象。

「サーバサイドは堅く、フロントエンドは柔らかく(by 竹添さん)」とか、最近のSPA風なアーキテクチャからすると、テンプレートエンジンの出番ってどうなのかな?という気もしますが、静的型付けでコンパイラによってチェックされるというのはやはりメリットらしいです。
反面、デザイナー、HTMLコーダからすると辛い面もあるとのこと。

3. Scala勉強会

Scala技術者は少ないので、毎日30分のScala勉強会をしているという話も印象に残りました。

(何でもそうですが)何かを身につけるにはそれなりの学習コストが必要なんですね。というわけで、自分も始業前の30分はScalaの勉強に充てようかなと思った次第。

4. JavaからScala, LLからScala

Scalaの学習にあたり、JavaからではなくLLから入った人の方が案外すんなり身につくみたいな話がちょっと意外で印象に残りました。

実際はその人の素養やバックグラウンドが大きく影響しているとは思いますが、今日、色々と話を聴き、関数型言語パラダイムをしっかりと意識して勉強していくことが大事なんだろうなと感じました。

おわりに

感想は中身が薄くてアレなのですが、とても有用な勉強会となりました。業務アプリを作るならScalaはベストな選択(by 村石さん)みたいな話もあったので、自分もScalaを使いこなせるようになりたいなぁと思いました。

イベントの提供に関わった方、スピーカーの方、参加された方、会場提供いただいたスマートニュース様、ありがとうございました。

Jenkins Workflow Pluginを使ってGradleマルチプロジェクトのワークフローを組んでみた(WIP)

先日のJenkinsユーザ・カンファレンスの講演を受けて、Jenkins Workflow Pluginがすごく便利そうだったので、早速試してみました。

想定するプロジェクト構成

基本的にはGradleの階層型マルチプロジェクト構成で、その他にドキュメントのビルド(Sphinx)とCoffeeScript, Sassのコンパイル用のプロジェクトがあります。
ファイル構成を簡略化するとこんな感じです。

root/
  build.gradle
  settings.gradle
  common/
    build.gradle
  sub1/
    build.gradle
  sub2/
    build.gradle
  doc/
    Makefile
  ui/
    Gruntfile.coffee
    package.json

各プロジェクトの概要は、

  • root
    • rootプロジェクト
  • common
    • sub1, sub2の共通クラス
  • sub1
    • サブプロジェクトその1(commonに依存)
  • sub2
    • サブプロジェクトその2(commonに依存)
  • doc
    • ドキュメントビルド用プロジェクト(Sphinx関連のファイルが置いてある)
    • Javaプロジェクトではない。
  • ui

パッケージのための成果物はcommon+sub2で1つで1つ, common+sub2+doc+uiで計2つ。

現行のジョブの流れと課題

というわけで、それぞれのプロジェクトのビルドを1つのジョブにマッピングさせて、以下のようなパイプラインを組んでいます。

f:id:jappy:20150112230334p:plain

チェックアウトはパイプラインの最初の1回だけ行います。
そのため、パイプラインの先頭でチェックアウトしてきたソースコード類を他のジョブに引き継ぐために、Clone Workspace SCM Pluginを利用しています。

これはこれで運用は周りますが、設定のメンテナンスという観点で、以下のような課題がありました(一部は対策済み)。

  • 似たようなジョブがたくさんできる(common1-build, sub1-build, sub2-build)
  • Jenkinsのビューでジョブ一覧を見た時にパイプラインの状況が分かりにくい。
  • ジョブ間にまたがった同じ処理の設定が面倒。
  • 【Gradle特有の問題】sub1, sub2のビルドの際に、依存プロジェクト(common)に変更がなくても常に再ビルドされてしまう。
    • (原因)UP-TO-DATEとするかどうかは、デフォルトではファイルのフルパスで判断しているから、らしい(どこかで見た気がするけど曖昧…)。
    • 依存プロジェクトのビルドがすぐ終わるような場合はあまり気になりませんが、やはり同じプロジェクトのビルドはパイプライン中で1回だけで済ませるべきなので、-a オプションをつけて依存プロジェクトのビルドは後続ジョブでスキップするような回避をしています。

Jenkins Workflow Plugin

そこでJenkins Workflow Pluginを試しました。といっても、今は、このパイプラインを置き換えるというよりも、このプラグインで同等のことが実現できるかどうかの実験段階ですが。

インストール

いつも通りのプラグインの管理画面で「Workflow: Aggregator」をチェックしてインストールします。"Jenkins Workflow"でページ検索してもhitしないので注意。 その後Jenkinsを再起動してインストール完了。

Workflow Jobを作成

試しに以下のようなワークフロースクリプトを組んでみて、(とりあえずパッケージングの手前まで)動くことが確認できました。

なお、上記のパイプラインと完全に一致しているわけではないので注意。

node('java') {
    git url: "http://gitbucket-server:8080/git/hoge/root.git", branch: "develop"
    buildSubproject('common')
    archive excludes: '.git/**/*', includes: '**/*'
    
    parallel(sub1: {
        buildSubproject('sub1')
    }, sub2: {
        buildSubproject('sub2')
    }, doc: {
        dir('doc') {
            sh "make html"
        }
    })
 
    step([$class: 'ArtifactArchiver', artifacts: 'doc/_build/html/**/*'])
    step([$class: 'JUnitResultArchiver', testResults: '**/build/test-results/TEST-*.xml'])
}
 
node('nodejs') {
    unarchive mapping: ['ui/' : '.']
    dir('ui') {
        sh 'npm install'
        sh 'grunt ci'
    }
    step([$class: 'ArtifactArchiver', artifacts: 'ui/build/**/*'])
}
 
def buildSubproject(name) {
   sh "chmod +x ./gradlew"
   sh "./gradlew --daemon ${name}:clean ${name}:build"
}

ちょっと補足。

  • GitリポジトリはGitBucketを使っています。
  • 開発環境の都合で、Java用プロジェクトと、Coffee/Sassビルド用プロジェクトはそれぞれ別スレーブ( java ラベル付きスレーブと nodejs ラベル付きスレーブ)になっています。
  • Coffee/SassのビルドはタスクランナーとしてGruntを使っています。 grunt ci はCI用のカスタムタスクの実行です。

所感&分かったこと

  • プラグインで拡張されているようなビルド処理やビルド後の処理などがどうなるのか気になっていたのですが、現状では完全には対応できるわけではないみたいですね。 ※ 公式のCOMPATIBILITY参照。
    • ビルド手順は sh を使えば大体のものが対応できそうだけど、ビルド後の処理(通知や静的解析結果の集計など)は苦しい気がしました。
    • スクリプト中で build(jobname) とすると別のジョブのビルドができるので、うまく組み合わせるとか、になりそう。
  • ビルドでの成果物のコピーは archiveunarchive で簡単にできる。
    • unarchive でのマッチングルールは、 archive のものと一致していなくてもよい。 archive includes: '*.txt' に対して unarchive mapping: ['a.txt' : '.'] とやってもコピー可。
    • archive excludes: '.git/**/*', includes: '**/*' のようにワークスペース全体を成果物としてしまえば、Clone Workspace SCMプラグイン相当のことができる。
    • 異なるスレーブ間でも archiveunarchive でコピー可。
    • build(jobname) でビルドしたジョブの成果物をワークフロージョブ内で unarchive でコピーすることはさすがにできない模様。
  • GitHubやGitBucketのHookを使ってこのワークフロージョブを走らせる、といったことはできない?
    • ワークフロージョブのトリガールールの設定はかなりシンプルでした。
    • ただ、ワークフロージョブとしてできなくても、トリガー専用ジョブ(フリースタイル・ジョブ)とワークフロージョブの2つに分けてつなげば回避できそうです。
  • Snippet Generatorがとても便利でした。
  • stageとかはチェックポイントは使いこなしていないので、要調査。
  • 昨日のレポートでも書いたのですが、解消できなかった疑問として、あるリビジョンでワークフローを流している途中に、別のリビジョンでワークフローを並行して流すことはできるのかどうか、検証する必要がありそうです。
    • ワークスペースが共通なので、Jenkinsの仕組み的に多分できなそうな気はしますが。

というわけで、カンファレンスで受けた印象通りに、すばらしいプラグインだと感じました。
このプラグインの適用事例が増えて、色んなワークフロースクリプト(のテンプレート or サンプル)が公開されてくることに期待したいです。