自前サーバーで提供する gem がある場合の Bundler の不可解な動作

Bundler の動作が不可解な点について。

再現条件がよく分からないが，一つはっきりしている条件は，Gemfile で指定する gem の中に「rubygems.org 以外のサーバーで提供するもの」が含まれる，という点だ。

以下の例では，my_awesome_gem という自作 gem が https://example.net/gemsource という URL で提供されている，と想定する。

この場合，Gemfile に

```rb
source "https://example.net/gemsource" do
  gem "my_awesome_gem"
end
```

と書けばよい。

なお，Ruby バージョン

```
ruby 3.3.1 (2024-04-23 revision c56cd86388) [arm64-darwin23]
```

で動作を確認した。

# 再現実験

まず Gemfile を以下のように書く。

```rb:Gemfile
source "https://rubygems.org"

source "https://example.net/gemsource" do
  gem "my_awesome_gem"
end

gem "debug"
```

そして，my_awesome_gem は

```
gem install my_awesome_gem --source https://example.net/gemsource
```

でインストールしておき，逆に **debug はインストールされていない状態に**する[^debug]。

[^debug]: この実験で，「当て馬」に使う gem は debug である必然性が無い。デフォルト gem でなく，かつ rubygems.org で提供されている gem なら何を使っても同じ結果になると思う。

この状態で，

```
bundle check
```

とすると，debug がインストールされていないので，当然チェックは通らない。
ただ，

```
Bundler can't satisfy your Gemfile's dependencies.
Install missing gems with `bundle install`.
```

のようなメッセージになっていて，何の gem が足りないのかが分からない。
Gemfile.lock が存在する場合は，それに照らして

```
The following gems are missing
 * debug (1.9.2)
Install missing gems with `bundle install`
```

などと教えてくれるが，まだ Gemfile.lock が作られていないので不足 gem についての情報は得られないのだ[^lack]。

[^lack]: この仕様もどうかと思う。なぜ教えてくれないのだろう。

そこで，この状態で `Bundler.require` を強行してみることにする。これで不足 gem が判明するはずだ。

この実験のためにスクリプトを書いてもいいが，以下のようなワンライナーで確認できる。

```
ruby -r bundler -e "Bundler.require"
```

これを実行すると「Could not find gem 云々」というエラーメッセージが出るはずだ。
やってみると，

```text:最初の 2 行を抜粋
/Users/XXXXX/.rbenv/versions/3.3.1/lib/ruby/site_ruby/3.3.0/bundler/resolver.rb:336:in `raise_not_found!': Could not find gem 'my_awesome_gem' in locally installed gems. (Bundler::GemNotFound)
        from /Users/XXXXX/.rbenv/versions/3.3.1/lib/ruby/site_ruby/3.3.0/bundler/resolver.rb:414:in `block in prepare_dependencies'
```

のようになった。
たいへん奇妙なことに「**my_awesome_gem が見つからない**」と言っている。
実際には **my_awesome_gem はインストールされていて，足りないのは debug** なのに！

試しに Gemfile の

```rb
source "https://example.net/gemsource" do
  gem "my_awesome_gem"
end
```

の部分をコメントアウトして `bundler check` すると，ちゃんと「debug が見つからない」と言ってくる。

つまり，この部分が何か悪さをしているのだ。

それ以上のことは分からなかった。

# メモ：自前サーバーで gem を提供する方法

自作 gem を rubygems.org 以外で提供するのはとても簡単。
[rubygems-generate_index](https://github.com/rubygems/rubygems-generate_index) という gem を使うので，これをあらかじめインストールしておく。

ウェブサーバーを用意し，公開するディレクトリーの下に `gems` というディレクトリーを置き，そこに自分で build してできた `*.gem` ファイルをためる。
そして，

```
gem generate_index
```

とやるだけ[^cmd]。
これで必要なファイル一式が作られる。`gems` ディレクトリーの中身を加除するたびにこのコマンドを実行すればいい。

[^cmd]: このコマンドは，`gems` ディレクトリーではなくそれを収めた（つまり一階層上の）ディレクトリーで実行する。

自前サーバーを使えば，IP アドレス制限を付けて，公開範囲を「自社内」などに制限することができる。

# 追記 2025-03-23

この記事に記述した再現実験をもう一度やってみた。

今回の環境は以下のとおり：

* ruby 3.4.2 (2025-02-15 revision d2930f8e7a) +PRISM [arm64-darwin24]
* Bundler version 2.6.6

まず，

```
bundle check
```

で「何の gem が足りないのかが分からない」問題は解消されていた。
メッセージは

```
The following gems are missing
 * debug (1.10.0)
Install missing gems with `bundle install`
```

となっており，debug gem が足りないのだと分かるようになった。
たいへんありがたい。

Gemfile にはバージョンを書いていないのに「debug (1.10.0) が足りない」と言ってくるのは，いつ頃からか分からないけど，`bundle check` で Gem source を見にいくようになったからだろう（昔の Bundler はそうではなかったはず）。
`bundler install` したときにインストールされるはずのバージョンをこのように表示しているのだと思う。

`Bundler.require` したとき，無いのは debug gem なのに「my_awesome_gem が見つからない」と言ってくるバグは直っていなかった。残念。