summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJohn Keiser <john@johnkeiser.com>2016-04-15 12:10:40 -0700
committerJohn Keiser <john@johnkeiser.com>2016-04-18 14:21:02 -0700
commitea2593fe58037739c5c2ada517e3d988dec290ff (patch)
tree55142ceaef43080201a8d9e54f79868368241f60
parentbbc1d6ebccbd7f22fea72a9c2b76ef19f3b5beb3 (diff)
downloadchef-ea2593fe58037739c5c2ada517e3d988dec290ff.tar.gz
Make gemfile_util capable of copying groups over
-rw-r--r--.travis.yml26
-rw-r--r--Gemfile12
-rw-r--r--Gemfile.lock25
-rw-r--r--Gemfile.windows11
-rw-r--r--Gemfile.windows.lock80
-rw-r--r--Rakefile4
-rw-r--r--acceptance/Gemfile.lock8
-rw-r--r--appveyor.yml2
-rw-r--r--chef-config/lib/chef-config/package_task.rb7
-rw-r--r--kitchen-tests/Berksfile.lock4
-rw-r--r--kitchen-tests/Gemfile.lock8
-rw-r--r--omnibus/.kitchen.yml37
-rw-r--r--omnibus/Gemfile.lock8
-rw-r--r--omnibus/config/software/chef.rb7
-rw-r--r--omnibus/files/chef-appbundle/build-chef-appbundle.rb9
-rw-r--r--omnibus/files/chef-gem/build-chef-gem/gem-install-software-def.rb21
-rw-r--r--omnibus/files/chef/build-chef.rb15
-rw-r--r--pedant.gemfile25
-rw-r--r--tasks/bin/bundle-platform.bat2
-rwxr-xr-xtasks/bin/create-override-gemfile110
-rwxr-xr-xtasks/bin/run_external_test11
-rw-r--r--tasks/bundle.rb7
-rw-r--r--tasks/bundle_util.rb3
-rw-r--r--tasks/dependencies.rb2
-rw-r--r--tasks/gemfile_util.rb476
25 files changed, 604 insertions, 316 deletions
diff --git a/.travis.yml b/.travis.yml
index 7d630f154f..ec07e3b456 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -8,7 +8,7 @@ before_install:
- gem install bundler -v $(grep bundler omnibus_overrides.rb | cut -d'"' -f2)
- rm -f .bundle/config
-bundler_args: --without docgen maintenance omnibus_package aix bsd solaris windows --frozen
+bundler_args: --without docgen maintenance omnibus_package aix bsd mac_os_x solaris windows --frozen
# do not run expensive spec tests on PRs, only on branches
branches:
@@ -26,29 +26,29 @@ matrix:
- rvm: 2.1
sudo: true
script: tasks/bin/run_chef_tests
- bundler_args: --without docgen maintenance integration aix bsd solaris windows --frozen
+ bundler_args: --without docgen maintenance integration aix bsd mac_os_x solaris windows --frozen
- rvm: 2.2
sudo: true
script: tasks/bin/run_chef_tests
- bundler_args: --without docgen maintenance integration aix bsd solaris windows --frozen
+ bundler_args: --without docgen maintenance integration aix bsd mac_os_x solaris windows --frozen
- rvm: 2.3.0
sudo: true
script: tasks/bin/run_chef_tests
- bundler_args: --without docgen maintenance integration aix bsd solaris windows --frozen
+ bundler_args: --without docgen maintenance integration aix bsd mac_os_x solaris windows --frozen
- rvm: rbx
sudo: true
script: tasks/bin/run_chef_tests
- bundler_args: --without docgen maintenance integration aix bsd solaris windows --frozen
+ bundler_args: --without docgen maintenance integration aix bsd mac_os_x solaris windows --frozen
#
# External tests
#
- env:
TEST_GEM: chef-provisioning
- script: tasks/bin/run_external_test $TEST_GEM "rake spec"
+ script: tasks/bin/run_external_test $TEST_GEM rake spec
rvm: 2.2
- env:
TEST_GEM: chef-provisioning-aws
- script: tasks/bin/run_external_test $TEST_GEM "rake spec"
+ script: tasks/bin/run_external_test $TEST_GEM rake spec
rvm: 2.2
# requires vagrant
# - env: TEST_GEM=chef-rewind
@@ -61,11 +61,11 @@ matrix:
rvm: 2.2
- env:
- TEST_GEM: chef-zero
- script: tasks/bin/run_external_test $TEST_GEM "rake spec" "rake cheffs"
+ script: tasks/bin/run_external_test $TEST_GEM rake spec cheffs
rvm: 2.2
- env:
TEST_GEM: cheffish
- script: tasks/bin/run_external_test $TEST_GEM "rake spec"
+ script: tasks/bin/run_external_test $TEST_GEM rake spec
rvm: 2.2
- env:
TEST_GEM: chefspec
@@ -78,19 +78,19 @@ matrix:
rvm: 2.2.0
- env:
TEST_GEM: foodcritic
- script: tasks/bin/run_external_test $TEST_GEM "rake test"
+ script: tasks/bin/run_external_test $TEST_GEM rake test
rvm: 2.2
- env:
TEST_GEM: halite
- script: tasks/bin/run_external_test $TEST_GEM "rake spec"
+ script: tasks/bin/run_external_test $TEST_GEM rake spec
rvm: 2.2
- env:
TEST_GEM: knife-windows
- script: tasks/bin/run_external_test $TEST_GEM "rake unit_spec"
+ script: tasks/bin/run_external_test $TEST_GEM rake unit_spec
rvm: 2.2
- env:
TEST_GEM: poise
- script: tasks/bin/run_external_test $TEST_GEM "rake spec"
+ script: tasks/bin/run_external_test $TEST_GEM rake spec
rvm: 2.2
#
### START TEST KITCHEN ONLY ###
diff --git a/Gemfile b/Gemfile
index ca6354d107..9099f6c4cb 100644
--- a/Gemfile
+++ b/Gemfile
@@ -3,7 +3,7 @@ require_relative "tasks/gemfile_util"
extend GemfileUtil
source "https://rubygems.org"
-gemspec name: "chef"
+gemspec name: ($chef_platform ? "chef-#{$chef_platform}" : "chef")
gem "activesupport", "< 4.0.0", group: :compat_testing, platform: "ruby"
gem "chef-config", path: File.expand_path("../chef-config", __FILE__) if File.exist?(File.expand_path("../chef-config", __FILE__))
@@ -31,7 +31,7 @@ group(:integration) do
gem "chef-sugar"
gem "chefspec"
gem "halite"
- gem "poise", git: "https://github.com/poise/poise" # until released poise's tests succeed against chef master
+ gem "poise"
gem "knife-windows"
gem "foodcritic"
end
@@ -53,9 +53,11 @@ group(:linux, :bsd, :mac_os_x, :solaris, :windows, :ruby_prof) do
# may need to disable this in insolation on fussy builds like AIX, RHEL4, etc
gem "ruby-prof"
end
-# Everything except AIX and Windows
-group(:linux, :bsd, :mac_os_x, :solaris) do
- gem "ruby-shadow"
+unless $chef_platform == "windows"
+ # Everything except AIX and Windows
+ group(:linux, :bsd, :mac_os_x, :solaris) do
+ gem "ruby-shadow"
+ end
end
group(:development, :test) do
diff --git a/Gemfile.lock b/Gemfile.lock
index 9c30299306..a2aec12cd6 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -7,13 +7,6 @@ GIT
rubocop (= 0.39.0)
GIT
- remote: https://github.com/poise/poise
- revision: a76980685a92283c08f5b2e4526ae1f08f87de79
- specs:
- poise (2.6.2.pre)
- halite (~> 1.0)
-
-GIT
remote: https://github.com/rubysec/bundler-audit.git
revision: 4e32fca89d75f0e249671431ff38aadc02bfb28b
ref: 4e32fca
@@ -25,9 +18,9 @@ GIT
PATH
remote: .
specs:
- chef (12.10.4)
+ chef (12.10.5)
bundler (>= 1.10)
- chef-config (= 12.10.4)
+ chef-config (= 12.10.5)
chef-zero (~> 4.5)
diff-lcs (~> 1.2, >= 1.2.4)
erubis (~> 2.7)
@@ -55,7 +48,7 @@ PATH
PATH
remote: chef-config
specs:
- chef-config (12.10.4)
+ chef-config (12.10.5)
fuzzyurl (~> 0.8.0)
mixlib-config (~> 2.0)
mixlib-shellout (~> 2.0)
@@ -202,10 +195,10 @@ GEM
rspec-mocks (~> 3.2)
mixlib-cli (1.5.0)
mixlib-config (2.2.1)
- mixlib-install (1.0.6)
- artifactory (>= 2.3.0)
- mixlib-shellout (>= 2.2.6)
- mixlib-versioning (>= 1.1.0)
+ mixlib-install (1.0.7)
+ artifactory
+ mixlib-shellout
+ mixlib-versioning
mixlib-log (1.6.0)
mixlib-shellout (2.2.6)
mixlib-versioning (1.1.0)
@@ -253,6 +246,8 @@ GEM
parser (2.3.0.7)
ast (~> 2.2)
plist (3.2.0)
+ poise (2.7.0)
+ halite (~> 1.0)
polyglot (0.3.5)
powerpack (0.1.1)
proxifier (1.0.3)
@@ -373,7 +368,7 @@ DEPENDENCIES
netrc
nokogiri
octokit
- poise!
+ poise
pry
pry-byebug
pry-remote
diff --git a/Gemfile.windows b/Gemfile.windows
index cf7b6efdb0..def7a0bff6 100644
--- a/Gemfile.windows
+++ b/Gemfile.windows
@@ -29,4 +29,13 @@
#
require_relative "tasks/gemfile_util"
-GemfileUtil.include_locked_gemfile(self, "Gemfile", without_groups: [ :linux, :solaris, :aix ])
+extend GemfileUtil
+
+# Honor the main gem's lockfile
+include_locked_gemfile("Gemfile", without_groups: [ :aix, :bsd, :mac_os_x, :linux, :solaris ], copy_groups: true)
+
+# We have the same deps as the main Gemfile (except since we're Windows, we read the
+# chef-windows.gemspec instead of chef.gemspec)
+$chef_platform = "windows"
+chef_gemfile = File.expand_path("../Gemfile", __FILE__)
+instance_eval(IO.read(chef_gemfile), chef_gemfile)
diff --git a/Gemfile.windows.lock b/Gemfile.windows.lock
index 79c946b30a..8eadf2b475 100644
--- a/Gemfile.windows.lock
+++ b/Gemfile.windows.lock
@@ -7,14 +7,6 @@ GIT
rubocop (= 0.39.0)
GIT
- remote: https://github.com/poise/poise
- revision: a76980685a92283c08f5b2e4526ae1f08f87de79
- ref: a76980685a92283c08f5b2e4526ae1f08f87de79
- specs:
- poise (2.6.2.pre)
- halite (~> 1.0)
-
-GIT
remote: https://github.com/rubysec/bundler-audit.git
revision: 4e32fca89d75f0e249671431ff38aadc02bfb28b
ref: 4e32fca89d75f0e249671431ff38aadc02bfb28b
@@ -24,19 +16,37 @@ 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-universal-mingw32)
+ chef (12.10.5)
+ bundler (>= 1.10)
+ chef-config (= 12.10.5)
+ chef-zero (~> 4.5)
+ diff-lcs (~> 1.2, >= 1.2.4)
+ erubis (~> 2.7)
+ ffi-yajl (~> 2.2)
+ highline (~> 1.6, >= 1.6.9)
+ mixlib-authentication (~> 1.4)
+ mixlib-cli (~> 1.4)
+ mixlib-log (~> 1.3)
+ mixlib-shellout (~> 2.0)
+ net-sftp (~> 2.1, >= 2.1.2)
+ net-ssh (>= 2.9, < 4.0)
+ net-ssh-multi (~> 1.1)
+ ohai (>= 8.6.0.alpha.1, < 9)
+ plist (~> 3.2)
+ proxifier (~> 1.0)
+ rspec-core (~> 3.4)
+ rspec-expectations (~> 3.4)
+ rspec-mocks (~> 3.4)
+ rspec_junit_formatter (~> 0.2.0)
+ serverspec (~> 2.7)
+ specinfra (~> 2.10)
+ syslog-logger (~> 1.6)
+ uuidtools (~> 2.1.5)
+ chef (12.10.5-universal-mingw32)
bundler (>= 1.10)
- chef-config (= 12.10.4)
+ chef-config (= 12.10.5)
chef-zero (~> 4.5)
diff-lcs (~> 1.2, >= 1.2.4)
erubis (~> 2.7)
@@ -72,6 +82,14 @@ PATH
windows-api (~> 0.4.4)
wmi-lite (~> 1.0)
+PATH
+ remote: chef-config
+ specs:
+ chef-config (12.10.5)
+ fuzzyurl (~> 0.8.0)
+ mixlib-config (~> 2.0)
+ mixlib-shellout (~> 2.0)
+
GEM
remote: https://rubygems.org/
specs:
@@ -145,6 +163,7 @@ GEM
multipart-post (>= 1.2, < 3)
fauxhai (3.3.0)
net-ssh
+ ffi (1.9.10)
ffi (1.9.10-x86-mingw32)
ffi-yajl (2.2.3)
libyajl2 (~> 1.2)
@@ -214,11 +233,12 @@ GEM
rspec-mocks (~> 3.2)
mixlib-cli (1.5.0)
mixlib-config (2.2.1)
- mixlib-install (1.0.6)
- artifactory (>= 2.3.0)
- mixlib-shellout (>= 2.2.6)
- mixlib-versioning (>= 1.1.0)
+ mixlib-install (1.0.7)
+ artifactory
+ mixlib-shellout
+ mixlib-versioning
mixlib-log (1.6.0)
+ mixlib-shellout (2.2.6)
mixlib-shellout (2.2.6-universal-mingw32)
win32-process (~> 0.8.2)
wmi-lite (~> 1.0)
@@ -238,6 +258,8 @@ GEM
net-ssh-gateway (>= 1.2.0)
net-telnet (0.1.1)
netrc (0.11.0)
+ nokogiri (1.6.7.2)
+ mini_portile2 (~> 2.0.0.rc2)
nokogiri (1.6.7.2-x86-mingw32)
mini_portile2 (~> 2.0.0.rc2)
nori (2.6.0)
@@ -267,6 +289,8 @@ GEM
parser (2.3.0.7)
ast (~> 2.2)
plist (3.2.0)
+ poise (2.7.0)
+ halite (~> 1.0)
polyglot (0.3.5)
powerpack (0.1.1)
proxifier (1.0.3)
@@ -315,7 +339,6 @@ GEM
unicode-display_width (~> 1.0, >= 1.0.1)
ruby-prof (0.15.9)
ruby-progressbar (1.7.5)
- ruby-shadow (2.5.0)
rubyntlm (0.6.0)
rufus-lru (1.0.5)
sawyer (0.7.0)
@@ -383,6 +406,7 @@ GEM
yard (0.8.7.6)
PLATFORMS
+ ruby
x86-mingw32
DEPENDENCIES
@@ -397,7 +421,8 @@ DEPENDENCIES
aws-sdk-v1 (= 1.66.0)!
binding_of_caller (= 0.7.2)!
builder (= 3.2.2)!
- bundler-audit!
+ bundler
+ bundler-audit (= 0.4.0)!
byebug (= 8.2.4)!
chef!
chef-api (= 0.5.0)!
@@ -409,7 +434,7 @@ DEPENDENCIES
chef-zero (= 4.6.1)!
cheffish (= 2.0.4)!
chefspec (= 4.6.1)!
- chefstyle!
+ chefstyle (= 0.3.1)!
childprocess (= 0.5.9)!
coderay (= 1.1.1)!
colorize (= 0.7.7)!
@@ -455,7 +480,7 @@ DEPENDENCIES
mixlib-authentication (= 1.4.0)!
mixlib-cli (= 1.5.0)!
mixlib-config (= 2.2.1)!
- mixlib-install (= 1.0.6)!
+ mixlib-install (= 1.0.7)!
mixlib-log (= 1.6.0)!
mixlib-shellout (= 2.2.6)!
mixlib-versioning (= 1.1.0)!
@@ -477,7 +502,7 @@ DEPENDENCIES
overcommit (= 0.33.0)!
parser (= 2.3.0.7)!
plist (= 3.2.0)!
- poise!
+ poise (= 2.7.0)!
polyglot (= 0.3.5)!
powerpack (= 0.1.1)!
proxifier (= 1.0.3)!
@@ -500,7 +525,6 @@ DEPENDENCIES
rubocop (= 0.39.0)!
ruby-prof (= 0.15.9)!
ruby-progressbar (= 1.7.5)!
- ruby-shadow (= 2.5.0)!
rubyntlm (= 0.6.0)!
rufus-lru (= 1.0.5)!
sawyer (= 0.7.0)!
diff --git a/Rakefile b/Rakefile
index e930c75460..c085f94d7a 100644
--- a/Rakefile
+++ b/Rakefile
@@ -34,7 +34,9 @@ ChefConfig::PackageTask.new(File.expand_path("..", __FILE__), "Chef") do |packag
package.generate_version_class = true
end
# Add a conservative dependency update to version:bump (which was created by PackageTask)
-task "version:bump" => %w{version:bump_patch version:update dependencies:update[conservative]}
+task "version:bump" => %w{version:bump_patch version:update} do
+ Rake::Task["dependencies:update"].invoke("conservative")
+end
task :pedant, :chef_zero_spec
diff --git a/acceptance/Gemfile.lock b/acceptance/Gemfile.lock
index 618acb42f8..fe7ffe9513 100644
--- a/acceptance/Gemfile.lock
+++ b/acceptance/Gemfile.lock
@@ -124,10 +124,10 @@ GEM
rspec-expectations (~> 3.2)
rspec-mocks (~> 3.2)
mixlib-config (2.2.1)
- mixlib-install (1.0.6)
- artifactory (>= 2.3.0)
- mixlib-shellout (>= 2.2.6)
- mixlib-versioning (>= 1.1.0)
+ mixlib-install (1.0.7)
+ artifactory
+ mixlib-shellout
+ mixlib-versioning
mixlib-log (1.6.0)
mixlib-shellout (2.2.6)
mixlib-versioning (1.1.0)
diff --git a/appveyor.yml b/appveyor.yml
index 5a24059131..17282f9414 100644
--- a/appveyor.yml
+++ b/appveyor.yml
@@ -32,7 +32,7 @@ install:
build_script:
- bundle config --local frozen 1
- - bundle config --local without docgen:maintenance:omnibus_package:integration:aix:bsd:linux:solaris
+ - bundle config --local without docgen:maintenance:omnibus_package:integration:aix:bsd:linux:mac_os_x:solaris
- bundle install || bundle install || bundle install
test_script:
diff --git a/chef-config/lib/chef-config/package_task.rb b/chef-config/lib/chef-config/package_task.rb
index 43d01f53ef..5971510337 100644
--- a/chef-config/lib/chef-config/package_task.rb
+++ b/chef-config/lib/chef-config/package_task.rb
@@ -222,7 +222,12 @@ end
task :install => [:package] do
with_clean_env do
full_module_path = File.join(full_package_dir, module_path)
- sh %{gem install #{full_module_path}-#{version}.gem --no-rdoc --no-ri}
+ # Install the windows version on windows
+ if Gem.win_platform? && File.exist?("#{full_module_path}-#{version}-universal-mingw32.gem")
+ sh %{gem install #{full_module_path}-#{version}-universal-mingw32.gem --no-rdoc --no-ri}
+ else
+ sh %{gem install #{full_module_path}-#{version}.gem --no-rdoc --no-ri}
+ end
end
end
diff --git a/kitchen-tests/Berksfile.lock b/kitchen-tests/Berksfile.lock
index dcdafe06f3..ee5027e4b7 100644
--- a/kitchen-tests/Berksfile.lock
+++ b/kitchen-tests/Berksfile.lock
@@ -18,7 +18,7 @@ GRAPH
mysql-chef_gem (~> 0.0)
postgresql (>= 1.0.0)
xfs (>= 0.0.0)
- iis (4.1.6)
+ iis (4.1.7)
windows (>= 1.34.6)
mysql (5.6.3)
yum-mysql-community (>= 0.0.0)
@@ -35,7 +35,7 @@ GRAPH
windows (>= 0.0.0)
xml (>= 0.0.0)
yum-epel (>= 0.0.0)
- postgresql (4.0.4)
+ postgresql (4.0.6)
apt (>= 1.9.0)
build-essential (>= 0.0.0)
openssl (~> 4.0)
diff --git a/kitchen-tests/Gemfile.lock b/kitchen-tests/Gemfile.lock
index 0fad64e53d..70f40c5235 100644
--- a/kitchen-tests/Gemfile.lock
+++ b/kitchen-tests/Gemfile.lock
@@ -82,10 +82,10 @@ GEM
rspec-expectations (~> 3.2)
rspec-mocks (~> 3.2)
mixlib-config (2.2.1)
- mixlib-install (1.0.6)
- artifactory (>= 2.3.0)
- mixlib-shellout (>= 2.2.6)
- mixlib-versioning (>= 1.1.0)
+ mixlib-install (1.0.7)
+ artifactory
+ mixlib-shellout
+ mixlib-versioning
mixlib-log (1.6.0)
mixlib-shellout (2.2.6)
mixlib-versioning (1.1.0)
diff --git a/omnibus/.kitchen.yml b/omnibus/.kitchen.yml
index 6a56b92dcb..e64cadca26 100644
--- a/omnibus/.kitchen.yml
+++ b/omnibus/.kitchen.yml
@@ -11,9 +11,7 @@ driver:
cpus: 4
memory: 4096
synced_folders:
- - ['..', '/home/vagrant/chef']
- - ['../../omnibus', '/home/vagrant/omnibus']
- - ['../../omnibus-software', '/home/vagrant/omnibus-software']
+ - ['../..', '/home/vagrant']
provisioner:
name: chef_zero
@@ -21,6 +19,11 @@ provisioner:
# This is not the version of chef that we're building - this is the version
# of chef that omnibus needs to build chef/chef.
require_chef_omnibus: true
+ attributes:
+ vagrant:
+ this_key_exists_so_we_have_a_vagrant_key: true
+ chef_omnibus_install_options: -P angrychef
+ chef_omnibus_root: /opt/angrychef
platforms:
- name: centos-5.11
@@ -73,6 +76,8 @@ platforms:
- name: windows-2012r2-standard
driver:
box: chef/windows-server-2012r2-standard # private
+ provisioner:
+ chef_omnibus_root: /opscode/angrychef
attribute_defaults: &attribute_defaults
build_user: vagrant
@@ -80,27 +85,17 @@ attribute_defaults: &attribute_defaults
build_user_password: vagrant
suites:
- - name: angrychef
- attributes:
- omnibus:
- <<: *attribute_defaults
- install_dir: /opt/angrychef
- run_list:
- - omnibus::default
- - name: chef-fips
- attributes:
- omnibus:
- <<: *attribute_defaults
- install_dir: /opt/chef-fips
- run_list:
- - omnibus::default
+ # - name: angrychef
+ # attributes:
+ # omnibus:
+ # <<: *attribute_defaults
+ # install_dir: /opt/angrychef
+ # run_list:
+ # - omnibus::default
- name: chef
- provisioner:
- chef_omnibus_install_options: -P angrychef
- chef_omnibus_root: /opt/angrychef
attributes:
omnibus:
<<: *attribute_defaults
- install_dir: /opt/chef
+ install_dir: /opscode/chef
run_list:
- omnibus::default
diff --git a/omnibus/Gemfile.lock b/omnibus/Gemfile.lock
index 7e92537e3c..90bf93fa1c 100644
--- a/omnibus/Gemfile.lock
+++ b/omnibus/Gemfile.lock
@@ -130,10 +130,10 @@ GEM
rspec-mocks (~> 3.2)
mixlib-cli (1.5.0)
mixlib-config (2.2.1)
- mixlib-install (1.0.6)
- artifactory (>= 2.3.0)
- mixlib-shellout (>= 2.2.6)
- mixlib-versioning (>= 1.1.0)
+ mixlib-install (1.0.7)
+ artifactory
+ mixlib-shellout
+ mixlib-versioning
mixlib-log (1.6.0)
mixlib-shellout (2.2.6)
mixlib-versioning (1.1.0)
diff --git a/omnibus/config/software/chef.rb b/omnibus/config/software/chef.rb
index 02f4162313..866694c894 100644
--- a/omnibus/config/software/chef.rb
+++ b/omnibus/config/software/chef.rb
@@ -62,8 +62,11 @@ build do
# Prepare to install: build config, retries, job, frozen=true
# TODO Windows install seems to sometimes install already-installed gems such
- # as gherkin (and fail as a result) if you use jobs: 4.
- create_bundle_config(chef_gemfile, retries: 4, jobs: 1, frozen: true)
+ # as gherkin (and fail as a result) if you use jobs > 1.
+ # TODO Windows cannot be frozen, because Bundler doesn't understand platform-specific
+ # versions. However, on Windows we have explicit version pins for most things, so
+ # we will *probably* get the exact versions of everything we want.
+ create_bundle_config(chef_gemfile, retries: 4, jobs: windows? ? 1 : 7, frozen: !windows?)
# Install all the things. Arguments are specified in .bundle/config (see create_bundle_config)
block { log.info(log_key) { "" } }
diff --git a/omnibus/files/chef-appbundle/build-chef-appbundle.rb b/omnibus/files/chef-appbundle/build-chef-appbundle.rb
index 97a94ef5a7..5e4a6afdfd 100644
--- a/omnibus/files/chef-appbundle/build-chef-appbundle.rb
+++ b/omnibus/files/chef-appbundle/build-chef-appbundle.rb
@@ -19,12 +19,12 @@ module BuildChefAppbundle
# times in the file.
#
distribution_gemfile = Pathname(shared_gemfile).relative_path_from(Pathname(installed_gemfile)).to_s
- gemfile_text = IO.read(installed_gemfile)
- gemfile_text << <<-EOM.gsub(/^\s+/, "")
+ gemfile_text = <<-EOM.gsub(/^\s+/, "")
# Lock gems that are part of the distribution
distribution_gemfile = File.expand_path(#{distribution_gemfile.inspect}, __FILE__)
instance_eval(IO.read(distribution_gemfile), distribution_gemfile)
EOM
+ gemfile_text << IO.read(installed_gemfile)
create_file(installed_gemfile) { gemfile_text }
# Remove the gemfile.lock
@@ -50,7 +50,10 @@ module BuildChefAppbundle
# "test", "changelog" and "guard" come from berkshelf, "maintenance" comes from chef
# "tools" and "integration" come from inspec
shellout!("#{bundle_bin} config --local without #{without_groups.join(":")}", env: env, cwd: installed_path)
- shellout!("#{bundle_bin} config --local frozen 1")
+ # TODO Windows cannot be frozen, because Bundler doesn't understand platform-specific
+ # versions. However, on Windows we have explicit version pins for most things, so
+ # we will *probably* get the exact versions of everything we want.
+ shellout!("#{bundle_bin} config --local frozen 1") unless windows?
shellout!("#{bundle_bin} check", env: env, cwd: installed_path)
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 ee096b8ed9..5d77ebf7f2 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
@@ -80,7 +80,7 @@ module BuildChefGem
end
def lockfile_path
- @lockfile_path ||= "#{gemfile_path}.lock"
+ "#{gemfile_path}.lock"
end
def gem_name
@@ -98,16 +98,19 @@ module BuildChefGem
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]
+ bundle = GemfileUtil::Bundle.parse(gemfile_path, lockfile_path)
+ result = bundle.gems[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
+ if bundle.select_gems(without_groups: without_groups).include?(gem_name)
+ log.info(software.name) { "Using #{gem_name} version #{result[:version]} from #{gemfile_path}" }
+ result
+ else
+ log.info(software.name) { "#{gem_name} not loaded from #{gemfile_path} because it was only in groups #{without_groups.join(", ")}. Skipping ..." }
+ nil
+ end
else
- raise "#{gem_name} not found in #{gemfile_path} or #{lockfile_path}"
+ log.info(software.name) { "#{gem_name} was not found in #{lockfile_path}. Skipping ..." }
+ nil
end
end
end
diff --git a/omnibus/files/chef/build-chef.rb b/omnibus/files/chef/build-chef.rb
index 21bd6c2250..dd2f76e049 100644
--- a/omnibus/files/chef/build-chef.rb
+++ b/omnibus/files/chef/build-chef.rb
@@ -9,12 +9,6 @@ module BuildChef
include BuildChefGem
def create_bundle_config(gemfile, without: without_groups, retries: nil, jobs: nil, frozen: nil)
- if without
- without = without.dup
- # no_aix, no_windows groups
- without << "no_#{Omnibus::Ohai["platform"]}"
- end
-
bundle_config = File.expand_path("../.bundle/config", gemfile)
block "Put build config into #{bundle_config}: #{ { without: without, retries: retries, jobs: jobs, frozen: frozen } }" do
@@ -84,7 +78,7 @@ module BuildChef
# Emit blank line to separate different tasks
log.info(log_key) { "" }
log.info(log_key) { "Properly installing git or path sourced gem #{gem_path} using rake install" }
- shellout!("#{bundle_bin} exec #{rake_bin} install", env: chef_env, cwd: gem_path)
+ shellout!("#{bundle_bin} exec #{rake_bin} install --trace", env: chef_env, cwd: gem_path)
end
end
end
@@ -113,7 +107,7 @@ module BuildChef
end
# Find the installed chef gem by looking for lib/chef.rb
- chef_gem = File.expand_path("../..", shellout!("#{gem_bin} which chef").stdout.chomp)
+ chef_gem = File.expand_path("../..", shellout!("#{gem_bin} which chef", env: chef_env).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)
@@ -136,7 +130,10 @@ module BuildChef
bundle "lock", env: shared_gemfile_env
# Freeze the location's Gemfile.lock.
- create_bundle_config(shared_gemfile, frozen: true)
+ # TODO Windows cannot be frozen, because Bundler doesn't understand platform-specific
+ # versions. However, on Windows we have explicit version pins for most things, so
+ # we will *probably* get the exact versions of everything we want.
+ create_bundle_config(shared_gemfile, frozen: !windows?)
# Clear the now-unnecessary git caches, cached gems, and git-checked-out gems
block "Delete bundler git cache and git installs" do
diff --git a/pedant.gemfile b/pedant.gemfile
deleted file mode 100644
index 3302bccfe1..0000000000
--- a/pedant.gemfile
+++ /dev/null
@@ -1,25 +0,0 @@
-source "https://rubygems.org"
-gemspec :name => "chef"
-
-# TODO figure out how to grab this stuff from the main Gemfile
-gem "activesupport", "< 4.0.0", :group => :compat_testing, :platform => "ruby"
-
-# We are pinning chef-zero to 4.2.x until ChefFS can deal
-# with V1 api calls or chef-zero supports both v0 and v1
-gem "chef-zero", "~> 4.2.3"
-
-group(:docgen) do
- gem "tomlrb"
- gem "yard"
-end
-
-group(:development, :test) do
- gem "simplecov"
- gem 'rack', "~> 1.5.1"
-
- gem 'ruby-shadow', :platforms => :ruby unless RUBY_PLATFORM.downcase.match(/(aix|cygwin)/)
-end
-
-# If you want to load debugging tools into the bundle exec sandbox,
-# add these additional dependencies into chef/Gemfile.local
-eval(IO.read(__FILE__ + '.local'), binding) if File.exists?(__FILE__ + '.local')
diff --git a/tasks/bin/bundle-platform.bat b/tasks/bin/bundle-platform.bat
new file mode 100644
index 0000000000..d193eb0c05
--- /dev/null
+++ b/tasks/bin/bundle-platform.bat
@@ -0,0 +1,2 @@
+@ECHO OFF
+ruby "%~dpn0" %*
diff --git a/tasks/bin/create-override-gemfile b/tasks/bin/create-override-gemfile
new file mode 100755
index 0000000000..b67da025d2
--- /dev/null
+++ b/tasks/bin/create-override-gemfile
@@ -0,0 +1,110 @@
+#!/usr/bin/env ruby
+
+require "rubygems"
+require "bundler"
+
+Bundler.with_clean_env do
+ require_relative "../gemfile_util"
+
+ options = {}
+ opts = OptionParser.new do |opts|
+ opts.banner = "Usage: create-override-gemfile [OPTIONS]"
+
+ opts.on("--gemfile GEMFILE", "The Gemfile to read (default: Gemfile).") { |path| options[:gemfile_path] = path }
+ opts.on("--lockfile GEMFILE", "The lockfile to read (default: <gemfile>.lock).") { |path| options[:lockfile_path] = path }
+
+ opts.on("--group GROUP", "Groups to include (whitelist).") do |group|
+ options[:groups] ||= []
+ options[:groups] << group.to_sym
+ end
+
+ opts.on("--without GROUP", "Groups to exclude.") do |group|
+ options[:without_groups] ||= []
+ options[:without_groups] << group.to_sym
+ end
+
+ opts.on("--gem GEM", "Gems to include regardless of groups.") do |name|
+ options[:gems] ||= []
+ options[:gems] << name
+ end
+
+ opts.on("--relative-to PATH", "A path to prepend to any relative paths in the Gemfile.") do |path|
+ unless Pathname.new(path).absolute?
+ puts opts
+ raise "--relative-to #{path} was not an absolute path!"
+ end
+ options[:relative_to] = path
+ end
+
+ opts.on("--[no-]copy-groups", "Whether to copy groups over from the original Gemfile or not (default: false).") { |val| options[:copy_groups] = val }
+
+ opts.on("--[no-]override", "Whether to emit override: true on each gem line (default: false).") { |val| options[:override] = val }
+
+ opts.on("-h", "--help", "Print this message.") do
+ puts opts
+ exit(0)
+ end
+ end
+
+ args = opts.parse(ARGV)
+
+ if args.size > 0
+ puts opts
+ raise "Invalid arguments #{args}"
+ end
+
+ def create_override_gemfile(gemfile_path: "Gemfile", lockfile_path: "#{gemfile_path}.lock", groups: nil, without_groups: nil, gems: [], copy_groups: false, relative_to: ".", override: false)
+ relative_to = Pathname.new(relative_to).realpath
+ # Select the gems we want
+ bundle = GemfileUtil::Bundle.parse(gemfile_path, lockfile_path)
+ gems_to_include = bundle.select_gems(groups: groups, without_groups: without_groups)
+ gems.each do |name|
+ raise "Requested gem #{name} is not in #{gemfile_path}.lock!" if !bundle.gems[name]
+ gems_to_include[name] ||= bundle.gems[name]
+ gems_to_include[name][:dependencies].each do |dep|
+ gems_to_include[name] ||= bundle.gems[dep]
+ end
+ end
+
+ # Add the gems to the Gemfile
+ gem_root = Pathname.new(gemfile_path).dirname.realpath
+ gems_to_include.sort_by { |name, options| options[:declared_groups].empty? ? 1 : 0 }.each do |name, options|
+ comment = nil
+ options = options.dup
+ version = options.delete(:version)
+ if copy_groups
+ # Some dependencies have no groups (are not in the Gemfile--just runtime
+ # dependencies). If we actually record that they have no groups, they
+ # will *always* be installed (or perhaps never). We only want them to
+ # install if their other deps do, so we mark them with the groups of the
+ # things that brought them in (the gems that depended on them). To do
+ # this, we just leave :groups intact.
+ if options[:declared_groups].empty?
+ options.delete(:declared_groups)
+ comment = " # Transitive dependency, not actually in original Gemfile"
+ else
+ # For other things, we want to copy the actual declared_groups--the
+ # ones that were in the Gemfile. We want the same --with and --without
+ # options to include and exclude them as worked with the original
+ # Gemfile.
+ options[:groups] = options.delete(:declared_groups)
+ end
+ else
+ options.delete(:groups)
+ options.delete(:declared_groups)
+ end
+ options.delete(:dependencies)
+ options.delete(:development_dependencies)
+ options[:override] = true if override
+ options[:path] = Pathname.new(options[:path]).expand_path(gem_root).relative_path_from(relative_to).to_s if options[:path]
+ line = "gem #{name.inspect}, #{version.inspect}"
+ options.each do |name, value|
+ line << ", #{name}: #{value.inspect}"
+ end
+ line << comment if comment
+ puts line
+ end
+ end
+
+ create_override_gemfile(options)
+end
diff --git a/tasks/bin/run_external_test b/tasks/bin/run_external_test
index f1cefb9138..74f76d3229 100755
--- a/tasks/bin/run_external_test
+++ b/tasks/bin/run_external_test
@@ -20,7 +20,7 @@ TEST_GEMFILE=$TEST_GEM_ROOT/Gemfile
MODIFIED_TEST_GEMFILE=$TEST_GEMFILE.externaltest
cat <<EOM > $MODIFIED_TEST_GEMFILE
require_relative "$PROJECT_ROOT/tasks/gemfile_util"
-GemfileUtil.include_locked_gemfile(self, "$PROJECT_ROOT/Gemfile", groups: [:default], gems: ["$TEST_GEM"] + "$TEST_WITH_GEMS".split(/\s+/))
+GemfileUtil.include_locked_gemfile(self, "$PROJECT_ROOT/Gemfile", gems: ["$TEST_GEM"] + "$TEST_WITH_GEMS".split(/\s+/))
$TEST_GEM_OVERRIDES
EOM
cat $TEST_GEMFILE >> $MODIFIED_TEST_GEMFILE
@@ -38,17 +38,10 @@ export BUNDLE_IGNORE_CONFIG=true
# Use the top level bundle cache so we don't have to reinstall their packages
if [ -n "$PROJECT_BUNDLE_PATH" ]; then
export BUNDLE_PATH=$PROJECT_BUNDLE_PATH
- export BUNDLE_DISABLE_SHARED_GEMS=1
- export BUNDLE_NO_PRUNE=true
fi
export BUNDLE_FROZEN=
bundle install
export BUNDLE_FROZEN=true
bundle config
-
-# Iterate through the remaining arguments as commands
-while test ${#} -gt 0; do
- bundle exec $1
- shift
-done
+bundle exec $@
diff --git a/tasks/bundle.rb b/tasks/bundle.rb
index b0cec5f580..349f83124c 100644
--- a/tasks/bundle.rb
+++ b/tasks/bundle.rb
@@ -17,6 +17,7 @@
require_relative "bundle_util"
require_relative "../version_policy"
+require "fileutils"
desc "Tasks to work with the main Gemfile and Gemfile.<platform>"
namespace :bundle do
@@ -35,7 +36,9 @@ namespace :bundle do
puts "-------------------------------------------------------------------"
puts "Updating Gemfile.#{platform}.lock ..."
puts "-------------------------------------------------------------------"
- bundle "lock", gemfile: "Gemfile.#{platform}", platform: platform, delete_gemfile_lock: true
+ puts "Copy Gemfile.lock to Gemfile.#{platform}.lock ..."
+ FileUtils.cp(File.join(project_root, "Gemfile.lock"), File.join(project_root, "Gemfile.#{platform}.lock"))
+ bundle "lock", gemfile: "Gemfile.#{platform}", platform: platform
end
end
end
@@ -55,6 +58,8 @@ namespace :bundle do
puts "-------------------------------------------------------------------"
puts "Updating Gemfile.#{platform}.lock (conservatively) ..."
puts "-------------------------------------------------------------------"
+ puts "Copy Gemfile.lock to Gemfile.#{platform}.lock ..."
+ FileUtils.cp(File.join(project_root, "Gemfile.lock"), File.join(project_root, "Gemfile.#{platform}.lock"))
bundle "lock", gemfile: "Gemfile.#{platform}", platform: platform
end
end
diff --git a/tasks/bundle_util.rb b/tasks/bundle_util.rb
index 91ffa1f317..ee75610cac 100644
--- a/tasks/bundle_util.rb
+++ b/tasks/bundle_util.rb
@@ -1,3 +1,4 @@
+require "bundler"
require "shellwords"
module BundleUtil
@@ -56,7 +57,7 @@ module BundleUtil
# Run the bundle command
ruby_platforms = platform ? PLATFORMS[platform].join(" ") : "ruby"
- cmd = Shellwords.join([bundle_platform, ruby_platforms, *args])
+ cmd = Shellwords.join([Gem.ruby, "-S", bundle_platform, ruby_platforms, *args])
puts "#{prefix}#{Shellwords.join(["bundle", *args])}#{platform ? " for #{platform} platform" : ""}:"
with_gemfile(gemfile) do
puts "#{prefix}BUNDLE_GEMFILE=#{gemfile}"
diff --git a/tasks/dependencies.rb b/tasks/dependencies.rb
index b2eabffb09..0fe3907ce1 100644
--- a/tasks/dependencies.rb
+++ b/tasks/dependencies.rb
@@ -29,7 +29,6 @@ namespace :dependencies do
dependencies:update_omnibus_gemfile_lock
dependencies:update_acceptance_gemfile_lock
dependencies:update_kitchen_tests_gemfile_lock
- dependencies:update_omnibus_berksfile_lock
dependencies:update_kitchen_tests_berksfile_lock
}
@@ -81,7 +80,6 @@ namespace :dependencies do
end
gemfile_lock_task :update_omnibus_gemfile_lock, dirs: %w{omnibus}
- berksfile_lock_task :update_omnibus_berksfile_lock, dirs: %w{omnibus}
gemfile_lock_task :update_acceptance_gemfile_lock, dirs: %w{
acceptance
acceptance/fips/test/integration/fips/serverspec
diff --git a/tasks/gemfile_util.rb b/tasks/gemfile_util.rb
index 96dfcd78a2..62d8cfdf0b 100644
--- a/tasks/gemfile_util.rb
+++ b/tasks/gemfile_util.rb
@@ -1,4 +1,6 @@
+require "rubygems"
require "bundler"
+require "shellwords"
require "set"
module GemfileUtil
@@ -7,212 +9,376 @@ module GemfileUtil
# gem statement about the same gem in the Gemfile.
#
def gem(name, *args)
- Bundler.ui.debug "gem #{name}, #{args.join(", ")}"
- current_dep = dependencies.find { |dep| dep.name == name }
-
- # Set path to absolute in case this is an included Gemfile in bundler 1.11.2 and below
options = args[-1].is_a?(Hash) ? args[-1] : {}
- 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)
- end
- # Handle override
- if options[:override]
- override = true
- options.delete(:override)
- if current_dep
- dependencies.delete(current_dep)
- end
- else
- # If an override gem already exists, and we're not an override gem,
- # ignore this gem in favor of the override (but warn if they don't match)
- if overridden_gems.include?(name)
- args.pop if args[-1].is_a?(Hash)
- version = args || [">=0"]
- desired_dep = Bundler::Dependency.new(name, version, options.dup)
- if desired_dep =~ current_dep
- Bundler.ui.debug "Replaced Gemfile dependency #{desired_dep} (#{desired_dep.source}) with override gem #{current_dep} (#{current_dep.source})"
- else
- Bundler.ui.warn "Replaced Gemfile dependency #{desired_dep} (#{desired_dep.source}) with incompatible override gem #{current_dep} (#{current_dep.source})"
- end
+
+ # Unless we're finished with everything, ignore gems that are being overridden
+ unless overridden_gems == :finished
+ # If it's a path or override gem, it overrides whatever else is there.
+ if options[:path] || options[:override]
+ options.delete(:override)
+ warn_if_replacing(name, overridden_gems[name], args)
+ overridden_gems[name] = args
+ return
+
+ # If there's an override gem, and we're *not* an override gem, don't do anything
+ elsif overridden_gems[name]
+ warn_if_replacing(name, args, overridden_gems[name])
return
end
end
- # Add the gem normally
+ # Otherwise, add the gem normally
super
+ rescue
+ puts $!.backtrace
+ raise
+ end
- overridden_gems << name if override
+ def overridden_gems
+ @overridden_gems ||= {}
+ end
- # Emit a warning if we're replacing a dep that doesn't match
- if current_dep && override
- added_dep = dependencies.find { |dep| dep.name == name }
- if added_dep =~ current_dep
- Bundler.ui.debug "Replaced Gemfile dependency #{current_dep} (#{current_dep.source}) with override gem #{added_dep} (#{added_dep.source})"
- else
- Bundler.ui.warn "Replaced Gemfile dependency #{current_dep} (#{current_dep.source}) with incompatible override gem #{added_dep} (#{added_dep.source})"
- end
- end
+ #
+ # Just before we finish the Gemfile, finish up the override gems
+ #
+ def to_definition(*args)
+ complete_overrides
+ super
end
- def overridden_gems
- @overridden_gems ||= Set.new
+ def complete_overrides
+ to_override = overridden_gems
+ unless to_override == :finished
+ @overridden_gems = :finished
+ to_override.each do |name, args|
+ gem name, *args
+ end
+ end
end
#
# Include all gems in the locked gemfile.
#
- # @param gemfile Path to the Gemfile to load (relative to your Gemfile)
+ # @param gemfile_path Path to the Gemfile to load (relative to your Gemfile)
+ # @param lockfile_path Path to the Gemfile to load (relative to your Gemfile).
+ # Defaults to <gemfile_path>.lock.
# @param groups A list of groups to include (whitelist). If not passed (or set
- # to nil), all gems will be selected.
+ # 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.
+ # 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.
+ # Gems in this list must be explicitly included in the Gemfile
+ # with a `gem "gem_name", ...` line or they will be silently
+ # ignored.
+ # @param copy_groups Whether to copy the groups over from the old lockfile to
+ # the new. Use this when the new lockfile has the same convention for
+ # groups as the old. Defaults to `false`.
#
- 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
+ def include_locked_gemfile(gemfile_path, lockfile_path = "#{gemfile_path}.lock", groups: nil, without_groups: nil, gems: [], copy_groups: false)
+ # Parse the desired lockfile
+ gemfile_path = Pathname.new(gemfile_path).expand_path(Bundler.default_gemfile.dirname).realpath
+ lockfile_path = Pathname.new(lockfile_path).expand_path(Bundler.default_gemfile.dirname).realpath
+
+ # Calculate relative_to
+ relative_to = Bundler.default_gemfile.dirname.realpath
+
+ # Call out to create-override-gemfile to read the Gemfile+Gemfile.lock (bundler does not work well if you do two things in one process)
+ create_override_gemfile_bin = File.expand_path("../bin/create-override-gemfile", __FILE__)
+ arguments = [
+ "--gemfile", gemfile_path,
+ "--lockfile", lockfile_path,
+ "--override"
+ ]
+ arguments += [ "--relative-to", relative_to ] if relative_to != "."
+ arguments += Array(groups).flat_map { |group| [ "--group", group ] }
+ arguments += Array(without_groups).flat_map { |without| [ "--without", without ] }
+ arguments += Array(gems).flat_map { |name| [ "--gem", name ] }
+ arguments << "--copy-groups" if copy_groups
+ cmd = Shellwords.join([ Gem.ruby, "-S", create_override_gemfile_bin, *arguments ])
+ output = nil
+ Bundler.ui.info("> #{cmd}")
+ Bundler.with_clean_env do
+ output = `#{cmd}`
end
- rescue
- puts "ERROR: #{$!}"
- puts $!.backtrace
- raise
+ instance_eval(output, cmd, 1)
end
#
# Include all gems in the locked gemfile.
#
# @param current_gemfile The Gemfile you are currently loading (`self`).
- # @param gemfile Path to the Gemfile to load (relative to your Gemfile)
+ # @param gemfile_path Path to the Gemfile to load (relative to your Gemfile)
+ # @param lockfile_path Path to the Gemfile to load (relative to your Gemfile).
+ # Defaults to <gemfile_path>.lock.
# @param groups A list of groups to include (whitelist). If not passed (or set
- # to nil), all gems will be selected.
+ # 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.
+ # 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.
+ # Gems in this list must be explicitly included in the Gemfile
+ # with a `gem "gem_name", ...` line or they will be silently
+ # ignored.
+ # @param copy_groups Whether to copy the groups over from the old lockfile to
+ # the new. Use this when the new lockfile has the same convention for
+ # groups as the old. Defaults to `false`.
#
- def self.include_locked_gemfile(current_gemfile, gemfile, groups: nil, without_groups: nil, gems: [])
+ def self.include_locked_gemfile(current_gemfile, gemfile_path, lockfile_path = "#{gemfile_path}.lock", groups: nil, without_groups: nil, gems: [], copy_groups: false)
current_gemfile.instance_eval do
extend GemfileUtil
- include_locked_gemfile(gemfile, groups: groups, without_groups: without_groups, gems: [])
+ include_locked_gemfile(gemfile_path, lockfile_path, groups: groups, without_groups: without_groups, gems: gems, copy_groups: copy_groups)
end
end
+ def warn_if_replacing(name, old_args, new_args)
+ return if !old_args || !new_args
+ if args_to_dep(name, *old_args) =~ args_to_dep(name, *new_args)
+ Bundler.ui.debug "Replaced Gemfile dependency #{name} (#{old_args}) with (#{new_args})"
+ else
+ Bundler.ui.warn "Replaced Gemfile dependency #{name} (#{old_args}) with (#{new_args})"
+ end
+ end
+
+ def args_to_dep(name, *version, **options)
+ version = [">= 0"] if version.empty?
+ Bundler::Dependency.new(name, version, options)
+ end
+
#
- # 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.
+ # Reads a bundle, including a gemfile and lockfile.
#
- # @return An array of strings with the names of the given gems.
+ # Does no validation, does not update the lockfile or its gems in any way.
#
- 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
-
- parsed_gemfile = Bundler::Dsl.new
- parsed_gemfile.eval_gemfile(gemfile)
- deps = parsed_gemfile.dependencies.select do |dep|
- dep_groups = dep.groups
+ class Bundle
+ #
+ # Parse the given gemfile/lockfile pair.
+ #
+ # @return [Bundle] The parsed bundle.
+ #
+ def self.parse(gemfile_path, lockfile_path = "#{gemfile_path}.lock")
+ result = new(gemfile_path, lockfile_path)
+ result.gems
+ result
+ end
+
+ #
+ # Create a new Bundle to parse the given gemfile/lockfile pair.
+ #
+ def initialize(gemfile_path, lockfile_path = "#{gemfile_path}.lock")
+ @gemfile_path = gemfile_path
+ @lockfile_path = lockfile_path
+ end
+
+ #
+ # The path to the Gemfile
+ #
+ attr_reader :gemfile_path
+
+ #
+ # The path to the Lockfile
+ #
+ attr_reader :lockfile_path
+
+ #
+ # The list of gems.
+ #
+ # @return [Hash<String, Hash>] The resulting gems, where key = gem_name, and the
+ # hash has:
+ # - version: version of the gem.
+ # - source info (:source/:git/:ref/:path) from the lockfile
+ # - dependencies: A list of gem names this gem has a runtime
+ # dependency on. Dependencies are transitive: if A depends on B,
+ # and B depends on C, then A has C in its :dependencies list.
+ # - development_dependencies: - A list of gem names this gem has a
+ # development dependency on. Dependencies are transitive: if A
+ # depends on B, and B depends on C, then A has C in its
+ # :development_dependencies list. development dependencies *include*
+ # runtime dependencies.
+ # - groups: The list of groups (symbols) this gem is in. Groups
+ # are transitive: if A has a runtime dependency on B, and A is
+ # in group X, then B is also in group X.
+ # - declared_groups: The list of groups (symbols) this gem was
+ # declared in the Gemfile.
+ #
+ def gems
+ @gems ||= begin
+ gems = locks.dup
+ gems.each do |name, g|
+ if gem_declarations.has_key?(name)
+ g[:declared_groups] = gem_declarations[name][:groups]
+ else
+ g[:declared_groups] = []
+ end
+ g[:groups] = g[:declared_groups].dup
+ end
+ # Transitivize groups (since dependencies are already transitive, this is easy)
+ gems.each do |name, g|
+ g[:dependencies].each do |dep|
+ gems[dep][:groups] |= gems[name][:declared_groups].dup
+ end
+ end
+ gems
+ end
+ end
+
+ #
+ # Get the gems (and their deps) in the given group.
+ #
+ # @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 regardless of what groups are included.
+ #
+ # @return Hash[String, Hash] The resulting gems, where key = gem_name, and the
+ # hash has:
+ # - version: version of the gem.
+ # - source info (:source/:git/:ref/:path) from the lockfile
+ # - dependencies: A list of gem names this gem has a runtime
+ # dependency on. Dependencies are transitive: if A depends on B,
+ # and B depends on C, then A has C in its :dependencies list.
+ # - development_dependencies: - A list of gem names this gem has a
+ # development dependency on. Dependencies are transitive: if A
+ # depends on B, and B depends on C, then A has C in its
+ # :development_dependencies list. development dependencies
+ # *include* runtime dependencies.
+ # - groups: The list of groups (symbols) this gem is in. Groups
+ # are transitive: if A has a runtime dependency on B, and A is
+ # in group X, then B is also in group X.
+ # - declared_groups: The list of groups (symbols) this gem was
+ # declared in the Gemfile.
+ #
+ def select_gems(groups: nil, without_groups: nil)
+ # First, select the gems that match
+ result = {}
+ gems.each do |name, g|
+ dep_groups = g[:declared_groups] - [ :only_a_runtime_dependency_of_other_gems ]
dep_groups = dep_groups & groups if groups
dep_groups = dep_groups - without_groups if without_groups
- dep_groups.any?
+ if dep_groups.any?
+ result[name] ||= g
+ g[:dependencies].each do |dep|
+ result[dep] ||= gems[dep]
+ end
+ end
end
- deps.map { |dep| dep.name }
+ result
end
- end
- #
- # 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 }
-
- # 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}"
+ #
+ # Get all locks from the given lockfile.
+ #
+ # @return Hash[String, Hash] The resulting gems, where key = gem_name, and the
+ # hash has:
+ # - version: version of the gem.
+ # - source info (:source/:git/:ref/:path)
+ # - dependencies: A list of gem names this gem has a runtime
+ # dependency on. Dependencies are transitive: if A depends on B,
+ # and B depends on C, then A has C in its :dependencies list.
+ # - development_dependencies: - A list of gem names this gem has a
+ # development dependency on. Dependencies are transitive: if A
+ # depends on B, and B depends on C, then A has C in its
+ # :development_dependencies list. development dependencies *include*
+ # runtime dependencies.
+ #
+ def locks
+ @locks ||= begin
+ # Grab all the specs from the lockfile
+ locks = {}
+ parsed_lockfile = Bundler::LockfileParser.new(IO.read(lockfile_path))
+ parsed_lockfile.specs.each do |spec|
+ # Never include bundler, it can't be bundled and doesn't put itself in
+ # the lockfile correctly anyway
+ next if spec.name == "bundler"
+ lock = lock_source_metadata(spec)
+ lock[:version] = spec.version.to_s
+ runtime = spec.dependencies.select { |dep| dep.type == :runtime }
+ lock[:dependencies] = Set.new(runtime.map { |dep| dep.name })
+ lock[:development_dependencies] = Set.new(spec.dependencies.map { |dep| dep.name })
+ lock[:dependencies].delete("bundler")
+ lock[:development_dependencies].delete("bundler")
+ locks[spec.name] = lock
+ end
+
+ # Transitivize the deps.
+ locks.each do |name, lock|
+ lock[:dependencies] = transitive_dependencies(locks, name, :dependencies)
+ lock[:development_dependencies] = transitive_dependencies(locks, name, :development_dependencies)
end
- results[gem_name] = gem_metadata(spec, lockfile)
- spec.dependencies.each do |dep|
- if dep.type == :runtime || include_development_deps
- to_process << dep.name
+
+ locks
+ end
+ end
+
+ #
+ # Get all desired gems, sans dependencies, from the gemfile.
+ #
+ # @param gemfile Path to the Gemfile to load
+ #
+ # @return Hash<String, Hash> An array of hashes where key = gem name and value
+ # has :groups (an array of symbols representing the groups the gem
+ # is in). :groups are not transitive, since we don't know the
+ # dependency tree yet.
+ #
+ def gem_declarations
+ @gem_declarations ||= begin
+ 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_path
+
+ parsed_gemfile = Bundler::Dsl.new
+ parsed_gemfile.eval_gemfile(gemfile_path)
+ parsed_gemfile.complete_overrides if parsed_gemfile.respond_to?(:complete_overrides)
+
+ result = {}
+ parsed_gemfile.dependencies.each do |dep|
+ groups = dep.groups.empty? ? [:default] : dep.groups
+ result[dep.name] = { groups: groups, platforms: dep.platforms }
end
+ result
end
end
end
- results
- end
+ private
- private
+ #
+ # Given a bunch of locks (name -> { dependencies: [name,name] }) and a
+ # dependency name, add its dependencies to the result transitively.
+ #
+ def transitive_dependencies(locks, name, dep_key, result = Set.new)
+ locks[name][dep_key].each do |dep|
+ # Only ever add a dep once, so we don't infinitely recurse
+ if result.add?(dep)
+ transitive_dependencies(locks, dep, dep_key, result)
+ end
+ end
+ result
+ end
- #
- # 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}"
+ #
+ # Get source and version metadata for the given Bundler spec (coming from a lockfile).
+ #
+ # @return Hash { version: <version>, git: <git>, path: <path>, source: <source>, ref: <ref> }
+ #
+ def lock_source_metadata(spec)
+ # Copy source information from included Gemfile
+ result = {}
+ case spec.source
+ when Bundler::Source::Rubygems
+ result[:source] = spec.source.remotes.first.to_s
+ when Bundler::Source::Git
+ result[:git] = spec.source.uri.to_s
+ result[:ref] = spec.source.revision
+ when Bundler::Source::Path
+ result[:path] = spec.source.path.to_s
+ else
+ raise "Unknown source #{spec.source} for gem #{spec.name}"
+ end
+ result
end
- result
end
-
end