summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorStefan Sedich <stefan.sedich@gmail.com>2017-07-08 11:37:43 -0700
committerStefan Sedich <stefan.sedich@gmail.com>2017-07-11 09:48:28 -0700
commit0c4c413aeec1c24899b78e277fb2094ae5509d07 (patch)
tree12c30b54d82592e67f31394553b6034ac403e4b1
parent9324c84671292c24f598a367708edd03722b6b5b (diff)
downloadbundler-0c4c413aeec1c24899b78e277fb2094ae5509d07.tar.gz
Adding process lock for bundle install operations to fix issue where multiple concurrent bundle install operations fail to complete
-rw-r--r--lib/bundler.rb1
-rw-r--r--lib/bundler/installer.rb30
-rw-r--r--lib/bundler/process_lock.rb21
-rw-r--r--spec/install/process_lock_spec.rb22
4 files changed, 60 insertions, 14 deletions
diff --git a/lib/bundler.rb b/lib/bundler.rb
index b0f6869423..9baa5fd788 100644
--- a/lib/bundler.rb
+++ b/lib/bundler.rb
@@ -40,6 +40,7 @@ module Bundler
autoload :LazySpecification, "bundler/lazy_specification"
autoload :LockfileParser, "bundler/lockfile_parser"
autoload :MatchPlatform, "bundler/match_platform"
+ autoload :ProcessLock, "bundler/process_lock"
autoload :RemoteSpecification, "bundler/remote_specification"
autoload :Resolver, "bundler/resolver"
autoload :Retry, "bundler/retry"
diff --git a/lib/bundler/installer.rb b/lib/bundler/installer.rb
index 78a73eeedd..cbd5b6ec20 100644
--- a/lib/bundler/installer.rb
+++ b/lib/bundler/installer.rb
@@ -68,23 +68,25 @@ module Bundler
def run(options)
create_bundle_path
- if Bundler.settings[:frozen]
- @definition.ensure_equivalent_gemfile_and_lockfile(options[:deployment])
- end
+ ProcessLock.lock do
+ if Bundler.settings[:frozen]
+ @definition.ensure_equivalent_gemfile_and_lockfile(options[:deployment])
+ end
- if @definition.dependencies.empty?
- Bundler.ui.warn "The Gemfile specifies no dependencies"
- lock
- return
- end
+ if @definition.dependencies.empty?
+ Bundler.ui.warn "The Gemfile specifies no dependencies"
+ lock
+ return
+ end
- resolve_if_needed(options)
- ensure_specs_are_compatible!
- warn_on_incompatible_bundler_deps
- install(options)
+ resolve_if_needed(options)
+ ensure_specs_are_compatible!
+ warn_on_incompatible_bundler_deps
+ install(options)
- lock unless Bundler.settings[:frozen]
- Standalone.new(options[:standalone], @definition).generate if options[:standalone]
+ lock unless Bundler.settings[:frozen]
+ Standalone.new(options[:standalone], @definition).generate if options[:standalone]
+ end
end
def generate_bundler_executable_stubs(spec, options = {})
diff --git a/lib/bundler/process_lock.rb b/lib/bundler/process_lock.rb
new file mode 100644
index 0000000000..3f848fe516
--- /dev/null
+++ b/lib/bundler/process_lock.rb
@@ -0,0 +1,21 @@
+# frozen_string_literal: true
+
+module Bundler
+ class ProcessLock
+ def self.lock(bundle_path = Bundler.bundle_path)
+ lock_file_path = File.join(bundle_path, "bundler.lock")
+
+ File.open(lock_file_path, "w") do |f|
+ f.flock(File::LOCK_EX)
+
+ yield
+ end
+ rescue Errno::ENOLCK # NFS
+ raise if Thread.main != Thread.current
+
+ yield
+ ensure
+ File.delete(lock_file_path) if File.exist?(lock_file_path)
+ end
+ end
+end
diff --git a/spec/install/process_lock_spec.rb b/spec/install/process_lock_spec.rb
new file mode 100644
index 0000000000..113fd37934
--- /dev/null
+++ b/spec/install/process_lock_spec.rb
@@ -0,0 +1,22 @@
+# frozen_string_literal: true
+
+RSpec.describe "process lock spec" do
+ describe "when an install operation is already holding a process lock" do
+ it "will not run a second concurrent bundle install until the lock is released" do
+ thread = Thread.new do
+ Bundler::ProcessLock.lock(default_bundle_path) do
+ sleep 1 # ignore quality_spec
+ expect(the_bundle).not_to include_gems "rack 1.0"
+ end
+ end
+
+ install_gemfile! <<-G
+ source "file://#{gem_repo1}"
+ gem "rack"
+ G
+
+ thread.join
+ expect(the_bundle).to include_gems "rack 1.0"
+ end
+ end
+end