PathScanner.native_callに絶対パス形式の除外ディレクトリサポートを追加
bootsnap 1.22.0では、PathScanner.native_callが絶対パス形式での除外ディレクトリ指定に対応しました。これにより、BOOTSNAP_IGNORE_DIRECTORIESに絶対パスを設定した場合も正しく動作するようになり、シンボリックリンクの無限ループなどの問題を確実に回避できます。
背景
#457でPathScanner.walkメソッド(PathScanner.ruby_callが使用)に絶対パス対応が追加されましたが、PathScanner.native_callの対応するコードには同様の実装がされていませんでした。この不整合が、#523でエラーハンドリングのrescueブロックが削除された際に顕在化しました。
PR作成者のプロジェクトでは、特定のディレクトリに無限シンボリックリンクループが存在し、BOOTSNAP_IGNORE_DIRECTORIESに絶対パスで除外設定していました。bootsnap 1.22.0へのアップデート後、rescueブロックの削除によりErrno::ELOOP: Too many levels of symbolic linksエラーが発生し、アプリケーションが起動できなくなりました。
技術的な変更
lib/bootsnap/load_path_cache/path_scanner.rbのnative_callメソッドが、除外ディレクトリの絶対パス・相対パスを区別して処理するように変更されました。
変更前:
queue.reject! do |dir|
ignored_directories.include?(dir) ||
(contains_bundle_path && dir.start_with?(BUNDLE_PATH))
end
変更後:
ignored_abs_paths, ignored_dir_names = ignored_directories.partition { |p| File.absolute_path?(p) }
ignored_abs_paths = nil if ignored_abs_paths.empty?
ignored_dir_names = nil if ignored_dir_names.empty?
queue.reject! do |dir|
if ignored_dir_names&.include?(dir)
true
elsif ignored_abs_paths || contains_bundle_path
absolute_dir = File.join(root_path, dir)
ignored_abs_paths&.include?(absolute_dir) ||
(contains_bundle_path && absolute_dir.start_with?(BUNDLE_PATH))
end
end
除外ディレクトリリストをFile.absolute_path?で絶対パスとディレクトリ名に分割し、それぞれ別のロジックで判定するようになりました。ディレクトリ名での判定は従来通り文字列比較で行い、絶対パスでの判定ではFile.join(root_path, dir)で完全なパスを構築してから比較します。
テストケースも3つ追加され、ディレクトリ名による除外、絶対パスによる除外、ネストされたディレクトリの絶対パスによる除外がそれぞれ検証されています。
設計判断
partitionによる事前分類と条件分岐の階層化により、不要な絶対パス構築を避ける設計が採用されました。
除外ディレクトリリストを絶対パスとディレクトリ名に事前分割することで、ディレクトリ名のみでの除外判定時にはFile.joinの呼び出しを完全にスキップできます。elsif ignored_abs_paths || contains_bundle_pathの条件により、絶対パスが1つも指定されておらず、バンドルパスチェックも不要な場合は、内部ブロック全体が実行されません。
また、空の配列をnilに変換する処理(ignored_abs_paths = nil if ignored_abs_paths.empty?)により、安全ナビゲーション演算子(&.)との組み合わせで条件判定を簡潔に記述しています。これはruby_callで使用されるwalkメソッドの実装パターン(ignored_directories.include?(name) || ignored_directories.include?(absolute_path))とは異なるアプローチですが、native_callでは大量のディレクトリを処理するため、パフォーマンスを重視した判断といえます。
まとめ
本PRは、PathScanner.native_callとPathScanner.ruby_callの除外ディレクトリ処理の整合性を保つ変更です。絶対パス判定ロジックを追加することで、シンボリックリンクループのような問題を確実に回避できるようになり、bootsnap 1.22.0で顕在化したリグレッションが解消されました。