1# frozen_string_literal: true 2 3RSpec.describe "bundle binstubs <gem>" do 4 context "when the gem exists in the lockfile" do 5 it "sets up the binstub" do 6 install_gemfile <<-G 7 source "file://#{gem_repo1}" 8 gem "rack" 9 G 10 11 bundle "binstubs rack" 12 13 expect(bundled_app("bin/rackup")).to exist 14 end 15 16 it "does not install other binstubs" do 17 install_gemfile <<-G 18 source "file://#{gem_repo1}" 19 gem "rack" 20 gem "rails" 21 G 22 23 bundle "binstubs rails" 24 25 expect(bundled_app("bin/rackup")).not_to exist 26 expect(bundled_app("bin/rails")).to exist 27 end 28 29 it "does install multiple binstubs" do 30 install_gemfile <<-G 31 source "file://#{gem_repo1}" 32 gem "rack" 33 gem "rails" 34 G 35 36 bundle "binstubs rails rack" 37 38 expect(bundled_app("bin/rackup")).to exist 39 expect(bundled_app("bin/rails")).to exist 40 end 41 42 it "allows installing all binstubs" do 43 install_gemfile! <<-G 44 source "file://#{gem_repo1}" 45 gem "rails" 46 G 47 48 bundle! :binstubs, :all => true 49 50 expect(bundled_app("bin/rails")).to exist 51 expect(bundled_app("bin/rake")).to exist 52 end 53 54 it "displays an error when used without any gem" do 55 install_gemfile <<-G 56 source "file://#{gem_repo1}" 57 gem "rack" 58 G 59 60 bundle "binstubs" 61 expect(exitstatus).to eq(1) if exitstatus 62 expect(out).to include("`bundle binstubs` needs at least one gem to run.") 63 end 64 65 it "displays an error when used with --all and gems" do 66 install_gemfile <<-G 67 source "file://#{gem_repo1}" 68 gem "rack" 69 G 70 71 bundle "binstubs rack", :all => true 72 expect(last_command).to be_failure 73 expect(last_command.bundler_err).to include("Cannot specify --all with specific gems") 74 end 75 76 context "when generating bundle binstub outside bundler" do 77 it "should abort" do 78 install_gemfile <<-G 79 source "file://#{gem_repo1}" 80 gem "rack" 81 G 82 83 bundle "binstubs rack" 84 85 File.open("bin/bundle", "wb") do |file| 86 file.print "OMG" 87 end 88 89 sys_exec "bin/rackup" 90 91 expect(last_command.stderr).to include("was not generated by Bundler") 92 end 93 end 94 95 context "the bundle binstub" do 96 before do 97 if system_bundler_version == :bundler 98 system_gems :bundler 99 elsif system_bundler_version 100 build_repo4 do 101 build_gem "bundler", system_bundler_version do |s| 102 s.executables = "bundle" 103 s.bindir = "exe" 104 s.write "exe/bundle", "puts %(system bundler #{system_bundler_version}\\n\#{ARGV.inspect})" 105 end 106 end 107 system_gems "bundler-#{system_bundler_version}", :gem_repo => gem_repo4 108 end 109 build_repo2 do 110 build_gem "prints_loaded_gems", "1.0" do |s| 111 s.executables = "print_loaded_gems" 112 s.bindir = "exe" 113 s.write "exe/print_loaded_gems", <<-R 114 specs = Gem.loaded_specs.values.reject {|s| Bundler.rubygems.spec_default_gem?(s) } 115 puts specs.map(&:full_name).sort.inspect 116 R 117 end 118 end 119 install_gemfile! <<-G 120 source "file://#{gem_repo2}" 121 gem "rack" 122 gem "prints_loaded_gems" 123 G 124 bundle! "binstubs bundler rack prints_loaded_gems" 125 end 126 127 # When environment has a same version of bundler as default gems. 128 # `system_gems "bundler-x.y.z"` will detect system binstub. 129 # We need to avoid it by virtual version of bundler. 130 let(:system_bundler_version) { Gem::Version.new(Bundler::VERSION).bump.to_s } 131 132 context "when system bundler was used" do 133 # Support master branch of bundler 134 if ENV["BUNDLER_SPEC_SUB_VERSION"] 135 let(:system_bundler_version) { Bundler::VERSION } 136 end 137 it "runs bundler" do 138 sys_exec! "#{bundled_app("bin/bundle")} install" 139 expect(out).to eq %(system bundler #{system_bundler_version}\n["install"]) 140 end 141 end 142 143 context "when BUNDLER_VERSION is set" do 144 let(:system_bundler_version) { Bundler::VERSION } 145 146 it "runs the correct version of bundler" do 147 sys_exec "BUNDLER_VERSION='999.999.999' #{bundled_app("bin/bundle")} install" 148 expect(exitstatus).to eq(42) if exitstatus 149 expect(last_command.stderr).to include("Activating bundler (999.999.999) failed:"). 150 and include("To install the version of bundler this project requires, run `gem install bundler -v '999.999.999'`") 151 end 152 end 153 154 context "when a lockfile exists with a locked bundler version" do 155 let(:system_bundler_version) { Bundler::VERSION } 156 157 it "runs the correct version of bundler when the version is newer" do 158 lockfile lockfile.gsub(system_bundler_version, "999.999.999") 159 sys_exec "#{bundled_app("bin/bundle")} install" 160 expect(exitstatus).to eq(42) if exitstatus 161 expect(last_command.stderr).to include("Activating bundler (999.999.999) failed:"). 162 and include("To install the version of bundler this project requires, run `gem install bundler -v '999.999.999'`") 163 end 164 165 it "runs the correct version of bundler when the version is older" do 166 simulate_bundler_version "55" 167 lockfile lockfile.gsub(system_bundler_version, "44.0") 168 sys_exec "#{bundled_app("bin/bundle")} install" 169 expect(exitstatus).to eq(42) if exitstatus 170 expect(last_command.stderr).to include("Activating bundler (44.0) failed:"). 171 and include("To install the version of bundler this project requires, run `gem install bundler -v '44.0'`") 172 end 173 174 it "runs the correct version of bundler when the version is a pre-release" do 175 simulate_bundler_version "55" 176 lockfile lockfile.gsub(system_bundler_version, "2.12.0.a") 177 sys_exec "#{bundled_app("bin/bundle")} install" 178 expect(exitstatus).to eq(42) if exitstatus 179 expect(last_command.stderr).to include("Activating bundler (2.12.0.a) failed:"). 180 and include("To install the version of bundler this project requires, run `gem install bundler -v '2.12.0.a'`") 181 end 182 end 183 184 context "when update --bundler is called" do 185 before { lockfile.gsub(system_bundler_version, "1.1.1") } 186 187 it "calls through to the latest bundler version" do 188 sys_exec! "#{bundled_app("bin/bundle")} update --bundler" 189 expect(last_command.stdout).to eq %(system bundler #{system_bundler_version}\n["update", "--bundler"]) 190 end 191 192 it "calls through to the explicit bundler version" do 193 sys_exec "#{bundled_app("bin/bundle")} update --bundler=999.999.999" 194 expect(exitstatus).to eq(42) if exitstatus 195 expect(last_command.stderr).to include("Activating bundler (999.999.999) failed:"). 196 and include("To install the version of bundler this project requires, run `gem install bundler -v '999.999.999'`") 197 end 198 end 199 200 context "without a lockfile" do 201 it "falls back to the latest installed bundler" do 202 FileUtils.rm bundled_app("Gemfile.lock") 203 sys_exec! bundled_app("bin/bundle").to_s 204 expect(out).to eq "system bundler #{system_bundler_version}\n[]" 205 end 206 end 207 208 context "using another binstub" do 209 let(:system_bundler_version) { :bundler } 210 it "loads all gems" do 211 sys_exec! bundled_app("bin/print_loaded_gems").to_s 212 # RG < 2.0.14 didn't have a `Gem::Specification#default_gem?` 213 # This is dirty detection for old RG versions. 214 if File.dirname(Bundler.load.specs["bundler"][0].loaded_from) =~ %r{specifications/default} 215 expect(out).to eq %(["prints_loaded_gems-1.0", "rack-1.2"]) 216 else 217 expect(out).to eq %(["bundler-#{Bundler::VERSION}", "prints_loaded_gems-1.0", "rack-1.2"]) 218 end 219 end 220 221 context "when requesting a different bundler version" do 222 before { lockfile lockfile.gsub(Bundler::VERSION, "999.999.999") } 223 224 it "attempts to load that version", :ruby_repo do 225 sys_exec bundled_app("bin/rackup").to_s 226 expect(exitstatus).to eq(42) if exitstatus 227 expect(last_command.stderr).to include("Activating bundler (999.999.999) failed:"). 228 and include("To install the version of bundler this project requires, run `gem install bundler -v '999.999.999'`") 229 end 230 end 231 end 232 end 233 234 it "installs binstubs from git gems" do 235 FileUtils.mkdir_p(lib_path("foo/bin")) 236 FileUtils.touch(lib_path("foo/bin/foo")) 237 build_git "foo", "1.0", :path => lib_path("foo") do |s| 238 s.executables = %w[foo] 239 end 240 install_gemfile <<-G 241 gem "foo", :git => "#{lib_path("foo")}" 242 G 243 244 bundle "binstubs foo" 245 246 expect(bundled_app("bin/foo")).to exist 247 end 248 249 it "installs binstubs from path gems" do 250 FileUtils.mkdir_p(lib_path("foo/bin")) 251 FileUtils.touch(lib_path("foo/bin/foo")) 252 build_lib "foo", "1.0", :path => lib_path("foo") do |s| 253 s.executables = %w[foo] 254 end 255 install_gemfile <<-G 256 gem "foo", :path => "#{lib_path("foo")}" 257 G 258 259 bundle "binstubs foo" 260 261 expect(bundled_app("bin/foo")).to exist 262 end 263 264 it "sets correct permissions for binstubs" do 265 with_umask(0o002) do 266 install_gemfile <<-G 267 source "file://#{gem_repo1}" 268 gem "rack" 269 G 270 271 bundle "binstubs rack" 272 binary = bundled_app("bin/rackup") 273 expect(File.stat(binary).mode.to_s(8)).to eq("100775") 274 end 275 end 276 277 context "when using --shebang" do 278 it "sets the specified shebang for the the binstub" do 279 install_gemfile <<-G 280 source "file://#{gem_repo1}" 281 gem "rack" 282 G 283 284 bundle "binstubs rack --shebang jruby" 285 286 expect(File.open("bin/rackup").gets).to eq("#!/usr/bin/env jruby\n") 287 end 288 end 289 end 290 291 context "when the gem doesn't exist" do 292 it "displays an error with correct status" do 293 install_gemfile <<-G 294 source "file://#{gem_repo1}" 295 G 296 297 bundle "binstubs doesnt_exist" 298 299 expect(exitstatus).to eq(7) if exitstatus 300 expect(out).to include("Could not find gem 'doesnt_exist'.") 301 end 302 end 303 304 context "--path" do 305 it "sets the binstubs dir" do 306 install_gemfile <<-G 307 source "file://#{gem_repo1}" 308 gem "rack" 309 G 310 311 bundle "binstubs rack --path exec" 312 313 expect(bundled_app("exec/rackup")).to exist 314 end 315 316 it "setting is saved for bundle install", :bundler => "< 2" do 317 install_gemfile <<-G 318 source "file://#{gem_repo1}" 319 gem "rack" 320 gem "rails" 321 G 322 323 bundle! "binstubs rack", forgotten_command_line_options([:path, :bin] => "exec") 324 bundle! :install 325 326 expect(bundled_app("exec/rails")).to exist 327 end 328 end 329 330 context "with --standalone option" do 331 before do 332 install_gemfile <<-G 333 source "file://#{gem_repo1}" 334 gem "rack" 335 G 336 end 337 338 it "generates a standalone binstub" do 339 bundle! "binstubs rack --standalone" 340 expect(bundled_app("bin/rackup")).to exist 341 end 342 343 it "generates a binstub that does not depend on rubygems or bundler" do 344 bundle! "binstubs rack --standalone" 345 expect(File.read(bundled_app("bin/rackup"))).to_not include("Gem.bin_path") 346 end 347 348 context "when specified --path option" do 349 it "generates a standalone binstub at the given path" do 350 bundle! "binstubs rack --standalone --path foo" 351 expect(bundled_app("foo/rackup")).to exist 352 end 353 end 354 end 355 356 context "when the bin already exists" do 357 it "doesn't overwrite and warns" do 358 FileUtils.mkdir_p(bundled_app("bin")) 359 File.open(bundled_app("bin/rackup"), "wb") do |file| 360 file.print "OMG" 361 end 362 363 install_gemfile <<-G 364 source "file://#{gem_repo1}" 365 gem "rack" 366 G 367 368 bundle "binstubs rack" 369 370 expect(bundled_app("bin/rackup")).to exist 371 expect(File.read(bundled_app("bin/rackup"))).to eq("OMG") 372 expect(out).to include("Skipped rackup") 373 expect(out).to include("overwrite skipped stubs, use --force") 374 end 375 376 context "when using --force" do 377 it "overwrites the binstub" do 378 FileUtils.mkdir_p(bundled_app("bin")) 379 File.open(bundled_app("bin/rackup"), "wb") do |file| 380 file.print "OMG" 381 end 382 383 install_gemfile <<-G 384 source "file://#{gem_repo1}" 385 gem "rack" 386 G 387 388 bundle "binstubs rack --force" 389 390 expect(bundled_app("bin/rackup")).to exist 391 expect(File.read(bundled_app("bin/rackup"))).not_to eq("OMG") 392 end 393 end 394 end 395 396 context "when the gem has no bins" do 397 it "suggests child gems if they have bins" do 398 install_gemfile <<-G 399 source "file://#{gem_repo1}" 400 gem "rack-obama" 401 G 402 403 bundle "binstubs rack-obama" 404 expect(out).to include("rack-obama has no executables") 405 expect(out).to include("rack has: rackup") 406 end 407 408 it "works if child gems don't have bins" do 409 install_gemfile <<-G 410 source "file://#{gem_repo1}" 411 gem "actionpack" 412 G 413 414 bundle "binstubs actionpack" 415 expect(out).to include("no executables for the gem actionpack") 416 end 417 418 it "works if the gem has development dependencies" do 419 install_gemfile <<-G 420 source "file://#{gem_repo1}" 421 gem "with_development_dependency" 422 G 423 424 bundle "binstubs with_development_dependency" 425 expect(out).to include("no executables for the gem with_development_dependency") 426 end 427 end 428 429 context "when BUNDLE_INSTALL is specified" do 430 it "performs an automatic bundle install" do 431 gemfile <<-G 432 source "file://#{gem_repo1}" 433 gem "rack" 434 G 435 436 bundle "config auto_install 1" 437 bundle "binstubs rack" 438 expect(out).to include("Installing rack 1.0.0") 439 expect(the_bundle).to include_gems "rack 1.0.0" 440 end 441 442 it "does nothing when already up to date" do 443 install_gemfile <<-G 444 source "file://#{gem_repo1}" 445 gem "rack" 446 G 447 448 bundle "config auto_install 1" 449 bundle "binstubs rack", :env => { "BUNDLE_INSTALL" => 1 } 450 expect(out).not_to include("Installing rack 1.0.0") 451 end 452 end 453end 454