本連載では第一線のPerlハッカーが回替わりで執筆していきます。今回のハッカーはcpanm、Plackの作者としても知られる宮川達彦さんで、テーマは
Carmelとは
規模にかかわらず、Perlでアプリケーションを開発すると、CPANモジュールに依存することは避けられません。書き捨てのワンライナーならともかく、作成したアプリケーションをデプロイ、メンテナンスしていくうえでは、依存しているモジュールのバージョンも管理することが重要になります。ある日、依存しているCPANモジュールに互換性のない変更が入ったせいで、自分のアプリケーションでエラーが出るようになった、という経験は思い当たる方が多いのではないでしょうか。
この問題を解決するソフトウェアがCarmelです。Carmelを使うと、プロジェクトで依存するCPANモジュールを、バージョンも含めて管理できます。開発環境やプロダクション環境にデプロイする際には、開発環境やCI
Carmelは筆者が開発しているソフトウェアで、同じく筆者が開発、メンテナンスしているCarton[1]と互換性があります。
Carmelの使い方
この節では、Carmelの使い方を解説します。
インストール
Carmelは通常のCPANモジュールと同様に、cpanm
コマンドなどでインストールできます。CarmelをCPANからインストールします。
> cpanm Carmel ... Successfully installed Carmel-v0.1.56 1 distribution installed
本稿では、執筆時点
cpanfile
の作成
Carmelを利用するには、cpanfile
の作成が必須になります。cpanfile
はプロジェクトが依存しているモジュールを記述するDSLrequires
でリストしていきます。
requires
は引数を1つ、または2つ取ります。第1引数には、依存するモジュール名を指定します。特に依存するバージョンがない場合は、第2引数を省略できます。特定の機能やバグ修正など、決まったバージョン以上が必要になる場合には、第2引数にバージョンの範囲を指定します。
上記のcpanfile
では、Plackモジュールのバージョン1.'1.
と記述した場合、'== 1.
のように==
で指定します[2]。また、複数の条件を,
この例では、
carmel
コマンドの使い方
cpanfile
を作成したら、carmel
コマンドを実行してモジュールを管理していきます。
carmel install
── 依存モジュールのインストール
carmel install
コマンドは、cpanfile
を読み込み、必要となるモジュールをインストールします。
> carmel install (省略) Using WWW::RobotRules (6.02) Using Path::Tiny (0.144) Using Plack (1.0050) (省略) ---> Complete! 3 cpanfile dependencies. 45 modules installed.
このとき、cpanfile
と同じディレクトリcpanfile.
というファイルが作成されます。
cpanfile.
はスナップショットファイルと呼ばれ、carmel install
実行時に解決したモジュールの一覧とそのバージョンなどが記録されます。このファイルをGitリポジトリに追加すれば、git clone
した別のマシンやディレクトリで実行しても、同じバージョンのモジュールが再現されることが保証されます。このファイルのアップデートによって依存するモジュールのバージョンを管理していくことになりますので、このファイルはGitリポジトリなど、ソースコード管理の対象にすることをお勧めします。
また、carmel install
した際のローカル環境などを記録した.carmel/
というファイルも作成されます。ただし、こちらは実行したマシン固有のファイルとなりますので、.carmel
ディレクトリはGitなどのバージョン管理からは除外するとよいでしょう。
carmel list
、tree
── 依存モジュールの一覧
実際にプロジェクトが依存しているモジュールの一覧を見るには、carmel list
やcarmel tree
コマンドが便利です。
> carmel list Apache::LogFormat::Compiler (0.36) Class::Inspector (1.36) Clone (0.46) Cookie::Baker (0.11) Cpanel::JSON::XS (4.36) Date::Parse (2.33) Devel::StackTrace (2.04) Devel::StackTrace::AsHTML (0.15) (省略)
> carmel tree LWP (6.70) Encode::Locale (1.05) File::Listing (6.15) HTTP::Date (6.05) Date::Parse (2.33) HTML::Parser (3.81) HTML::Tagset (3.20) HTTP::Message (6.44) Clone (0.46) IO::HTML (1.004) LWP::MediaTypes (6.04) URI (5.19) HTTP::CookieJar (0.014) HTTP::Cookies (6.10) HTTP::Negotiate (6.01) Net::HTTP (6.23) Try::Tiny (0.31) WWW::RobotRules (6.02) Path::Tiny (0.144) Plack (1.0050) (省略)
list
、tree
コマンドともに、プロジェクトが直接依存しているモジュールや、そのモジュールがさらに依存しているモジュールが表示されます。tree
コマンドでは、その関係がツリー状に表示されます。この例では、PlackやLWPモジュールが依存しているモジュールが数多くあることがわかります。
carmel exec
── インストールされたモジュールを利用して実行する
carmel install
直後にcpanfile
に記述したモジュールを利用しようとすると、次のようなエラーになります。
> perl ./myapp.pl Can't locate Plack.pm in @INC (you may need to install the Plack module) ...
エラーが発生するのは、carmel install
コマンドでインストールされたモジュールは、利用しているPerlのインクルードパス@INC
)carmel exec
コマンドを利用します。
> carmel exec perl ./myapp.pl 1.0050 at ./myapp.pl line 4.
carmel exec
下で実行されたPerlランタイムではインクルードパスがオーバーライドされ、Carmelでインストール、管理されたファイルをCarmelリポジトリから読み込むようになります。
carmel update
── 依存モジュールのアップデート
carmel install
でインストールしたモジュールのバージョンは先述したようにcpanfile.
に保存され、同じスナップショットファイルを利用する限り、同じバージョンがインストールされ続けます。これで依存モジュールが、テストしてないバージョンに意図せず変更されてしまうことを避けられますが、最新の機能やバグ修正を取り込みたいなど、依存モジュールのバージョンをアップデートしたいこともあります。このときに利用するのがcarmel update
コマンドです。
たとえば、Plack 1.cpanfile.
があるとします。
> carmel list | grep Plack Plack (1.0048)
執筆時点でのPlackモジュールの最新版は1.
> carmel update Plack ---> Checking updates... Using Plack (1.0050) (省略) ---> Complete! 3 cpanfile dependencies. 45 modules installed. > carmel list | grep Plack Plack (1.0050)
最新版にアップデートされました。cpanfile.
がGit管理されている場合にgit diff
を実行すると、次のようにcpanfile.
が更新されたことがわかります。
> git diff --- a/cpanfile.snapshot +++ b/cpanfile.snapshot @@ -482,12 +482,12 @@ DISTRIBUTIONS strict 0 warnings 0 warnings::register 0 - Plack-1.0048 - pathname: M/MI/MIYAGAWA/Plack-1.0048.tar.gz + Plack-1.0050 + pathname: M/MI/MIYAGAWA/Plack-1.0050.tar.gz provides: HTTP::Message::PSGI undef HTTP::Server::PSGI undef
carmel update
を引数なしで実行すると、cpanfile
で指定されているすべてのモジュールがアップデート対象となります。
carmel diff
── 変更されたモジュールの表示
Gitリポジトリ内で作業している場合に、carmel update
やcarmel install
で変更されたモジュールのバージョンを見やすく表示するサブコマンドがcarmel diff
です。
たとえば、筆者が運営しているCPAN Meta DBで最近carmel update
を実行した際の表示は、次のようになりました。
> carmel update ---> Checking updates... Using Amazon::S3 (0.45) Using Class::Accessor (0.51) Using Digest::HMAC (1.04) Using Digest::SHA (6.04) Using Digest::MD5::File (0.08) Using LWP (6.70) (省略) Using Router::Simple (0.17) Using Starman (0.4016) Using YAML (1.30) ---> Complete! 19 cpanfile dependencies. 80 modules installed. > carmel diff M CGI (4.54 -> 4.57) A Clone (0.46) M Cpanel-JSON-XS (4.30 -> 4.36) M DBD-SQLite (1.70 -> 1.72) A Digest-SHA (6.04) M File-ShareDir-Install (0.13 -> 0.14) M File-Slurper (0.013 -> 0.014) M HTML-Parser (3.78 -> 3.81) A HTTP-CookieJar (0.014) M HTTP-Message (6.37 -> 6.44) M JSON-MaybeXS (1.004003 -> 1.004005) M libwww-perl (6.67 -> 6.70) M Module-Build (0.4231 -> 0.4234) M Module-Build-Tiny (0.039 -> 0.046) M Module-CoreList (5.20220620 -> 5.20230423) M Net-HTTP (6.22 -> 6.23) M Net-Server (2.010 -> 2.014) M Path-Tiny (0.122 -> 0.144) M Plack (1.0048 -> 1.0050) M Starman (0.4015 -> 0.4016) M Test-Deep (1.130 -> 1.204) M URI (5.10 -> 5.19)
表示される内容はcpanfile.
のgit diff
を表示したものと同等ですが、どのモジュールが変更M
)、追加A
)、削除D
)
依存モジュール管理のワークフロー
Gitで管理されたリポジトリをゼロから作り、Carmelを実行する様子を簡単にまとめると次のようになります。
> mkdir MyApp > cd MyApp > $EDITOR cpanfile > carmel install (省略) > cat > .gitignore /.carmel > git add cpanfile cpanfile.snapshot .gitignore > git commit -m "add Carmel and cpanfile"
<続きの