一般社団法人 全国個人事業主支援協会

COLUMN コラム

こんにちは。フリーランスエンジニアのsawadaです。

最近、Capistranoで遊んでいたら、Bundlerの仕様で色々と気になることが出てきたので、今回調べたことを備忘録としてまとめておこうと思います。
Capistranoの話より、Bundlerの話がメインです。

環境

今回、色々と検証した環境は以下の通りです。

  • macOS Big Sur 11.2.2
  • ruby 2.6.6
  • Bundler version 1.17.2

bundle install

まずは、bundle installについてです。

Capistranoの公式サイトに従って、Capistranoをインストールするのを例にしてみます。

  • bundle initでGemfileを作る
% bundle init
Writing new Gemfile to /path/to/Gemfile

% cat Gemfile 
# frozen_string_literal: true

source "https://rubygems.org"

git_source(:github) {|repo_name| "https://github.com/#{repo_name}" }

# gem "rails"
  • GemfileにCapistranoのインストール設定を追記する
% vi Gemfile 

% cat Gemfile 
# frozen_string_literal: true

source "https://rubygems.org"

git_source(:github) {|repo_name| "https://github.com/#{repo_name}" }

# gem "rails"
group :development do
  gem "capistrano", "~> 3.16", require: false
end
  • bundle installで必要なGemをインストールする
% bundle install
Fetching gem metadata from https://rubygems.org/.......
Resolving dependencies...
Using rake 13.0.3
Fetching net-ssh 6.1.0
Installing net-ssh 6.1.0
Fetching net-scp 3.0.0
Installing net-scp 3.0.0
Fetching sshkit 1.21.2
Installing sshkit 1.21.2
Fetching airbrussh 1.4.0
Installing airbrussh 1.4.0
Using bundler 1.17.2
Using concurrent-ruby 1.1.8
Using i18n 1.8.9
Fetching capistrano 3.16.0
Installing capistrano 3.16.0
Bundle complete! 1 Gemfile dependency, 9 gems now installed.
Use `bundle info [gemname]` to see where a bundled gem is installed.

これでbundle exec capが実行できるようになりました。

また、今実行したbundle installによって、Gemの依存関係を記したGemfile.lockが作成されます。

% cat Gemfile.lock 
GEM
  remote: https://rubygems.org/
  specs:
    airbrussh (1.4.0)
      sshkit (>= 1.6.1, != 1.7.0)
    capistrano (3.16.0)
      airbrussh (>= 1.0.0)
      i18n
      rake (>= 10.0.0)
      sshkit (>= 1.9.0)
    concurrent-ruby (1.1.8)
    i18n (1.8.9)
      concurrent-ruby (~> 1.0)
    net-scp (3.0.0)
      net-ssh (>= 2.6.5, = 1.1.2)
      net-ssh (>= 2.8.0)

PLATFORMS
  ruby

DEPENDENCIES
  capistrano (~> 3.16)

BUNDLED WITH
   1.17.2

bundle installGemfile.lockがある場合、Gemfileではなく、Gemfile.lockを基に必要なGemをインストールします

Gemfile.lockにはインストールしたGemのバージョンが記載されているので、このファイルを管理することで、他の人や別の環境でも同じバージョンのGemをインストールすることができます。

以上をまとめるとこのような感じです。

  • bundle installはGemfile.lockがある場合は、それを基に必要なGemをインストールし、ない場合はGemfileを基にGemをインストールする。
  • Gemfile.lockがない状態でbundle installを実行すると、その時インストールしたGemのバージョンが書かれたGemfile.lockが作られる。

不要なGemのアンインストール

次に、bundle installによって間違ってインストールしてしまったGemのアンインストール方法を紹介します。

使用するのはbundle exec gem uninstall #{gems}gem uninstall #{gems}です。

アンインストール対象のGemの確認方法

Gemをアンインストールする前に、そもそもbundle installによって、何のGemをインストールしたのかを調べる方法を書いておこうと思います。

  • Gemのインストールされている場所を調べる

インストールされたGemを調べるためにも、Gemのインストールされた場所を確認します。bundle show #{gem}gem environmentで確認が可能です。

% bundle show capistrano        
/path/to/.rbenv/versions/2.6.6/lib/ruby/gems/2.6.0/gems/capistrano-3.16.0

% gem environment
RubyGems Environment:
省略
  - GEM PATHS:
     - /path/to/.rbenv/versions/2.6.6/lib/ruby/gems/2.6.0
     - /path/to/.gem/ruby/2.6.0
省略
  • 削除するべきGemを確認

私はGemの更新日時で削除するべきGemかどうかを判断しました。(もっといい方法があるかもしれませんが、、、)

# 3月6日のやつがbundle installで追加でインストールされたやつ
% ls -ltr /path/to/.rbenv/versions/2.6.6/lib/ruby/gems/2.6.0/gems/ | tail | awk '{print $6,$7,$8,$9}'
2 18 00:46 turbolinks-source-5.2.0/
2 18 00:46 turbolinks-5.2.1/
2 18 00:46 web-console-4.1.0/
2 18 00:46 webdrivers-4.5.0/
2 18 00:46 webpacker-4.3.0/
3 6 16:26 net-ssh-6.1.0/
3 6 16:26 net-scp-3.0.0/
3 6 16:26 sshkit-1.21.2/
3 6 16:26 airbrussh-1.4.0/
3 6 16:26 capistrano-3.16.0/

bundle exec gem uninstall

アンインストール対象がわかったらbundle exec gem uninstall #{gems}でGemをアンインストールしていきます。

ただし、Gemを複数アンインストールする場合は、bundle exec gem uninstall対象のGemを配列で渡してあげなければいけません

これは、bundle execが、Gemfileを基に必要なGemがすべて存在するかを確認してから実行されるためです。
1回目のbundle exec gem uninstallで削除したGemがないことにより、2回目以降のbundle exec gem uninstallがエラーになります。

  • エラー例
# 1回目のアンインストール
# バージョンの指定は不要
% bundle exec gem uninstall net-ssh

You have requested to uninstall the gem:
	net-ssh-6.1.0

net-scp-3.0.0 depends on net-ssh (>= 2.6.5, = 2.8.0)
If you remove this gem, these dependencies will not be met.
Continue with Uninstall? [yN]  y
Successfully uninstalled net-ssh-6.1.0

# 2回目のアンインストール
# 先にアンインストールしたnet-sshがないのでエラーになる
% bundle exec gem uninstall net-scp
Could not find net-ssh-6.1.0 in any of the sources
Run `bundle install` to install missing gems.
  • 複数アンインストールする場合は配列で渡す
% bundle exec gem uninstall net-ssh net-scp                                                                 

You have requested to uninstall the gem:
	net-scp-3.0.0

sshkit-1.21.2 depends on net-scp (>= 1.1.2)
If you remove this gem, these dependencies will not be met.
Continue with Uninstall? [yN]  y
Successfully uninstalled net-scp-3.0.0

You have requested to uninstall the gem:
	net-ssh-6.1.0

net-scp-3.0.0 depends on net-ssh (>= 2.6.5, = 2.8.0)
If you remove this gem, these dependencies will not be met.
Continue with Uninstall? [yN]  y
Successfully uninstalled net-ssh-6.1.0

gem uninstall

先に書いた通り、bundle exec gem uninstallは少し癖があるので、Gemをアンインストールする時はgem uninstallを使用する方が楽だと思います。

gem uninstallは連続でGemをアンインストールすることもできるし、配列で複数のGemを一気にアンインストールすることもできます。

# 3ついっぺんに消す
% gem uninstall sshkit airbrussh capistrano                                                                     
Remove executables:
	cap, capify

in addition to the gem? [Yn]  y
Removing cap
Removing capify
Successfully uninstalled capistrano-3.16.0
Successfully uninstalled airbrussh-1.4.0
Successfully uninstalled sshkit-1.21.2

–withoutオプション

最後に--withoutオプションについて説明します。

bundle installでは--withoutオプションを使用することで不要なgroupをインストール対象から外すことが可能です。

# capistranoはdevelopment group
% cat Gemfile | grep -A 2 group
group :development do
  gem "capistrano", "~> 3.16", require: false
end

# --without=developmentを指定するとcapistranoがインストールされない
% bundle install --without=development
Using bundler 1.17.2
Bundle complete! 1 Gemfile dependency, 1 gem now installed.
Gems in the group development were not installed.
Use `bundle info [gemname]` to see where a bundled gem is installed.

ちなみに、この--withoutオプションは記憶されるため、次にbundle installを実行した時は--withoutオプションがなくても先に指定したgroupのGemをインストールしません。

# --withoutオプションがないが、capistranoはインストールされない
% bundle install
Using bundler 1.17.2
Bundle complete! 1 Gemfile dependency, 1 gem now installed.
Gems in the group development were not installed.
Use `bundle info [gemname]` to see where a bundled gem is installed.

記憶されたオプションは/path/to/.bundle/configに記録されているので、設定を変更したい場合はこのconfigを修正します。

% cat .bundle/config 
---
BUNDLE_WITHOUT: "development"

# configを削除するとcapistranoがインストールできる
% rm -f .bundle/config 
% bundle install      
Fetching gem metadata from https://rubygems.org/......
Using rake 13.0.3
Fetching net-ssh 6.1.0
Installing net-ssh 6.1.0
Fetching net-scp 3.0.0
Installing net-scp 3.0.0
Fetching sshkit 1.21.2
Installing sshkit 1.21.2
Fetching airbrussh 1.4.0
Installing airbrussh 1.4.0
Using bundler 1.17.2
Using concurrent-ruby 1.1.8
Using i18n 1.8.9
Fetching capistrano 3.16.0
Installing capistrano 3.16.0
Bundle complete! 1 Gemfile dependency, 9 gems now installed.
Use `bundle info [gemname]` to see where a bundled gem is installed.

もしくは--withオプションでも設定を書き換えることができます。

% cat .bundle/config 
---
BUNDLE_WITHOUT: "development"

# --withオプションを付けるとcapistranoがインストールされる
% bundle install --with=development
Fetching gem metadata from https://rubygems.org/......
Using rake 13.0.3
Fetching net-ssh 6.1.0
Installing net-ssh 6.1.0
Fetching net-scp 3.0.0
Installing net-scp 3.0.0
Fetching sshkit 1.21.2
Installing sshkit 1.21.2
Fetching airbrussh 1.4.0
Installing airbrussh 1.4.0
Using bundler 1.17.2
Using concurrent-ruby 1.1.8
Using i18n 1.8.9
Fetching capistrano 3.16.0
Installing capistrano 3.16.0
Bundle complete! 1 Gemfile dependency, 9 gems now installed.
Use `bundle info [gemname]` to see where a bundled gem is installed.

# configの内容も変更されている
% cat .bundle/config               
---
BUNDLE_WITH: "development"

(ここを調べるのに一番時間が掛かったのですが、、、)
bundle checkbundle execを実行するときも.bundle/configの設定は有効となります。

# capistranoがインストールされていなくても、、、
% bundle show capistrano
Could not find gem 'capistrano'.
Did you mean capistrano?

# BUNDLE_WITHOUTが指定されていれば、bundle checkはエラーにならない。
% cat .bundle/config 
---
BUNDLE_WITHOUT: "development"

% bundle check
The Gemfile's dependencies are satisfied

Bundler persists this value in app/.bundle/config so that calls to Bundler.setup do not try to find gems from the Gemfile that you didn’t install.

おわりに

このエントリーを書くに至った発端は、

–withoutオプションが記憶されるのはわかったけど、bundle checkとかbundle installの時もその設定は使われるの?

と疑問に思ってしまったことでした。

調べてみたら意外と答えが見つからなかったので、同じような疑問を持ってしまった誰かの参考になれば幸いです。
(正直、Bundler使う分には絶対に知らなくていい内容ですねw)

The following two tabs change content below.

sawada

2011年からIT業界で働いています。 主にJavaを使ったWebシステム開発をやってきました。 2020年3月からフリーランスエンジニアに転向しました。

この記事をシェアする

  • Twitterでシェア
  • Facebookでシェア
  • LINEでシェア