Redmine 2.4 ではチケット更新時のフックポイントがモデルからコントローラに移った

細かい変更だけど、個人的には心待ちにしていた Redmine のアップデートの話。

どんな変更?

Redmine 2.4 はスルーしていたのですが、正月の暇な空気の中で眺めてたら、チケット更新時のフックポイントの位置が変更されていました。

Defect #15044: Patch for giving controller_issues_edit_after_save api hook the correct context - Redmine

もともとモデルにあったフックポイントがコントローラに移動されたという割と小さな変更です。

作成時のフックポイントはもとからコントローラにあったので、この変更で統一された事になります。

何がうれしいの?

チケット更新時のフックポイントで呼び出されるプラグインを書いた場合に、プラグイン側から Rails のコントローラの機能を使えるようになりました。

たとえば、コントローラのコンテキストでは Rails のルーティングの機能が使えるため、チケットの URL が簡単に取れたりします。

どうやって使うの?

Redmineプラグインでは、フックポイント名をメソッド名としたクラスを実装します。

フックプラグインの作り方 → Hooks - Redmine

用意されているフックポイントの一覧 → Hooks List - Redmine

例えば、チケット更新時、変更が DB に保存された直後に呼び出されるフックポイント名は controller_issues_new_after_save なので、こんな感じになります。

class FooHook < Redmine::Hook::Listener
  def controller_issues_new_after_save(context)

  end
end

上記フックポイントの一覧のリンク先には明記されていませんが、フックの呼び出し元がコントローラだった場合、引数の context に呼び出し元のコントローラのインスタンスが入っています。

例えば更新したチケットの URL が欲しいならこんなに簡単に。

class FooHook < Redmine::Hook::Listener
  def controller_issues_new_after_save(context)
    issue_controller = context[:controller]
    issue = context[:issue]

    # 更新したチケットの URL を取得
    url = issue_controller.issue_url(issue)
  end
end

これがチケット作成時には簡単にできてたのですが、チケット更新時はとても面倒な処理となっていました。

まとめ

とても細かい Redmine 2.4 の機能について説明しました。

この変更によってチケットの更新時のフックプラグインの実装に自由度が出てくると思います。

bash と curl だけで S3 の Tokyo リージョンにバケットを作る

cosmin/s3-bash がよさげだったのでこれを使う。

環境

ローカルに持ってくる

$ git clone https://github.com/cosmin/s3-bash.git
$ cd s3-bash

シークレットアクセスキーファイルを作る

$ echo -n "シークレットアクセスキー" > secret.txt

注意点:

s3-bash は実行時にこのファイルの内容が 40 文字かどうかをチェックする。

上記のコマンドで -n を忘れたり、エディタで編集してうっかり改行を入れたりすると以下のようなエラーメッセージが出る。

s3-put: We do not understand Amazon AWS 
secret keys which are not 40 bytes long. 
Have you included a carriage 
return or line feed by mistake 
at the end of the secret key file?

リージョンの指定

content.xml というファイルを以下の内容で作成する。

<CreateBucketConfiguration xmlns="http://s3.amazonaws.com/doc/2006-03-01/">
  <LocationConstraint>ap-northeast-1</LocationConstraint>
</CreateBucketConfiguration>

他のリージョンで作成する場合は ap-northeast-1 の部分を変更すればよい。

実行

$ ./s3-put -k アクセスキー -s secret.txt -T content.xml /バケット名

Vagrant の .vagrant.d の場所を変更する

.vagrant.d フォルダを別の場所に変更したい場合は環境変数 VAGRANT_HOME をセットすればいいらしい。

vagrant.d outside of the home folder

Windows に msi で入れたら Vagrant の box とかが入っているフォルダが c:/Users/ユーザ名/.vagrant.d になったのだけれども、容量が空いている別のドライブに移動したくなったので。

Gitlab, AsakusaSatellite などの開発者向け Rails アプリを Redmine で OAuth 認証するための Omniauth プラグイン作った

Redmine・Gitlab・Jenkins のログインパスワードの管理が大変になったので OAuth 化した - すえひろがりっっっっ!Redmine を OAuth プロバイダにできました。
Rails 製のクライアントアプリケーション側は Omniauth Strategy を都度 initializers 以下に設置するようにしていましたが、各アプリケーションに対して同じような作業をするのは DRY じゃないのでライブラリ化しました。

omniauth-redmine https://github.com/suer/omniauth-redmine

環境

ruby 1.9 以降
rails 3.x 以降

セットアップ

Gemfile に以下を追記します。

gem 'omniauth-redmine', :github => 'suer/omniauth-redmine'

設定

devise を利用している場合は RAILS_ROOT/config/initializers/devise.rb に

config.omniauth :redmine, "consumer key", "consumer secret", :redmine_base_url => "http://redmine.base.url/"

のように追記します。 :redmine_base_url オプションは必須項目です。

omniauth を生で扱ってる場合は RAILS_ROOT/config/initializers/omniauth.rb として以下の内容を記述します。

Rails.application.config.middleware.use OmniAuth::Builder do
  provider :redmine, "consumer key", "consumer secret", :redmine_base_url => "http://redmine.base.url/"
end

まとめ

これで Rails 製のアプリケーションへの組み込みの敷居が下がったと思います。
以下のような開発者向けアプリケーションもアカウントを共通化できるかと思います。

nginx でリバースプロキシ時に Host ヘッダを元のリクエストから受け継ぐ

例えば Jenkins を Tomcat にデプロイしていて nginx からリバースプロキシする場合、

        location /jenkins {
                proxy_pass http://localhost:8080;
        }

みたいにすると Host ヘッダが localhost になる。

そこで無理やり

        location /jenkins {
                proxy_set_header Host $host;
                proxy_pass http://localhost:8080;
        }

とすると元のリクエストの Host ヘッダが受け継がれる。

Redmine・Gitlab・Jenkins のログインパスワードの管理が大変になったので OAuth 化した

開発環境として Gitlab、Jenkins、Redmine をセットで使っているのですが、それぞれにパスワードの設定が必要となって管理が面倒です。

アカウントを一つに統合したい。ということでやってみました。

環境

  • Redmine 2.3.0
  • Jenkins 1.499
  • Gitlab 5.2

方針

Redmineプラグインで OAuth プロバイダの機能を追加し、Redmine のアカウントで Jenkins と Gitlab へログインできるようにします。

Redmine に OAuth プロバイダの機能をつける

やりたいことに近いプラグインがあったのですが、Rails3 以降の Redmine に対応していなかったので、fork して、ついでに日本語化しておきました。

http://redmine.root.url/oauth_clients/ にアクセスすると OAuth の consumer key を発行できるようになります。

Jenkins の認証を OAuth に対応する

@mallowlabs さんがあっさり作ってくれました。

Jenkins へログインしようとすると Redmine の OAuth へリダイレクトさるようになります。


Gitlab の認証を OAuth に対応する

Gitlab は Omniauth でのログインに対応しているので、Redmine の OAuth を使って認証するような Strategy を書いて config/initializers に突っ込みました。

こんなふうに書いています。

module OmniAuth
  module Strategies
    class Redmine < OmniAuth::Strategies::OAuth
      option :client_options, {
        :authorize_url => 'https://dev.example.com/redmine/oauth/authorize',
        :request_token_url => 'https://dev.example.com/redmine/oauth/request_token',
        :access_token_url => 'https://dev.example.com/redmine/oauth/access_token'
      }

      uid { raw_info['user']['id'] }

      info do
        {
          'nickname' => raw_info['user']['mail'],
          'email' => raw_info['user']['mail'],
          'name' => "#{raw_info['user']['lastname']} #{raw_info['user']['firstname']}"
        }
      end

      def raw_info
        @raw_info ||= MultiJson.decode(access_token.get("https://dev.example.com/redmine/oauth/user_info.json").body)
      end
    end
  end
end

Omniauth の Strategy の書き方はこちら。

さらに、assets/authbuttons/redmine_32.png に適当なアイコンを置いておくと、こんなふうにログインフォームの下に Redmine の OAuth へのリンクが設置されます。


まとめ

以上で Redmine のアカウントにパスワードを集約できました。
シングルサインオンとまではいかないまでも、一度 Redmine にログインしてしまえば数クリックでそれぞれの環境にログインできるのでとても快適です。

Chef で rbenv + Passenger の環境を構築する

rbenv + Passenger な環境の構築におおいにハマったのでメモ。

環境

  • CentOS 5.9
  • Chef 11.4.4
  • Berkshelf 1.4.3

Berksfile

rbenv と Apache のレシピは Berkshelf で取ってくる。
ここで注意点。 OpsCode Community サイトから取得できる rbenv のレシピ(http://community.opscode.com/cookbooks/rbenv) は rbenv 自体は入れられるが、
これで入れた rbenv 環境下で passenger をビルドすることができないっぽい。

rbenv 環境下でスクリプトを実行できるリソース定義はないものかと探したところ、
https://github.com/fnichol/chef-rbenv を使えばできそうだったのでこちらを採用する。

使い方は http://fnichol.github.io/chef-rbenv/ を参照。

# Berksfile
site :opscode
cookbook 'ruby_build'
cookbook 'rbenv', github: "fnichol/chef-rbenv"
...
$ berks install --path cookbooks

passenger のレシピ

$ knife cookbook create passenger -o site-cookbooks

で cookbook を作成して以下のとおりレシピを記述する。

# site-cookbooks/passenger/recipes/default.rb

# Ruby 2.0.0 をインストールし、デフォルトに設定する
include_recipe "rbenv::system"
rbenv_ruby "2.0.0-p0"
rbenv_global "2.0.0-p0"

# Passenger のインストール
rbenv_gem "passenger" do
  version "4.0.2"
end

# Apache モジュールのビルドに必要なパッケージのインストール (注: CentOS のみ対応)
include_recipe "build-essential"
package "httpd-devel"
if node['platform_version'].to_f < 6.0
  package 'curl-devel'
else
  package 'libcurl-devel'
  package 'openssl-devel'
  package 'zlib-devel'
end

# Apache モジュールのビルド・インストール
conf_path = "/etc/httpd/conf.d/passenger.conf"
unless File.exists?(conf_path)
  rbenv_script "passenger_module" do
    code <<-CODE
      passenger-install-apache2-module --auto
      echo LoadModule passenger_module `passenger-config --root`/libout/apache2/mod_passenger.so >> #{conf_path}
      echo PassengerRoot `passenger-config --root` >> #{conf_path}
      echo PassengerDefaultRuby `rbenv which ruby` >> #{conf_path}
    CODE
  end
end

Apache のインストールなどは省略。

ちょっと大雑把な記述だけど、ここでは fnichol/chef-rbenv の cookbook を使うことで rbenv_script という resource が使えるようになるのがポイント。
rbenv_script の resource 内では指定した rbenv の環境下 (デフォルトでは global) でスクリプトが実行できる。

execute resource を使用した場合は、せっかく入れた rbenv が使われず、システムの ruby (入っていれば) が使われてハマる。

以上で rbenv + Passenger な環境が簡単に手に入るようになりました。