From 415148392140b0dc7b45cdd8f73273fc108f5345 Mon Sep 17 00:00:00 2001 From: Keenan Brock Date: Tue, 19 Dec 2023 13:16:59 -0500 Subject: [PATCH] Add search path for overridden gems Before ------ Given a relative path to an overriding gem, use the Gemfile directory to locate a gem After ----- Given a relative path (or no path at all - assume it is the gem name), use the Gemfile directory and BUNDLE_BUNDLER_INJECT__GEM_PATH directories to locate a gem --- README.md | 28 ++++++++++++++++++++++++++++ lib/bundler/inject/dsl_patch.rb | 27 ++++++++++++++++++++++++--- spec/bundler_inject_spec.rb | 24 ++++++++++++++++++++++++ 3 files changed, 76 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 1fe94a4..450bb99 100644 --- a/README.md +++ b/README.md @@ -79,6 +79,8 @@ original declaration. ## Configuration +### Disabling warnings + To disable warnings that are output to the console when `override_gem` or `ensure_gem` is in use, you can update a bundler setting: @@ -95,6 +97,32 @@ $ export BUNDLE_BUNDLER_INJECT__DISABLE_WARN_OVERRIDE_GEM=true There is a fallback for those that will check the `RAILS_ENV` environment variable, and will disable the warning when in `"production"`. +### Specifying gem source directories + +Many developers checkout gems into a single directory for enhancement. +Instead of specifying the full path of gems every time, specify a gem path +to locate these directories. This can be defined with a bundler setting: + +```console +$ bundle config bundler_inject.gem_path ~/src:~/gem_src +``` + +or use an environment variable: + +```console +$ export BUNDLE_BUNDLER_INJECT__GEM_PATH=~/src:~/gem_src +``` + +An override will find a gem in either of these two directories or in the directory +where the Gemfile override is located. + +```Gemfile +# located in ~/src/ansi +override_gem "ansi" +# located in $PWD/mime_override +override_gem "mime/type", path: "mime_override" +``` + ## What is this sorcery? While this is technically a bundler plugin, bundler-inject does not use the diff --git a/lib/bundler/inject/dsl_patch.rb b/lib/bundler/inject/dsl_patch.rb index 5ff685b..e6468de 100644 --- a/lib/bundler/inject/dsl_patch.rb +++ b/lib/bundler/inject/dsl_patch.rb @@ -8,7 +8,7 @@ def override_gem(name, *args) calling_file = "#{calling_loc.path}:#{calling_loc.lineno}" remove_dependencies_and_sources(dependency) - expand_gem_path(args, calling_file) + expand_gem_path(name, args, calling_file) gem(name, *args).tap do warn_override_gem(calling_file, name, args) end @@ -59,10 +59,12 @@ def remove_dependencies_and_sources(dependency) end end - def expand_gem_path(args, calling_file) + def expand_gem_path(name, args, calling_file) + args << {:path => name} if args.empty? return unless args.last.kind_of?(Hash) && args.last[:path] - args.last[:path] = File.expand_path(args.last[:path], File.dirname(calling_file)) + full_path = bundler_inject_search_path(File.dirname(calling_file)).map { |p| File.expand_path(args.last[:path], p) }.detect { |f| File.exist?(f) } + args.last[:path] = full_path if full_path end def extract_version_opts(args) @@ -98,6 +100,25 @@ def load_bundler_d(dir) eval_gemfile(f) end end + + # Location to look for overriden gems + # + # This can be set in two ways: + # + # - Via bundler's Bundler::Settings + # + # To configure the setting, you can run: + # + # bundle config bundler_inject.gem_path ~/src:~/gems_src + # + # OR use an environment variable + # + # BUNDLE_BUNDLER_INJECT__GEM_PATH=~/src:~/gems_src + # + def bundler_inject_search_path(gemfile_dir) + @bundler_inject_gem_path ||= (Bundler.settings["bundler_inject.gem_path"] || "").split(File::PATH_SEPARATOR) + [gemfile_dir] + @bundler_inject_gem_path + end end end end diff --git a/spec/bundler_inject_spec.rb b/spec/bundler_inject_spec.rb index c510095..4e22521 100644 --- a/spec/bundler_inject_spec.rb +++ b/spec/bundler_inject_spec.rb @@ -143,6 +143,30 @@ end end + it "with a filename path in a gem_path location " do + with_path_based_gem("https://github.com/rubyworks/ansi", "the_gem") do |path| + write_bundler_d_file <<~F + override_gem "ansi", :path => "the_gem" + F + bundle(:update, :env => {"BUNDLE_BUNDLER_INJECT__GEM_PATH" => path.dirname.to_s}) + + expect(lockfile_specs).to eq [["ansi", "1.5.0"]] + expect(err).to match %r{^\*\* override_gem\("ansi", :path=>#{path.expand_path.to_s.inspect}\) at .+/bundler\.d/local_overrides\.rb:1$} + end + end + + it "with no path in a gem_path location " do + with_path_based_gem("https://github.com/rubyworks/ansi", "ansi") do |path| + write_bundler_d_file <<~F + override_gem "ansi" + F + bundle(:update, :env => {"BUNDLE_BUNDLER_INJECT__GEM_PATH" => path.dirname.to_s}) + + expect(lockfile_specs).to eq [["ansi", "1.5.0"]] + expect(err).to match %r{^\*\* override_gem\("ansi", :path=>#{path.expand_path.to_s.inspect}\) at .+/bundler\.d/local_overrides\.rb:1$} + end + end + it "when the gem doesn't exist" do write_bundler_d_file <<~F override_gem "omg"