Skip to content

Commit a0edaaa

Browse files
committed
Fix bundler 2.7.2 binstub generation - copy instead of generate
Bundler 2.7+ refuses to create its own binstub, displaying: 'Bundler itself does not use binstubs because its version is selected by RubyGems' Solution: Copy bundler's existing bundle executable from bundler/bin/bundle to binstubs/bundle instead of trying to regenerate it with 'bundle binstubs bundler'. Updated tests to mock bundler/bin/bundle file creation instead of mocking the 'bundle binstubs' command execution.
1 parent a97d633 commit a0edaaa

2 files changed

Lines changed: 31 additions & 30 deletions

File tree

src/ruby/supply/supply.go

Lines changed: 17 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -854,16 +854,24 @@ func (s *Supplier) regenerateBundlerBinStub(appDir string) error {
854854
return fmt.Errorf("could not create binstubs directory: %v", err)
855855
}
856856

857-
// Note: --path flag is deprecated in bundler 2.7+
858-
// The bin path is already configured via 'bundle config set bin'
859-
cmd := exec.Command("bundle", "binstubs", "bundler", "--force")
860-
cmd.Dir = appDir
861-
cmd.Stdout = text.NewIndentWriter(os.Stdout, []byte(" "))
862-
cmd.Stderr = text.NewIndentWriter(os.Stderr, []byte(" "))
863-
if err := s.Command.Run(cmd); err != nil {
864-
return err
857+
// Note: Bundler 2.7+ refuses to create its own binstub with message:
858+
// "Bundler itself does not use binstubs because its version is selected by RubyGems"
859+
// Instead, we copy the bundle executable from bundler/bin to binstubs directory
860+
bundlerBin := filepath.Join(s.Stager.DepDir(), "bundler", "bin", "bundle")
861+
binstubBundle := filepath.Join(s.Stager.DepDir(), "binstubs", "bundle")
862+
863+
if exists, err := libbuildpack.FileExists(bundlerBin); err != nil {
864+
return fmt.Errorf("could not check if bundler bin exists: %v", err)
865+
} else if exists {
866+
// Copy bundler's bundle executable to binstubs
867+
if err := libbuildpack.CopyFile(bundlerBin, binstubBundle); err != nil {
868+
return fmt.Errorf("could not copy bundle binstub: %v", err)
869+
}
870+
} else {
871+
return fmt.Errorf("bundler bin not found at %s", bundlerBin)
865872
}
866-
return libbuildpack.CopyFile(filepath.Join(s.Stager.DepDir(), "binstubs", "bundle"), filepath.Join(s.Stager.DepDir(), "bin", "bundle"))
873+
874+
return libbuildpack.CopyFile(binstubBundle, filepath.Join(s.Stager.DepDir(), "bin", "bundle"))
867875
}
868876

869877
func (s *Supplier) EnableLDLibraryPathEnv() error {

src/ruby/supply/supply_test.go

Lines changed: 14 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,6 @@ import (
88
"os/exec"
99
"path/filepath"
1010

11-
"reflect"
12-
1311
"github.com/cloudfoundry/ruby-buildpack/src/ruby/cache"
1412
"github.com/cloudfoundry/ruby-buildpack/src/ruby/supply"
1513

@@ -481,17 +479,16 @@ var _ = Describe("Supply", func() {
481479

482480
const windowsWarning = "**WARNING** Windows line endings detected in Gemfile. Your app may fail to stage. Please use UNIX line endings."
483481

484-
handleBundleBinstubRegeneration := func(cmd *exec.Cmd) error {
485-
if len(cmd.Args) >= 4 && reflect.DeepEqual(cmd.Args[0:4], []string{"bundle", "binstubs", "bundler", "--force"}) {
486-
binstubsPath := filepath.Join(depsDir, depsIdx, "binstubs")
487-
Expect(os.MkdirAll(binstubsPath, 0755)).To(Succeed())
488-
Expect(os.WriteFile(filepath.Join(binstubsPath, "bundle"), []byte("new bundle binstub"), 0644)).To(Succeed())
489-
}
490-
return nil
482+
// Setup bundler bin directory with bundle executable (simulates bundler installation)
483+
setupBundlerBin := func() {
484+
bundlerBinDir := filepath.Join(depsDir, depsIdx, "bundler", "bin")
485+
Expect(os.MkdirAll(bundlerBinDir, 0755)).To(Succeed())
486+
Expect(os.WriteFile(filepath.Join(bundlerBinDir, "bundle"), []byte("new bundle binstub"), 0755)).To(Succeed())
491487
}
492488

493489
itRegeneratesBundleBinstub := func() {
494490
It("Re-generates the bundler binstub to replace older, rails-generated ones that are incompatible with bundler > 1.16.0", func() {
491+
setupBundlerBin()
495492
Expect(supplier.InstallGems()).To(Succeed())
496493
Expect(os.ReadFile(filepath.Join(depsDir, depsIdx, "binstubs", "bundle"))).To(Equal([]byte("new bundle binstub")))
497494
Expect(os.ReadFile(filepath.Join(depsDir, depsIdx, "bin", "bundle"))).To(Equal([]byte("new bundle binstub")))
@@ -501,13 +498,14 @@ var _ = Describe("Supply", func() {
501498
Context("Windows Gemfile", func() {
502499
BeforeEach(func() {
503500
mockVersions.EXPECT().HasWindowsGemfileLock().Return(false, nil)
504-
mockCommand.EXPECT().Run(gomock.Any()).AnyTimes().Do(handleBundleBinstubRegeneration)
501+
mockCommand.EXPECT().Run(gomock.Any()).AnyTimes()
505502
Expect(os.WriteFile(filepath.Join(buildDir, "Gemfile"), []byte("source \"https://rubygems.org\"\r\ngem \"rack\"\r\n"), 0644)).To(Succeed())
506503
})
507504

508505
itRegeneratesBundleBinstub()
509506

510507
It("Warns the user", func() {
508+
setupBundlerBin()
511509
Expect(supplier.InstallGems()).To(Succeed())
512510
Expect(buffer.String()).To(ContainSubstring(windowsWarning))
513511
})
@@ -521,10 +519,7 @@ var _ = Describe("Supply", func() {
521519
if len(cmd.Args) > 2 && cmd.Args[1] == "install" {
522520
Expect(os.MkdirAll(filepath.Join(cmd.Dir, ".bundle"), 0755)).To(Succeed())
523521
Expect(os.WriteFile(filepath.Join(cmd.Dir, ".bundle", "config"), []byte("new bundle config"), 0644)).To(Succeed())
524-
} else {
525-
return handleBundleBinstubRegeneration(cmd)
526522
}
527-
528523
return nil
529524
})
530525
Expect(os.WriteFile(filepath.Join(buildDir, "Gemfile"), []byte("source \"https://rubygems.org\"\ngem \"rack\"\n"), 0644)).To(Succeed())
@@ -537,11 +532,13 @@ var _ = Describe("Supply", func() {
537532
itRegeneratesBundleBinstub()
538533

539534
It("Does not warn the user", func() {
535+
setupBundlerBin()
540536
Expect(supplier.InstallGems()).To(Succeed())
541537
Expect(buffer.String()).ToNot(ContainSubstring(windowsWarning))
542538
})
543539

544540
It("does not change .bundle/config", func() {
541+
setupBundlerBin()
545542
Expect(os.MkdirAll(filepath.Join(buildDir, ".bundle"), 0755)).To(Succeed())
546543
Expect(os.WriteFile(filepath.Join(buildDir, ".bundle", "config"), []byte("orig content"), 0644)).To(Succeed())
547544
Expect(os.ReadFile(filepath.Join(buildDir, ".bundle", "config"))).To(Equal([]byte("orig content")))
@@ -572,10 +569,9 @@ var _ = Describe("Supply", func() {
572569
if cmd.Args[1] == "install" {
573570
Expect(filepath.Join(cmd.Dir, "Gemfile")).To(BeAnExistingFile())
574571
Expect(filepath.Join(cmd.Dir, "Gemfile.lock")).To(BeAnExistingFile())
575-
} else {
576-
handleBundleBinstubRegeneration(cmd)
577572
}
578573
})
574+
setupBundlerBin()
579575
Expect(supplier.InstallGems()).To(Succeed())
580576

581577
Expect(os.ReadFile(filepath.Join(buildDir, "Gemfile.lock"))).To(ContainSubstring(gemfileLock))
@@ -588,10 +584,9 @@ var _ = Describe("Supply", func() {
588584
if cmd.Args[1] == "install" {
589585
Expect(cmd.Dir).ToNot(Equal(buildDir))
590586
installCalled = true
591-
} else {
592-
handleBundleBinstubRegeneration(cmd)
593587
}
594588
})
589+
setupBundlerBin()
595590
Expect(supplier.InstallGems()).To(Succeed())
596591
Expect(installCalled).To(BeTrue())
597592
})
@@ -614,10 +609,9 @@ var _ = Describe("Supply", func() {
614609
Expect(filepath.Join(cmd.Dir, "Gemfile")).To(BeAnExistingFile())
615610
Expect(filepath.Join(cmd.Dir, "Gemfile.lock")).ToNot(BeAnExistingFile())
616611
Expect(os.WriteFile(filepath.Join(cmd.Dir, "Gemfile.lock"), []byte(newGemfileLock), 0644)).To(Succeed())
617-
} else {
618-
handleBundleBinstubRegeneration(cmd)
619612
}
620613
})
614+
setupBundlerBin()
621615
Expect(supplier.InstallGems()).To(Succeed())
622616

623617
Expect(os.ReadFile(filepath.Join(buildDir, "Gemfile.lock"))).To(ContainSubstring(gemfileLock))
@@ -630,10 +624,9 @@ var _ = Describe("Supply", func() {
630624
if cmd.Args[1] == "install" {
631625
Expect(cmd.Dir).ToNot(Equal(buildDir))
632626
installCalled = true
633-
} else {
634-
handleBundleBinstubRegeneration(cmd)
635627
}
636628
})
629+
setupBundlerBin()
637630
Expect(supplier.InstallGems()).To(Succeed())
638631
Expect(installCalled).To(BeTrue())
639632
})

0 commit comments

Comments
 (0)