diff options
author | John Keiser <john@johnkeiser.com> | 2016-04-14 14:24:46 -0700 |
---|---|---|
committer | John Keiser <john@johnkeiser.com> | 2016-04-14 22:53:37 -0700 |
commit | d0bf64da60725181918a2fa0de949f4158da29a1 (patch) | |
tree | 4029aa13518c8dffef8bb900616ce4e04612567a | |
parent | 303a25c9cb42b4d8d5860daf82efd11c5776fe61 (diff) | |
download | chef-jk/bundle-repro.tar.gz |
Use released oc-chef-pedant with latest chef-zerojk/bundle-repro
-rw-r--r-- | .travis.yml | 1 | ||||
-rw-r--r-- | Gemfile | 10 | ||||
-rw-r--r-- | Gemfile.lock | 94 | ||||
-rw-r--r-- | Gemfile.windows.lock | 100 | ||||
-rw-r--r-- | appveyor.yml | 1 | ||||
-rw-r--r-- | omnibus/Gemfile.lock | 2 | ||||
-rw-r--r-- | omnibus/files/chef-gem/build-chef-gem/gem-install-software-def.rb | 43 | ||||
-rw-r--r-- | omnibus/files/chef/build-chef.rb | 16 | ||||
-rw-r--r-- | tasks/gemfile_util.rb | 185 |
9 files changed, 201 insertions, 251 deletions
diff --git a/.travis.yml b/.travis.yml index ec268e1343..243349bb8d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -62,7 +62,6 @@ matrix: rvm: 2.2 - env: - TEST_GEM: chef-zero - - TEST_WITH_GEMS: oc-chef-pedant script: tasks/bin/run_external_test $TEST_GEM "rake spec" "rake cheffs" rvm: 2.2 - env: @@ -10,8 +10,7 @@ gem "chef-config", path: File.expand_path("../chef-config", __FILE__) if File.ex # Ensure that we can always install rake, regardless of gem groups gem "rake" gem "bundler" -# Remove "master" bit when cheffish tests succeed in Ruby 2.2 -gem "cheffish", github: "chef/cheffish" +gem "cheffish" group(:omnibus_package) do gem "appbundler" @@ -32,14 +31,13 @@ group(:integration) do gem "chef-sugar" gem "chefspec" gem "halite" - gem "poise", github: "poise/poise" # until released poise's tests succeed against chef master + gem "poise", git: "https://github.com/poise/poise" # until released poise's tests succeed against chef master gem "knife-windows" gem "foodcritic" # Used by others: - gem "fauxhai", github: "customink/fauxhai" # until AIX changes are released - gem "poise-boiler", github: "poise/poise-boiler" # until new release is made with higher rake dep - gem "oc-chef-pedant", github: "chef/chef-server" # until new chef-zero release + gem "fauxhai", git: "https://github.com/customink/fauxhai" # until AIX changes are released + gem "poise-boiler", git: "https://github.com/poise/poise-boiler" # until new release is made with higher rake dep end group(:docgen) do diff --git a/Gemfile.lock b/Gemfile.lock index 5790365375..21052fd7c3 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,36 +1,27 @@ GIT - remote: git://github.com/chef/chef-server.git - revision: 88a33630aaeae21a63b4a8111fd0a196438520c0 + remote: https://github.com/chef/chefstyle.git + revision: cc37808b7849fdcf49f04011626143940f83fe92 + branch: master specs: - oc-chef-pedant (2.2.0) - activesupport (~> 3.2) - erubis (~> 2.7) - mixlib-authentication (~> 1.3) - mixlib-config (~> 2.0) - mixlib-shellout (>= 1.1) - net-http-spy (~> 0.2) - rest-client (>= 1.6) - rspec (~> 3.2) - rspec-rerun (~> 1.0) - rspec_junit_formatter (~> 0.2) + chefstyle (0.3.1) + rubocop (= 0.39.0) GIT - remote: git://github.com/chef/cheffish.git - revision: 614d15ddc31b759319696aeab0f4ef50a101ebc2 + remote: https://github.com/customink/fauxhai + revision: 079c591fabd78ef13e284a84fe808fc48bf1930f specs: - cheffish (2.0.3) - chef-zero (~> 4.3) - compat_resource + fauxhai (3.3.0) + net-ssh GIT - remote: git://github.com/customink/fauxhai.git - revision: a616c6aa9e2e7e6d9825a579a00b8d6e15484205 + remote: https://github.com/poise/poise + revision: a76980685a92283c08f5b2e4526ae1f08f87de79 specs: - fauxhai (3.2.0) - net-ssh + poise (2.6.2.pre) + halite (~> 1.0) GIT - remote: git://github.com/poise/poise-boiler.git + remote: https://github.com/poise/poise-boiler revision: 0714455eba947bde2a9d49b72db1bc3ad1034ee4 specs: poise-boiler (1.7.2) @@ -60,21 +51,6 @@ GIT yard-classmethods (~> 1.0) GIT - remote: git://github.com/poise/poise.git - revision: a76980685a92283c08f5b2e4526ae1f08f87de79 - specs: - poise (2.6.2.pre) - halite (~> 1.0) - -GIT - remote: https://github.com/chef/chefstyle.git - revision: cc37808b7849fdcf49f04011626143940f83fe92 - branch: master - specs: - chefstyle (0.3.1) - rubocop (= 0.39.0) - -GIT remote: https://github.com/rubysec/bundler-audit.git revision: 4e32fca89d75f0e249671431ff38aadc02bfb28b ref: 4e32fca @@ -84,6 +60,14 @@ GIT thor (~> 0.18) PATH + remote: chef-config + specs: + chef-config (12.10.4) + fuzzyurl (~> 0.8.0) + mixlib-config (~> 2.0) + mixlib-shellout (~> 2.0) + +PATH remote: . specs: chef (12.10.4) @@ -113,14 +97,6 @@ PATH syslog-logger (~> 1.6) uuidtools (~> 2.1.5) -PATH - remote: chef-config - specs: - chef-config (12.10.4) - fuzzyurl (~> 0.8.0) - mixlib-config (~> 2.0) - mixlib-shellout (~> 2.0) - GEM remote: https://rubygems.org/ specs: @@ -165,12 +141,15 @@ GEM ubuntu_ami (~> 0.4, >= 0.4.1) chef-rewind (0.0.9) chef-sugar (3.3.0) - chef-zero (4.5.0) + chef-zero (4.6.1) ffi-yajl (~> 2.2) hashie (>= 2.0, < 4.0) mixlib-log (~> 1.3) rack uuidtools (~> 2.1) + cheffish (2.0.4) + chef-zero (~> 4.3) + compat_resource chefspec (4.6.1) chef (>= 11.14) fauxhai (~> 3.2) @@ -193,8 +172,6 @@ GEM thread_safe (~> 0.3, >= 0.3.1) diff-lcs (1.2.5) docile (1.1.5) - domain_name (0.5.20160310) - unf (>= 0.0.5, < 1.0.0) erubis (2.7.0) ethon (0.8.1) ffi (>= 1.3.0) @@ -252,8 +229,6 @@ GEM thor hashie (3.4.3) highline (1.7.8) - http-cookie (1.0.2) - domain_name (~> 0.5) httpclient (2.7.1) i18n (0.7.0) inifile (3.0.0) @@ -282,7 +257,9 @@ GEM multi_json (~> 1.10) logify (0.2.0) method_source (0.8.2) - mime-types (2.99.1) + mime-types (3.0) + mime-types-data (~> 3.2015) + mime-types-data (3.2016.0221) mini_portile2 (2.0.0) mixlib-authentication (1.4.0) mixlib-log @@ -303,7 +280,6 @@ GEM multipart-post (2.0.0) net-http-persistent (2.9.4) net-http-pipeline (1.0.1) - net-http-spy (0.2.1) net-scp (1.2.1) net-ssh (>= 2.6.5) net-sftp (2.1.2) @@ -370,10 +346,6 @@ GEM rainbow (2.1.0) rake (11.1.2) rb-readline (0.5.3) - rest-client (1.8.0) - http-cookie (>= 1.0.2, < 2.0) - mime-types (>= 1.16, < 3.0) - netrc (~> 0.7) retryable (2.0.3) rspec (3.4.0) rspec-core (~> 3.4.0) @@ -390,8 +362,6 @@ GEM rspec-mocks (3.4.1) diff-lcs (>= 1.2.0, < 2.0) rspec-support (~> 3.4.0) - rspec-rerun (1.1.0) - rspec (~> 3.0) rspec-support (3.4.1) rspec_junit_formatter (0.2.3) builder (< 4) @@ -457,9 +427,6 @@ GEM typhoeus (0.8.0) ethon (>= 0.8.0) ubuntu_ami (0.4.1) - unf (0.1.4) - unf_ext - unf_ext (0.0.7.2) unicode-display_width (1.0.3) url (0.3.2) uuidtools (2.1.5) @@ -493,7 +460,7 @@ DEPENDENCIES chef-provisioning-aws chef-rewind chef-sugar - cheffish! + cheffish chefspec chefstyle! fauxhai! @@ -503,7 +470,6 @@ DEPENDENCIES knife-windows netrc nokogiri - oc-chef-pedant! octokit poise! poise-boiler! diff --git a/Gemfile.windows.lock b/Gemfile.windows.lock index 6cff3e06a8..bd515be37b 100644 --- a/Gemfile.windows.lock +++ b/Gemfile.windows.lock @@ -1,39 +1,29 @@ GIT - remote: git://github.com/chef/chef-server.git - revision: 88a33630aaeae21a63b4a8111fd0a196438520c0 - ref: 88a33630aaeae21a63b4a8111fd0a196438520c0 + remote: https://github.com/chef/chefstyle.git + revision: cc37808b7849fdcf49f04011626143940f83fe92 + ref: cc37808b7849fdcf49f04011626143940f83fe92 specs: - oc-chef-pedant (2.2.0) - activesupport (~> 3.2) - erubis (~> 2.7) - mixlib-authentication (~> 1.3) - mixlib-config (~> 2.0) - mixlib-shellout (>= 1.1) - net-http-spy (~> 0.2) - rest-client (>= 1.6) - rspec (~> 3.2) - rspec-rerun (~> 1.0) - rspec_junit_formatter (~> 0.2) + chefstyle (0.3.1) + rubocop (= 0.39.0) GIT - remote: git://github.com/chef/cheffish.git - revision: 614d15ddc31b759319696aeab0f4ef50a101ebc2 - ref: 614d15ddc31b759319696aeab0f4ef50a101ebc2 + remote: https://github.com/customink/fauxhai + revision: 079c591fabd78ef13e284a84fe808fc48bf1930f + ref: 079c591fabd78ef13e284a84fe808fc48bf1930f specs: - cheffish (2.0.3) - chef-zero (~> 4.3) - compat_resource + fauxhai (3.3.0) + net-ssh GIT - remote: git://github.com/customink/fauxhai.git - revision: a616c6aa9e2e7e6d9825a579a00b8d6e15484205 - ref: a616c6aa9e2e7e6d9825a579a00b8d6e15484205 + remote: https://github.com/poise/poise + revision: a76980685a92283c08f5b2e4526ae1f08f87de79 + ref: a76980685a92283c08f5b2e4526ae1f08f87de79 specs: - fauxhai (3.2.0) - net-ssh + poise (2.6.2.pre) + halite (~> 1.0) GIT - remote: git://github.com/poise/poise-boiler.git + remote: https://github.com/poise/poise-boiler revision: 0714455eba947bde2a9d49b72db1bc3ad1034ee4 ref: 0714455eba947bde2a9d49b72db1bc3ad1034ee4 specs: @@ -64,22 +54,6 @@ GIT yard-classmethods (~> 1.0) GIT - remote: git://github.com/poise/poise.git - revision: a76980685a92283c08f5b2e4526ae1f08f87de79 - ref: a76980685a92283c08f5b2e4526ae1f08f87de79 - specs: - poise (2.6.2.pre) - halite (~> 1.0) - -GIT - remote: https://github.com/chef/chefstyle.git - revision: cc37808b7849fdcf49f04011626143940f83fe92 - ref: cc37808b7849fdcf49f04011626143940f83fe92 - specs: - chefstyle (0.3.1) - rubocop (= 0.39.0) - -GIT remote: https://github.com/rubysec/bundler-audit.git revision: 4e32fca89d75f0e249671431ff38aadc02bfb28b ref: 4e32fca89d75f0e249671431ff38aadc02bfb28b @@ -140,9 +114,6 @@ PATH GEM remote: https://rubygems.org/ specs: - activesupport (3.2.22.2) - i18n (~> 0.6, >= 0.6.4) - multi_json (~> 1.0) addressable (2.4.0) appbundler (0.9.0) mixlib-cli (~> 1.4) @@ -181,12 +152,15 @@ GEM ubuntu_ami (~> 0.4, >= 0.4.1) chef-rewind (0.0.9) chef-sugar (3.3.0) - chef-zero (4.5.0) + chef-zero (4.6.1) ffi-yajl (~> 2.2) hashie (>= 2.0, < 4.0) mixlib-log (~> 1.3) rack uuidtools (~> 2.1) + cheffish (2.0.4) + chef-zero (~> 4.3) + compat_resource chefspec (4.6.1) chef (>= 11.14) fauxhai (~> 3.2) @@ -209,8 +183,6 @@ GEM thread_safe (~> 0.3, >= 0.3.1) diff-lcs (1.2.5) docile (1.1.5) - domain_name (0.5.20160310) - unf (>= 0.0.5, < 1.0.0) erubis (2.7.0) ethon (0.8.1) ffi (>= 1.3.0) @@ -268,8 +240,6 @@ GEM thor hashie (3.4.3) highline (1.7.8) - http-cookie (1.0.2) - domain_name (~> 0.5) httpclient (2.7.1) i18n (0.7.0) inifile (3.0.0) @@ -298,7 +268,9 @@ GEM multi_json (~> 1.10) logify (0.2.0) method_source (0.8.2) - mime-types (2.99.1) + mime-types (3.0) + mime-types-data (~> 3.2015) + mime-types-data (3.2016.0221) mini_portile2 (2.0.0) mixlib-authentication (1.4.0) mixlib-log @@ -321,7 +293,6 @@ GEM multipart-post (2.0.0) net-http-persistent (2.9.4) net-http-pipeline (1.0.1) - net-http-spy (0.2.1) net-scp (1.2.1) net-ssh (>= 2.6.5) net-sftp (2.1.2) @@ -388,11 +359,6 @@ GEM rainbow (2.1.0) rake (11.1.2) rb-readline (0.5.3) - rest-client (1.8.0-x86-mingw32) - ffi (~> 1.9) - http-cookie (>= 1.0.2, < 2.0) - mime-types (>= 1.16, < 3.0) - netrc (~> 0.7) retryable (2.0.3) rspec (3.4.0) rspec-core (~> 3.4.0) @@ -409,8 +375,6 @@ GEM rspec-mocks (3.4.1) diff-lcs (>= 1.2.0, < 2.0) rspec-support (~> 3.4.0) - rspec-rerun (1.1.0) - rspec (~> 3.0) rspec-support (3.4.1) rspec_junit_formatter (0.2.3) builder (< 4) @@ -476,9 +440,6 @@ GEM typhoeus (0.8.0) ethon (>= 0.8.0) ubuntu_ami (0.4.1) - unf (0.1.4) - unf_ext - unf_ext (0.0.7.2-x86-mingw32) unicode-display_width (1.0.3) url (0.3.2) uuidtools (2.1.5) @@ -542,8 +503,8 @@ DEPENDENCIES chef-provisioning-aws (= 1.9.0)! chef-rewind (= 0.0.9)! chef-sugar (= 3.3.0)! - chef-zero (= 4.5.0)! - cheffish! + chef-zero (= 4.6.1)! + cheffish (= 2.0.4)! chefspec (= 4.6.1)! chefstyle! childprocess (= 0.5.9)! @@ -557,7 +518,6 @@ DEPENDENCIES descendants_tracker (= 0.0.4)! diff-lcs (= 1.2.5)! docile (= 1.1.5)! - domain_name (= 0.5.20160310)! erubis (= 2.7.0)! ethon (= 0.8.1)! faraday (= 0.9.2)! @@ -578,7 +538,6 @@ DEPENDENCIES halite (= 1.2.1)! hashie (= 3.4.3)! highline (= 1.7.8)! - http-cookie (= 1.0.2)! httpclient (= 2.7.1)! i18n (= 0.7.0)! inifile (= 3.0.0)! @@ -598,7 +557,8 @@ DEPENDENCIES logging (= 2.1.0)! logify (= 0.2.0)! method_source (= 0.8.2)! - mime-types (= 2.99.1)! + mime-types (= 3.0)! + mime-types-data (= 3.2016.0221)! mini_portile2 (= 2.0.0)! mixlib-authentication (= 1.4.0)! mixlib-cli (= 1.5.0)! @@ -612,7 +572,6 @@ DEPENDENCIES multipart-post (= 2.0.0)! net-http-persistent (= 2.9.4)! net-http-pipeline (= 1.0.1)! - net-http-spy (= 0.2.1)! net-scp (= 1.2.1)! net-sftp (= 2.1.2)! net-ssh (= 3.1.1)! @@ -623,7 +582,6 @@ DEPENDENCIES nokogiri (= 1.6.7.2)! nori (= 2.6.0)! oauth2 (= 1.1.0)! - oc-chef-pedant! octokit (= 4.3.0)! ohai (= 8.14.0)! overcommit (= 0.33.0)! @@ -644,14 +602,12 @@ DEPENDENCIES rainbow (= 2.1.0)! rake (= 11.1.2)! rb-readline (= 0.5.3)! - rest-client (= 1.8.0)! retryable (= 2.0.3)! rspec (= 3.4.0)! rspec-core (= 3.4.4)! rspec-expectations (= 3.4.0)! rspec-its (= 1.2.0)! rspec-mocks (= 3.4.1)! - rspec-rerun (= 1.1.0)! rspec-support (= 3.4.1)! rspec_junit_formatter (= 0.2.3)! rubocop (= 0.39.0)! @@ -679,8 +635,6 @@ DEPENDENCIES treetop (= 1.6.5)! typhoeus (= 0.8.0)! ubuntu_ami (= 0.4.1)! - unf (= 0.1.4)! - unf_ext (= 0.0.7.2)! unicode-display_width (= 1.0.3)! url (= 0.3.2)! uuidtools (= 2.1.5)! diff --git a/appveyor.yml b/appveyor.yml index 13437088bb..d76e43013e 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -16,6 +16,7 @@ skip_tags: true branches: only: - master + - jk/bundle-repro install: - systeminfo diff --git a/omnibus/Gemfile.lock b/omnibus/Gemfile.lock index 715a2f3edc..7e92537e3c 100644 --- a/omnibus/Gemfile.lock +++ b/omnibus/Gemfile.lock @@ -1,6 +1,6 @@ GIT remote: https://github.com/chef/omnibus-software.git - revision: 8d611676c4458fa679cbc48e2111892ae7986cbf + revision: 6127be3af79941c32419228cbd9e63c4f061d76b specs: omnibus-software (4.0.0) omnibus (>= 5.2.0) diff --git a/omnibus/files/chef-gem/build-chef-gem/gem-install-software-def.rb b/omnibus/files/chef-gem/build-chef-gem/gem-install-software-def.rb index 5992ae8057..ee096b8ed9 100644 --- a/omnibus/files/chef-gem/build-chef-gem/gem-install-software-def.rb +++ b/omnibus/files/chef-gem/build-chef-gem/gem-install-software-def.rb @@ -1,6 +1,7 @@ require "bundler" require "omnibus" require_relative "../build-chef-gem" +require_relative "../../../../tasks/gemfile_util" module BuildChefGem class GemInstallSoftwareDef @@ -34,16 +35,16 @@ module BuildChefGem gem_name = self.gem_name gem_version = self.gem_version - gemspec = self.gemspec + gem_metadata = self.gem_metadata lockfile_path = self.lockfile_path software.build do extend BuildChefGem if gem_version == "<skip>" - if gemspec + if gem_metadata block do - log.info(log_key) { "#{gem_name} has source #{gemspec.source.name} in #{lockfile_path}. We only cache rubygems.org installs in omnibus to keep things simple. The chef step will build #{gem_name} ..." } + log.info(log_key) { "#{gem_name} has source #{gem_metadata} in #{lockfile_path}. We only cache rubygems.org installs in omnibus to keep things simple. The chef step will build #{gem_name} ..." } end else block do @@ -95,34 +96,26 @@ module BuildChefGem end end - def gemspec - @gemspec ||= begin - old_frozen = Bundler.settings[:frozen] - Bundler.settings[:frozen] = true - begin - bundle = Bundler::Definition.build(gemfile_path, lockfile_path, nil) - dependencies = bundle.dependencies.select { |d| (d.groups - without_groups).any? } - # This is sacrilege: figure out a way we can grab the list of dependencies *without* - # requiring everything to be installed or calling private methods ... - gemspec = bundle.resolve.for(bundle.send(:expand_dependencies, dependencies)).find { |s| s.name == gem_name } - if gemspec - log.info(software.name) { "Using #{gem_name} version #{gemspec.version} from #{gemfile_path}" } - elsif bundle.resolve.find { |s| s.name == gem_name } - log.info(software.name) { "#{gem_name} not loaded from #{gemfile_path}, skipping" } - else - raise "#{gem_name} not found in #{gemfile_path} or #{lockfile_path}" - end - gemspec - ensure - Bundler.settings[:frozen] = old_frozen + def gem_metadata + @gem_metadata ||= begin + selected = GemfileUtil.select_gems(gemfile_path, without_groups: without_groups) + result = GemfileUtil.locked_gems(lockfile_path, selected)[gem_name] + if result + log.info(software.name) { "Using #{gem_name} version #{result[:version]} from #{gemfile_path}" } + result + elsif GemfileUtil.locked_gems(lockfile_path, GemfileUtil.select_gems(gemfile_path))[gem_name] + log.info(software.name) { "#{gem_name} not loaded from #{gemfile_path} because it was only in groups #{without_groups.join(", ")}, skipping" } + nil + else + raise "#{gem_name} not found in #{gemfile_path} or #{lockfile_path}" end end end def gem_version @gem_version ||= begin - if gemspec && gemspec.source.name == "rubygems repository https://rubygems.org/" - gemspec.version.to_s + if gem_metadata && URI(gem_metadata[:source]) == URI("https://rubygems.org/") + gem_metadata[:version] else "<skip>" end diff --git a/omnibus/files/chef/build-chef.rb b/omnibus/files/chef/build-chef.rb index d3e68d4e8a..8617312592 100644 --- a/omnibus/files/chef/build-chef.rb +++ b/omnibus/files/chef/build-chef.rb @@ -108,22 +108,24 @@ module BuildChef name, version = $1, $2 # rubocop is an exception, since different projects disagree next if GEMS_ALLOWED_TO_FLOAT.include?(name) - gem_pins << "override_gem #{name.inspect}, #{version.inspect}\n" + gem_pins << "gem #{name.inspect}, #{version.inspect}, override: true\n" end end + # Find the installed chef gem by looking for lib/chef.rb + chef_gem = File.expand_path("../..", shellout!("#{gem_bin} which chef").stdout.chomp) + # Figure out the path to gemfile_util from there + gemfile_util = Pathname.new(File.join(chef_gem, "tasks", "gemfile_util")) + gemfile_util = gemfile_util.relative_path_from(Pathname.new(shared_gemfile).dirname) + create_file(shared_gemfile) { <<-EOM } # Meant to be included in component Gemfiles at the end with: # # instance_eval(IO.read("#{install_dir}/Gemfile"), "#{install_dir}/Gemfile") # # Override any existing gems with our own. - def override_gem(name, *args, &block) - # If the Gemfile re-specifies something in our lockfile, ignore it. - current = dependencies.find { |dep| dep.name == name } - dependencies.delete(current) if current - gem(name, *args, &block) - end + require_relative "#{gemfile_util}" + extend GemfileUtil #{gem_pins} EOM end diff --git a/tasks/gemfile_util.rb b/tasks/gemfile_util.rb index 293c755186..90c6762ccd 100644 --- a/tasks/gemfile_util.rb +++ b/tasks/gemfile_util.rb @@ -2,17 +2,8 @@ require "bundler" require "set" module GemfileUtil - # gemspec and gem need to use absolute paths for things in order for our Gemfile - # to be *included* in another. This works around some issues in bundler 1.11.2. - def gemspec(options = {}) - options[:path] = File.expand_path(options[:path] || ".", Bundler.default_gemfile.dirname) - super - end - # - # gemspec and gem need to use absolute paths for things in order for our Gemfile - # to be *included* in another. This works around some issues in bundler 1.11.2. - # Also adds `override: true`, which allows your statement to override any other + # Adds `override: true`, which allows your statement to override any other # gem statement about the same gem in the Gemfile. # def gem(name, *args) @@ -24,7 +15,7 @@ module GemfileUtil if options[:path] # path sourced gems are assumed to be overrides. options[:override] = true - options[:path] = File.expand_path(options[:path], Bundler.default_gemfile.dirname) + # options[:path] = File.expand_path(options[:path], Bundler.default_gemfile.dirname) end # Handle override if options[:override] @@ -72,88 +63,134 @@ module GemfileUtil # # Include all gems in the locked gemfile. # - # @param gemfile Path to the Gemfile to load + # @param gemfile Path to the Gemfile to load (relative to your Gemfile) # @param groups A list of groups to include (whitelist). If not passed (or set # to nil), all gems will be selected. + # @param without_groups A list of groups to ignore. Gems will be excluded from + # the results if all groups they belong to are ignored. + # This matches bundler's `without` behavior. # @param gems A list of gems to include above and beyond the given groups. # Gems in this list must be explicitly included in the Gemfile # with a `gem "gem_name", ...` line or they will be silently # ignored. # - def include_locked_gemfile(gemfile, groups: nil, gems: []) - Bundler.ui.info "Loading locks from #{gemfile} ..." + def include_locked_gemfile(gemfile, groups: nil, without_groups: nil, gems: []) gemfile = File.expand_path(gemfile, Bundler.default_gemfile.dirname) + gems = Set.new(gems) + GemfileUtil.select_gems(gemfile, groups: nil, without_groups: nil) + specs = GemfileUtil.locked_gems("#{gemfile}.lock", gems) + specs.each do |name, version: nil, **options| + options = options.merge(override: true) + Bundler.ui.debug("Adding gem #{name}, #{version}, #{options} from #{gemfile}") + gem name, version, options + end + rescue + puts "ERROR: #{$!}" + puts $!.backtrace + raise + end - # - # Read the gemfile and inject its locks as first-class dependencies - # - old_gemfile = ENV["BUNDLE_GEMFILE"] - old_frozen = Bundler.settings[:frozen] - begin - # Set frozen to true so we don't try to install stuff. - Bundler.settings[:frozen] = true + # + # Select the desired gems, sans dependencies, from the gemfile. + # + # @param gemfile Path to the Gemfile to load + # @param groups A list of groups to include (whitelist). If not passed (or set + # to nil), all gems will be selected. + # @param without_groups A list of groups to ignore. Gems will be excluded from + # the results if all groups they belong to are ignored. + # This matches bundler's `without` behavior. + # + # @return An array of strings with the names of the given gems. + # + def self.select_gems(gemfile, groups: nil, without_groups: nil) + Bundler.with_clean_env do # Set BUNDLE_GEMFILE to the new gemfile temporarily so all bundler's things work # This works around some issues in bundler 1.11.2. ENV["BUNDLE_GEMFILE"] = gemfile - bundle = Bundler::Definition.build(gemfile, "#{gemfile}.lock", nil) - # Narrow it down to just the dependencies in the desired groups - dependencies = bundle.dependencies.select do |dep| - groups.nil? || (dep.groups & groups).any? || gems.include?(dep.name) - end - # Get the resolved specs descended from our dependencies, from the lockfile - # This is sacrilege: figure out a way we can grab the list of dependencies *without* - # requiring everything to be installed or calling private methods ... - specs = bundle.resolve.for(bundle.send(:expand_dependencies, dependencies)) - # Go through and create the actual gemfile from the given locks and - # groups. - specs.sort_by { |spec| spec.name }.each do |spec| - # bundler can't be installed by bundler so don't pin it. - next if spec.name == "bundler" + parsed_gemfile = Bundler::Dsl.new + parsed_gemfile.eval_gemfile(gemfile) + deps = parsed_gemfile.dependencies.select do |dep| + dep_groups = dep.groups + dep_groups = dep_groups & groups if groups + dep_groups = dep_groups - without_groups if without_groups + dep_groups.any? + end + deps.map { |dep| dep.name } + end + end - # Copy groups and platforms from included Gemfile - gem_metadata = {} - dep = bundle.dependencies.find { |d| d.name == spec.name } - if dep - gem_metadata[:groups] = dep.groups unless dep.groups == [:default] - gem_metadata[:platforms] = dep.platforms unless dep.platforms.empty? - end - gem_metadata[:override] = true + # + # Get all gems in the locked gemfile that start from the given gem set. + # + # @param lockfile Path to the Gemfile to load + # @param groups A list of groups to include (whitelist). If not passed (or set + # to nil), all gems will be selected. + # @param without_groups A list of groups to ignore. Gems will be excluded from + # the results if all groups they belong to are ignored. + # This matches bundler's `without` behavior. + # @param gems A list of gems to include above and beyond the given groups. + # Gems in this list must be explicitly included in the Gemfile + # with a `gem "gem_name", ...` line or they will be silently + # ignored. + # @param include_development_deps Whether to include development dependencies + # or runtime only. + # + # @return Hash[String, Hash] A hash from gem_name -> { version: <version>, source: <source>, git: <git>, path: <path>, ref: <ref> } + # + def self.locked_gems(lockfile, gems, include_development_deps: false) + # Grab all the specs from the lockfile + parsed_lockfile = Bundler::LockfileParser.new(IO.read(lockfile)) + specs = {} + parsed_lockfile.specs.each { |s| specs[s.name] = s } - # Copy source information from included Gemfile - use_version = false - case spec.source - when Bundler::Source::Rubygems - gem_metadata[:source] = spec.source.remotes.first.to_s - use_version = true - when Bundler::Source::Git - gem_metadata[:git] = spec.source.uri.to_s - gem_metadata[:ref] = spec.source.revision - when Bundler::Source::Path - gem_metadata[:path] = spec.source.path.to_s - else - raise "Unknown source #{spec.source} for gem #{spec.name}" + # Select the desired gems, as well as their dependencies + to_process = Array(gems) + results = {} + while to_process.any? + gem_name = to_process.pop + next if gem_name == "bundler" # can't be bundled. Messes things up. Stop it. + # Only process each gem once + unless results.has_key?(gem_name) + spec = specs[gem_name] + unless spec + raise "Gem #{gem_name.inspect} was requested but was not in #{lockfile}! Gems in lockfile: #{specs.keys}" end - - # Emit the dep - if use_version - Bundler.ui.debug("Adding #{spec.name}, #{spec.version}, #{gem_metadata} from #{gemfile}") - gem spec.name, spec.version, gem_metadata - else - Bundler.ui.debug("Adding #{spec.name}, #{gem_metadata} from #{gemfile}") - gem spec.name, gem_metadata + results[gem_name] = gem_metadata(spec, lockfile) + spec.dependencies.each do |dep| + if dep.type == :runtime || include_development_deps + to_process << dep.name + end end end + end + + results + end + + private - Bundler.ui.info "Loaded #{bundle.resolve.count} locked gem versions #{groups ? "from groups #{groups.join(", ")}" : ""}#{gems.empty? ? "" : " (including #{gems.join(", ")})"} from #{gemfile}" - rescue Exception - # Bundler does a bad job of rescuing. - Bundler.ui.info $! - Bundler.ui.info $!.backtrace - raise - ensure - Bundler.settings[:frozen] = old_frozen - ENV["BUNDLE_GEMFILE"] = old_gemfile + # + # Get metadata for the given Bundler spec (coming from a lockfile). + # + # @return Hash { version: <version>, git: <git>, path: <path>, source: <source>, ref: <ref> } + # + def self.gem_metadata(spec, lockfile) + # Copy source information from included Gemfile + result = {} + case spec.source + when Bundler::Source::Rubygems + result[:source] = spec.source.remotes.first.to_s + result[:version] = spec.version.to_s + when Bundler::Source::Git + result[:git] = spec.source.uri.to_s + result[:ref] = spec.source.revision + when Bundler::Source::Path + # Path is relative to the lockfile (if it's relative at all) + result[:path] = File.expand_path(spec.source.path.to_s, File.dirname(lockfile)) + else + raise "Unknown source #{spec.source} for gem #{spec.name}" end + result end + end |