C# + Nancy + Herokuで、Rakeタスクを使ってFluentMigratorによるマイグレーションを実行する

以前の記事で、HerokuのMono環境でRakeタスクが実行できることが分かりました。

次は、RakeタスクのRunnerが存在するFluentMigratorを使って、Heroku Postgresのマイグレーションを試してみました。

 

環境

  • Windows7 x64
  • SourceTree
  • .NET Framework 4.5
  • Ruby 2.1.5 (RubyInstaller版)
  • PostgreSQL 9.3.5 x64
  • NuGetパッケージ
    • FluentMigrator 1.3.1
    • FluentMigrator.Tools 1.3.1
    • Npgsql 1.2.2.3
  • Herokuのbuildpack
    • heroku/heroku-buildpack-multi
    • friism/heroku-buildpack-mono
    • heroku/heroku-buildpack-ruby

なお、ここに記載されていないものは以前の記事と同様です。

また、コマンド類はSourceTreeのターミナルから実行しています。

 

Herokuの環境変数確認

heroku-buildpack-multiを使うため、環境変数BUILDPACK_URLが存在することを確認します。

$ heroku config
=== <AppName> Config Vars
BUILDPACK_URL:               https://github.com/heroku/heroku-buildpack-multi.git
...

 

Heroku Postgresの追加・確認

Heroku Postgresの追加

作成したアプリにHeroku Postgresを追加します。

$ heroku addons:add heroku-postgresql:hobby-dev
Adding heroku-postgresql:hobby-dev on <AppName>... done, v14 (free)
Attached as HEROKU_POSTGRESQL_PINK_URL
Database has been created and is available
 ! This database is empty. If upgrading, you can transfer
 ! data from another database with pgbackups:restore.
Use `heroku addons:docs heroku-postgresql` to view documentation.

 

Heroku Postgresのバージョン確認

開発環境にPostgreSQLをインストールするために、Heroku Postgresのバージョンを調べたところ、現在は9.3系のようでした。
Heroku Postgres | Heroku Dev Center

ただ、Herokuでは32bit/64bitの動いているか分からなかったため、pgAdminでHeroku Postgresにつないで確認することにしました。
pgAdmin: PostgreSQL administration and management tools

 
また、Heroku Postgresの接続文字列が分からなかったことから、以下のようなRakeタスクを持ったRakefileを作成・デプロイし、確認しました。

task :show do
  require 'uri'
  db = URI.parse(ENV['DATABASE_URL'])
  
  puts "Server=#{db.host}; Port=#{db.port}; Database=#{db.path[1..-1]}; User Id=#{db.user}; Password=#{db.password}; SSL=true;SslMode=Require;"
end

 
pgAdminを起動・Heroku Postgresへ接続し、SELECT version();で確認したところ、

PostgreSQL 9.3.5 on x86_64-unknown-linux-gnu, compiled by gcc (Ubuntu/Linaro 4.6.3-1ubuntu5) 4.6.3, 64-bit

と表示されたため、PostgreSQL 9.3.5の64bit版をインストールすれば良さそうでした。

 

開発環境のPostgreSQLまわりをセットアップ

インストール

以下よりダウンロード・インストールします。
Download PostgreSQL | EnterpriseDB

 
インストーラーを起動し、以下の内容でセットアップしました。開発環境なので、パスワードは適当です。

項目
Installation Directory C:\PostgreSQL\9.3
Data Directory C:\PostgreSQL\9.3\data
Password postgres
Port 5432 (デフォルト)
Locale C
Stack Build ... チェックを外す

 
初めてPostgreSQLを使ったため、Localeがよく分からなかったのですが、以下を参考にCを選びました。

 

環境変数Pathへ追加

PostgreSQLCLIを使うため、環境変数のPathに以下を追加しました。

C:\PostgreSQL\9.3\bin

 

データベースの確認

psqlをpostgresユーザで起動します。

>psql -U postgres
ユーザ postgres のパスワード:
psql (9.3.5)
"help" でヘルプを表示します.

postgres=#

 
データベースを確認します。

postgres=# \l
                                        データベース一覧
   名前    |  所有者  | エンコーディング | 照合順序 | Ctype(変換演算子) |      アクセス権
-----------+----------+------------------+----------+-------------------+-----------------------
 postgres  | postgres | UTF8             | C        | C                 |
 template0 | postgres | UTF8             | C        | C                 | =c/postgres          +
           |          |                  |          |                   | postgres=CTc/postgres
 template1 | postgres | UTF8             | C        | C                 | =c/postgres          +
           |          |                  |          |                   | postgres=CTc/postgres
(3 行)

 

FluentMigrator用のデータベースを追加

開発環境で使う、fluentmigratorデータベースを追加します。

一度psqlを閉じ、createdbを実行します。

>createdb -U postgres fluentmigrator
パスワード:

 
psqlで結果を確認します。

postgres=# \l
                                          データベース一覧
      名前      |  所有者  | エンコーディング | 照合順序 | Ctype(変換演算子) |      アクセス権
----------------+----------+------------------+----------+-------------------+-----------------------
 fluentmigrator | postgres | UTF8             | C        | C                 |
 postgres       | postgres | UTF8             | C        | C                 |
 template0      | postgres | UTF8             | C        | C                 | =c/postgres          +
                |          |                  |          |                   | postgres=CTc/postgres
 template1      | postgres | UTF8             | C        | C                 | =c/postgres          +
                |          |                  |          |                   | postgres=CTc/postgres
(4 行)

 

開発環境の接続はすべて信頼する

都度設定するのは手間なので、C:\PostgreSQL\9.3\data\pg_hba.conf を編集し、すべて信頼します。

# IPv4 local connections:
host    all             all             127.0.0.1/32            trust   # 追加
#host    all             all             127.0.0.1/32            md5  # コメントアウト

 

サービスの再起動

設定ファイルを編集したため、以下のサービスを再起動します。

postgresql-x64-9.3

 

NuGetパッケージの設定

NuGetパッケージの追加

PostgreSQLに対してFluentMigratorを実行するため、以下のNuGetパッケージを追加します。

  • FluentMigrator
  • FluentMigrator.Tools
  • Npgsql

 

FluentMigratorの実行に必要なファイルをプロジェクトへ追加・設定
ファイルをリンクとして追加

FluentMigratorをRakeタスクで実行するため、以下のファイルをプロジェクトの「追加 > 既存の項目」から「リンクとして追加」しておきます。

ファイル名 存在するパス
FluentMigrator.Runner.dll path\to\project_root\packages\FluentMigrator.Tools.1.3.1.0\tools\AnyCPU\40\FluentMigrator.Runner.dll
Migrate.exe path\to\project_root\packages\FluentMigrator.Tools.1.3.1.0\tools\AnyCPU\40\Migrate.exe

 

ファイルのプロパティを修正

ビルドした時に出力ディレクトリへコピーされるよう、上記2ファイルのプロパティを修正します。

項目
ビルドアクション コンテンツ
出力ディレクトリにコピー 常にコピーする

参考:ファイルのプロパティ - MSDN

 

gem Albacore の確認

FluentMigratorのWikiには、gem Albacoreを使うことでRakeタスクとして実行できると書かれています。
Migration Runners · schambers/fluentmigrator Wiki · GitHub

しかし、Gemfileにalbacoreを追加してインストールし、Wikiのコードをコピペして実行したところ、エラーが発生しました。

$ bundle exec rake db:migrate -f Albacore.rake
DL is deprecated, please use Fiddle
rake aborted!
NoMethodError: undefined method `fluentmigrator' for main:Object
...

 
Albacoreのソースコードを見ると、v1に存在していた以下のようなコードが、masterには存在しませんでした。
albacore/fluentmigrator.rb at v1.0.0 · Albacore/albacore

AlbacoreのWikiのIndexを見ても、Albacore 1.0.0の下にしかFluent MigratorのTask Typesが記載されていませんでした*1
Home · Albacore/albacore Wiki · GitHub

また、他のTaskTypesであるCSCもAlbacore v2.xではサポートされていないとの回答がありました。
ruby - Why is this csc task not working in Albacore? - Stack Overflow

 
これより、現在のAlbacore v2.xではFluentMigratorはサポートされていないと考えられたため、

  • Albacore v1.0.0 を使う
  • 自力でFluentMigratorのRakeタスクを書く

のどちらかの方法を取る必要がありました。

Albacore v1.x系は更新されなさそうなので、今回は後者の方法を選びました。参考としたソースコードは以下のあたりです。

 
ちなみに、Albacore 2.xは2014/8/21にリリースされたので、それ以前の日付の記事は現時点ではあまり参考にならないかもしれません。

 

Rakeタスクを作成

Rakeタスクを書くにあたり、以下の点で悩みました。

 

開発環境とHeroku環境の判定

各環境では接続文字列などが異なるため、各環境をどのように判定するのか悩みました。

 

gem Platformを使う

Herokuでは、

require 'platform'
puts "OS:#{Platform::OS}"
puts "IMPL:#{Platform::IMPL}"
puts "ARCH:#{Platform::ARCH}"

とすると、Platform::ARCHがunknownを返してきます。

ただ、unknownだけで判断するのはあまり良くない気がしました。

 

環境変数RACK_ENVを使う

Herokuでよく見かける環境変数RACK_ENVが使えないかを調べました。

すると、Herokuでは gem Rack があるとENV['RACK_ENV']productionが入るというドキュメントを見ました((正確にはconfigディレクトリやconfig.ruファイルが必要そうですが、手元ではGemfileにrackを追加するだけでもRACK_ENVにproductionが設定されました))。
Heroku Ruby Support | Heroku Dev Center

ただ、Rackアプリではないのに不要なgemを追加するのも気が引けました。

 

結論

今回はHeroku Postgresを使うため、DATABASE_URLが設定されているかどうか(設定されているとHeroku環境)ということにしました。

 

Heroku上でMigrater.exeを実行する方法

Rakefile

system "\"#{runner_path}\" <各種オプション>"

と書いて実行したところ、エラーが発生しました。

$ heroku run rake migration
Running `rake migration` attached to terminal... up, run.2591
sh: 1: Migrate.exe: not found

 
Migrate.exeがデプロイされていないのかと思いましたが、ファイルは存在していました。

$ heroku run ls
Running `ls` attached to terminal... up, run.3544
app.json                              Nancy.Owin.dll
bin                                   NancyOwinHeroku-sample
FluentMigrator.dll                    NancyOwinHeroku-sample.exe
FluentMigrator.Runner.dll             NancyOwinHeroku-sample.exe.config
Gemfile                               NancyOwinHeroku-sample.exe.mdb
Gemfile.lock                          NancyOwinHeroku-sample.sln
LICENSE                               Npgsql.dll
Microsoft.Owin.dll                    Owin.dll
Microsoft.Owin.Host.HttpListener.dll  packages
Microsoft.Owin.Hosting.dll            Procfile
Migrate.exe                           Rakefile
mono                                  README.md
Mono.Security.dll                     tmp
Nancy.dll                             vendor

 
調べてみたところ、Herokuでexeファイルを実行するには、Monoを使う必要がありました。
node.js - Is there a way to run a .net exe on heroku? - Stack Overflow

 
そのため、Rakefileで実行する箇所を以下のような感じにします。

system "mono \"#{runner_path}\" <各種オプション>"

 

HerokuにてFluentMigratorを実行

Herokuへデプロイ
git push heroku master

 

FluentMigratorによるマイグレーション

ネットワークを介してherokuコマンドを実行すると、「Running attached to terminal... up, run.」のあとで結構待たされるため、One-Off Dynoのbashを使って作業を行います。
One-Off Dynos - An example one-off dyno | Heroku Dev Center

$ heroku run bash
Running `bash` attached to terminal... up, run.5670
~ $

 
続いて、Rakefileに書いたマイグレーションを実行します。

~ $ rake migration
...
[+] Using Database postgres and Connection String Server ...
...
[+] Task completed.

 

Heroku Postgresの確認

テーブルが生成され、データも登録されていました。

これにより、HerokuでもFluentMigratorが実行できると分かりました。

$ heroku pg:psql
---> Connecting to HEROKU_POSTGRESQL_AMBER_URL (DATABASE_URL)
psql (9.3.5)
SSL 接続 (暗号化方式: DHE-RSA-AES256-SHA, ビット長: 256)
"help" でヘルプを表示します.

<AppName>::AMBER=> \d
                 リレーションの一覧
 スキーマ |    名前     |    型    |     所有者
----------+-------------+----------+----------------
 public   | Ringo       | テーブル | vumlwdhcmtacuy
 public   | VersionInfo | テーブル | vumlwdhcmtacuy
(2 行)

<AppName>::AMBER=> SELECT * FROM "Ringo";
 ID |      Name
----+----------------
  1 | ふじ
  2 | シナノゴールド
  3 | あいかの香り
(3 行)

 
なお、最後のSQLでテーブル名をダブルクオーテーションで囲まないとエラーになります。

<AppName>::AMBER=> SELECT * FROM ringo;
ERROR:  relation "ringo" does not exist
行 1: SELECT * FROM ringo;

参考:PostgreSQLのテーブル名はダブルクォート有無で別名になった. - それマグで!

 

ソースコード

GitHubに上げました。
thinkAmi-sandbox/NancyOwinHeroku-FluentMigrator · GitHub

 

参考

Heroku Postgres
Rake

*1:ぱっと見、v1.0.0でしか使えないとは分かりづらかった