Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 24 additions & 0 deletions bundler/lib/bundler/plugin/events.rb
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,30 @@ def self.defined_event?(event)
# GEM_AFTER_INSTALL = "after-install"
define :GEM_AFTER_INSTALL, "after-install"

# @!parse
# A hook called before each individual gem is fetched for installation.
# Includes a Bundler::ParallelInstaller::SpecInstallation and a source reference.
# GEM_BEFORE_FETCH = "before-fetch"
define :GEM_BEFORE_FETCH, "before-fetch"

# @!parse
# A hook called after each individual gem is fetched for installation.
# Includes a Bundler::ParallelInstaller::SpecInstallation and a source reference.
# GEM_AFTER_FETCH = "after-fetch"
define :GEM_AFTER_FETCH, "after-fetch"

# @!parse
# A hook called before a git source is fetched.
# Includes a Bundler::Source::Git reference.
# GIT_GEM_BEFORE_FETCH = "git-before-fetch"
define :GIT_BEFORE_FETCH, "git-before-fetch"

# @!parse
# A hook called after a git source is fetched.
# Includes a Bundler::Source::Git reference.
# GEM_AFTER_FETCH = "git-after-fetch"
define :GIT_AFTER_FETCH, "git-after-fetch"

# @!parse
# A hook called before any gems install
# Includes an Array of Bundler::Dependency objects
Expand Down
4 changes: 4 additions & 0 deletions bundler/lib/bundler/source/git.rb
Original file line number Diff line number Diff line change
Expand Up @@ -191,8 +191,10 @@ def specs(*)
set_cache_path!(app_cache_path) if use_app_cache?

if requires_checkout? && !@copied
Plugin.hook(Plugin::Events::GIT_BEFORE_FETCH, self)
fetch unless use_app_cache?
checkout
Plugin.hook(Plugin::Events::GIT_AFTER_FETCH, self)
end

local_specs
Expand All @@ -205,7 +207,9 @@ def install(spec, options = {})
print_using_message "Using #{version_message(spec, options[:previous_spec])} from #{self}"

if (requires_checkout? && !@copied) || force
Plugin.hook(Plugin::Events::GEM_BEFORE_FETCH, spec, self)
checkout
Plugin.hook(Plugin::Events::GEM_AFTER_FETCH, spec, self)
end

generate_bin_options = { disable_extensions: !spec.missing_extensions?, build_args: options[:build_args] }
Expand Down
5 changes: 4 additions & 1 deletion bundler/lib/bundler/source/rubygems.rb
Original file line number Diff line number Diff line change
Expand Up @@ -431,11 +431,14 @@ def fetch_names(fetchers, dependency_names, index)
end

def fetch_gem_if_possible(spec, previous_spec = nil)
if spec.remote
Plugin.hook(Plugin::Events::GEM_BEFORE_FETCH, spec, self)
gem_path = if spec.remote
fetch_gem(spec, previous_spec)
else
cached_gem(spec)
end
Plugin.hook(Plugin::Events::GEM_AFTER_FETCH, spec, self)
gem_path
end

def fetch_gem(spec, previous_spec = nil)
Expand Down
73 changes: 73 additions & 0 deletions bundler/spec/plugins/hook_spec.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,79 @@
# frozen_string_literal: true

RSpec.describe "hook plugins" do

context "before-and-after-fetch hooks" do
before do
build_repo2 do
build_plugin "fetch-timing-timing-plugin" do |s|
s.write "plugins.rb", <<-RUBY
@timing_start = nil
Bundler::Plugin::API.hook Bundler::Plugin::Events::GEM_BEFORE_FETCH do |spec_install|
@timing_start = Process.clock_gettime(Process::CLOCK_MONOTONIC)
puts "gem \#{spec_install.name} started fetch at \#{@timing_start}"
end
Bundler::Plugin::API.hook Bundler::Plugin::Events::GEM_AFTER_FETCH do |spec_install|
timing_end = Process.clock_gettime(Process::CLOCK_MONOTONIC)
puts "gem \#{spec_install.name} took \#{timing_end - @timing_start} to fetch"
@timing_start = nil
end
RUBY
end
end

bundle "plugin install fetch-timing-timing-plugin --source https://gem.repo2"
end

it "runs before and after each rubygem is installed" do
install_gemfile <<-G
source "https://gem.repo1"
gem "rake"
gem "myrack"
G

expect(out).to include "gem rake started fetch at"
expect(out).to match /gem rake took \d+\.\d+ to fetch/
expect(out).to include "gem myrack started fetch at"
expect(out).to match /gem myrack took \d+\.\d+ to fetch/
end
end

context "before-and-after-git hooks" do
before do
build_repo2 do
build_plugin "fetch-timing-timing-plugin" do |s|
s.write "plugins.rb", <<-RUBY
@timing_start = nil
Bundler::Plugin::API.hook Bundler::Plugin::Events::GIT_BEFORE_FETCH do |spec_install|
@timing_start = Process.clock_gettime(Process::CLOCK_MONOTONIC)
puts "gem \#{spec_install.name} started git fetch/checkout at \#{@timing_start}"
end
Bundler::Plugin::API.hook Bundler::Plugin::Events::GIT_AFTER_FETCH do |spec_install|
timing_end = Process.clock_gettime(Process::CLOCK_MONOTONIC)
puts "gem \#{spec_install.name} took \#{timing_end - @timing_start} to fetch"
@timing_start = nil
end
RUBY
end
end

bundle "plugin install fetch-timing-timing-plugin --source https://gem.repo2"
end

it "runs before and after each git source rubygem is installed" do
build_git "foo", "1.0", path: lib_path("foo")

relative_path = lib_path("foo").relative_path_from(bundled_app)
install_gemfile <<-G, verbose: true
source "https://gem.repo1"
gem "foo", :git => "#{relative_path}"
G

expect(out).to include "gem foo started git fetch/checkout at"
expect(out).to match /gem foo took \d+\.\d+ to fetch/
end
end

context "before-install-all hook" do
before do
build_repo2 do
Expand Down
Loading