Ruby Freaks Lounge

第36回 Redmineプラグイン開発(2)

この記事を読むのに必要な時間:およそ 6 分

デプロイコマンドの実装

では核心のdeployアクションに入りましょう。このアクションでチケットのステータスの確認や,履歴の保存などを行います。

def deploy
  ### ① チケットの状態チェック
  warning_status = IssueStatus.find(:first, :conditions => { :name => "実装完了" })
  if @project.issues.map(&:status).include?(warning_status)
    flash[:error] = "未チェックのチケットが存在します!"
    redirect_to :action => "index", :project_id => params[:project_id]
    return
  end

  ### ② リポジトリの更新,最新リビジョン番号の取得
  repository = @project.repository
  repository.fetch_changesets
  latest_changeset = Changeset.find(:first, :conditions => { :repository_id => repository }, :order => "revision DESC")

  ### ③ デプロイの実行
  start = Time.now
  cmd = "cap deploy 2>&1"
  finish = Time.now

  ### ④ 実行結果の確認
  out = `#{cmd}`
  return_code = $? ? $?.exitstatus : 8929

  ### ⑤ 履歴の作成
  DeployHistory.create!(:project => @project,
                        :changeset => latest_changeset,
                        :log => out,
                        :started_on => start,
                        :deployed_on => finish,
                        :deployer => User.current)

  ### ⑥ indexアクションに戻る
  flash[:notice] = "デプロイに成功しました! リビジョン: #{latest_changeset.revision}"
  redirect_to :action => "index", :project_id => params[:project_id]
end

だいぶ長いコードになりました。順々に説明していきます。

まず,では実装完了ステータスのチケットがあるかどうかをチェックし,ある場合にはエラーメッセージをflash領域に格納してリダイレクトしています。flashには値を入れておけばRedmineが自動でレンダリングしてくれます。あらかじめ準備されているキーはerror, warning, noticeの3つで,それぞれデザインが変わります。

図4 flash[:error]とflash[:notice]のデザイン

図4 flash[:error]とflash[:notice]のデザイン

図5

画像

では履歴の保存用にリポジトリの最新リビジョンを取得しています。注意しなければならないのは,Redmineではリポジトリとの同期を定期的に行っているわけではなく,リポジトリ画面にアクセスした際に同期している,という点です。つまり,リポジトリ画面にアクセスしていない場合はRedmine上のリポジトリデータが古い可能性があります。そのため,Repository#fetch_changesetsメソッドを呼び,最新の情報に更新しています。

, ではデプロイの実施と前後の処理時間を取得しています。Linuxのコマンドを実行しているだけなのでcapistranoである必要性は特になく,自作のシェルスクリプトでもかまいません。

そしてで履歴を作成し,で完了メッセージをflashに格納し,indexアクションにリダイレクトします。

まだデプロイしてもアクティビティへ表示されるようにはなっていませんが,とりあえずコアな部分はできました。デプロイ!ボタンを押して,実際に動くことを確認してみましょう。

また,ログなどを表示するデプロイ履歴の詳細画面の作成については今回は省略します。Redmineとは関連しない画面なので,前回のステータスの変更履歴画面と同じように実装すれば問題なく作れるかと思います。

アクティビティへの表示

さて,晴れてRedmine上からデプロイができるようになり,そのログも確認できるようになりました。デプロイはコミットやWikiの更新などと同様にプロジェクトへ加える変更となりますから,デプロイした際にはアクティビティに表示されるようにしてみましょう。

アクティビティ機能はメニューなどと同様にプラグインから利用する事が可能ですが,やや手間がかかります。具体的には以下の2つの作業が必要です。

  1. アクティビティの提供元となるモデルを指定する
  2. モデルに対してアクティビティを生成するための共通インターフェースを実装する

今回はもちろんDeployHistoryモデルが提供元です。

まず,提供元となるモデルはinit.rbで定義します。activity_providerというメソッドで,アクティビティの名前とモデルのクラス名を指定します。

init.rb(追加分のみ)

  activity_provider :deploy_histories, :class_name => "DeployHistory"

次にモデルで実装する共通インターフェースですが,これはacts_as_activity_providerとacts_as_eventという2つのクラスメソッドで実装します。acts_as_activity_providerではアクティビティの取得処理を,acts_as_eventでは表示内容を定義しています。

deploy_history.rb(追加分のみ)

  acts_as_activity_provider :timestamp => "deployed_on",
                            :author_key => :deployer_id,
                            :find_options => { :include => :project } # ①

  acts_as_event :title => Proc.new {|inst| "リビジョン#{inst.changeset.revision}番がデプロイされました。"},
                :description => :log, # 120文字に短縮されます。
                :datetime => :deployed_on,
                :url => Proc.new {|inst| {:controller => 'deployments', :action => 'show', :id => inst.id, :project_id => inst.project_id }},
                :author => :deployer

ではfind_optionsにprojectを指定しています。アクティビティの検索条件にはプロジェクトのIDが含まれるため,必ずincludeに指定する必要があります。

これで完了です。再起動してアクティビティを見ると,先ほどデプロイした際の履歴が追加されているはずです。実はRedmineのアクティビティはそれ専用のテーブルが用意されているわけではなく,提供元となっている複数のモデルからレコードを取得し,それらを内部でマージする実装になっています。ですので,アクティビティを有効にする前のオペレーションでも表示される,というわけです。

おわりに

今回はContinuousDeploymentの開発を通して,チケットのステータスの取得やアクティビティへの表示,リポジトリの操作など,Redmineのビルトインの機能をプラグインから利用する,ということを解説しました。

ちなみにデプロイやビルドとRedmineとの連動,と言うことであればHudsonというCIサーバとRedmineを連動させるプラグインが既にリリースされていますリンク⁠。ですが,CIサーバの導入はわりとヘビーな話になりますし,既にcapistranoや自作のシェルスクリプトで運用している現場にとってはまずはこうして自作のプラグインから始める,というのは気軽で選択肢の一つとしてあってよいかと思います。

最終回となる次回はプラグイン開発の落ち穂拾いと,社内用プラグイン開発のススメ,と言う開発とはちょっと離れた内容についてお話したいと思います。どうぞお楽しみに。

著者プロフィール

京和崇行(きょうわたかゆき)

株式会社カカクコム所属。食べログをRailsでリニューアルした時のメンバーの一人。 今は主にサーバサイドの開発とチューニングを担当…のはずだが他にも色々やっている。 クライミングとニコニコ動画とtwitterがあれば生きていける。