Skip to content

Commit 6e2eacf

Browse files
authored
Merge pull request #819 from Shopify/Alex/use-bundled-gem-version
Use bundle-locked gem versions in child contexts
2 parents 107bee8 + e6f5729 commit 6e2eacf

10 files changed

Lines changed: 92 additions & 20 deletions

File tree

Gemfile.lock

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
PATH
22
remote: .
33
specs:
4-
spoom (1.7.9)
4+
spoom (1.7.10)
55
erubi (>= 1.10.0)
66
prism (>= 0.28.0)
77
rbi (>= 0.3.3)

lib/spoom/bundler_helper.rb

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
# typed: strict
2+
# frozen_string_literal: true
3+
4+
module Spoom
5+
module BundlerHelper
6+
extend T::Sig
7+
8+
class << self
9+
# Generate a gem requirement for the given gem name, using that gem's version in the "real" current bundle.
10+
#
11+
# This ensures that any child Spoom::Contexts use predictable gem versions,
12+
# without having to manually specify them and bump them to stay in sync with Spoom's real Gemfile.
13+
#
14+
# Given `"foo"`, returns a string like 'gem "foo", "= 1.2.3"', suitable for inserting into a Gemfile.
15+
#: (String) -> String
16+
def gem_requirement_from_real_bundle(gem_name)
17+
specs = Bundler.load.gems[gem_name]
18+
19+
if specs.nil? || specs.empty?
20+
raise "Did not find gem #{gem_name.inspect} in the current bundle"
21+
elsif specs.count > 1
22+
raise <<~MSG
23+
Found multiple versions of #{gem_name.inspect} in the current bundle:
24+
#{specs.sort_by(&:version).map { |spec| " - #{spec.name} #{spec.version}" }.join("\n")}
25+
MSG
26+
else
27+
spec = specs.first
28+
%(gem "#{spec.name}", "= #{spec.version}")
29+
end
30+
end
31+
end
32+
end
33+
end

lib/spoom/cli/srb/sigs.rb

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
# typed: true
22
# frozen_string_literal: true
33

4+
require "spoom/bundler_helper"
5+
46
module Spoom
57
module Cli
68
module Srb
@@ -142,13 +144,14 @@ class Module; include T::Sig; end
142144
# Now we create a new context to import our modified gem and run tapioca
143145
say("Running Tapioca...")
144146
tapioca_context = Spoom::Context.mktmp!
145-
tapioca_context.write!("Gemfile", <<~RB)
147+
tapioca_context.write_gemfile!(<<~GEMFILE)
146148
source "https://rubygems.org"
147149
148-
gem "rbs", ">= 4.0.0.dev.4"
149-
gem "tapioca"
150+
#{Spoom::BundlerHelper.gem_requirement_from_real_bundle("rbs")}
151+
#{Spoom::BundlerHelper.gem_requirement_from_real_bundle("tapioca")}
152+
150153
gem "#{spec.name}", path: "#{copy_context.absolute_path}"
151-
RB
154+
GEMFILE
152155
exec(tapioca_context, "bundle install")
153156
exec(tapioca_context, "bundle exec tapioca gem #{spec.name} --no-doc --no-loc --no-file-header")
154157

lib/spoom/context/bundle.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ def gemfile_lock_specs
4848
return {} unless file?("Gemfile.lock")
4949

5050
parser = Bundler::LockfileParser.new(read_gemfile_lock)
51-
parser.specs.map { |spec| [spec.name, spec] }.to_h
51+
parser.specs.to_h { |spec| [spec.name, spec] }
5252
end
5353

5454
# Get `gem` version from the `Gemfile.lock` content

lib/spoom/version.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,5 +2,5 @@
22
# frozen_string_literal: true
33

44
module Spoom
5-
VERSION = "1.7.9"
5+
VERSION = "1.7.10"
66
end

rbi/spoom.rbi

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,13 @@ module Spoom
1111
end
1212
end
1313

14+
module Spoom::BundlerHelper
15+
class << self
16+
sig { params(gem_name: ::String).returns(::String) }
17+
def gem_requirement_from_real_bundle(gem_name); end
18+
end
19+
end
20+
1421
module Spoom::Cli; end
1522

1623
class Spoom::Cli::Deadcode < ::Thor

test/spoom/bundler_helper_test.rb

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
# typed: true
2+
# frozen_string_literal: true
3+
4+
require "test_helper"
5+
6+
module Spoom
7+
class BundlerHelperTest < Minitest::Test
8+
def test_gem_requirement_from_real_bundle_returns_gem_requirement_string
9+
gem_from_spoom_bundle = "tapioca"
10+
gem_requirement = BundlerHelper.gem_requirement_from_real_bundle(gem_from_spoom_bundle)
11+
12+
assert_match(/^gem "#{gem_from_spoom_bundle}", "= .+"$/, gem_requirement)
13+
end
14+
15+
def test_raises_if_gem_not_found_in_bundle
16+
assert_raises(RuntimeError) do
17+
BundlerHelper.gem_requirement_from_real_bundle("not-real")
18+
end
19+
end
20+
end
21+
end

test/spoom/context/sorbet_test.rb

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ def test_context_run_srb_from_bundle
3434
context.write_gemfile!(<<~GEMFILE)
3535
source "https://rubygems.org"
3636
37-
gem "sorbet"
37+
#{Spoom::BundlerHelper.gem_requirement_from_real_bundle("sorbet")}
3838
GEMFILE
3939
context.bundle("config set --local path $GEM_HOME")
4040
context.bundle_install!
@@ -67,7 +67,7 @@ def test_context_run_srb_from_path
6767
context.write_gemfile!(<<~GEMFILE)
6868
source "https://rubygems.org"
6969
70-
gem "sorbet"
70+
#{Spoom::BundlerHelper.gem_requirement_from_real_bundle("sorbet")}
7171
GEMFILE
7272
context.bundle_install!
7373

@@ -163,7 +163,7 @@ def test_context_srb_version_return_version_string
163163
context.write_gemfile!(<<~GEMFILE)
164164
source "https://rubygems.org"
165165
166-
gem "sorbet"
166+
#{Spoom::BundlerHelper.gem_requirement_from_real_bundle("sorbet")}
167167
GEMFILE
168168
context.bundle_install!
169169

test/spoom/deadcode/plugins_test.rb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,8 +51,8 @@ def test_deadcode_plugins_with_sorbet
5151
plugins = plugins_classes_for_gemfile(<<~GEMFILE)
5252
source "https://rubygems.org"
5353
54-
gem "sorbet"
55-
gem "sorbet-runtime"
54+
#{Spoom::BundlerHelper.gem_requirement_from_real_bundle("sorbet")}
55+
#{Spoom::BundlerHelper.gem_requirement_from_real_bundle("sorbet-runtime")}
5656
GEMFILE
5757

5858
assert_equal(

test/test_helper.rb

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -22,21 +22,29 @@ def new_project(name = nil)
2222
# Default Gemfile contents requiring only Spoom
2323
#: -> String
2424
def spoom_gemfile
25-
<<~GEMFILE
26-
source("https://rubygems.org")
27-
28-
gemspec name: "spoom", path: "#{SPOOM_PATH}"
29-
gem "tapioca"
30-
gem "sorbet-static-and-runtime", "#{Sorbet::GEM_VERSION}"
31-
gem "json", "2.7.2"
32-
GEMFILE
25+
Spoom::TestHelper.default_spoom_test_gemfile
3326
end
3427

3528
# Replace all sorbet-like version "0.5.5888" in `test` by "X.X.XXXX"
3629
#: (String text) -> String
3730
def censor_sorbet_version(text)
3831
text.gsub(/\d\.\d\.\d{4,5}/, "X.X.XXXX")
3932
end
33+
34+
class << self
35+
#: -> String
36+
def default_spoom_test_gemfile
37+
@default_spoom_test_gemfile ||= <<~GEMFILE #: String?
38+
source("https://rubygems.org")
39+
40+
gemspec name: "spoom", path: "#{SPOOM_PATH}"
41+
42+
#{Spoom::BundlerHelper.gem_requirement_from_real_bundle("tapioca")}
43+
#{Spoom::BundlerHelper.gem_requirement_from_real_bundle("sorbet-static-and-runtime")}
44+
#{Spoom::BundlerHelper.gem_requirement_from_real_bundle("json")}
45+
GEMFILE
46+
end
47+
end
4048
end
4149
end
4250

0 commit comments

Comments
 (0)