Skip to content

Commit 774054c

Browse files
committed
✨ Support eval_gemfile
- Fixes #154
1 parent 975ded0 commit 774054c

File tree

4 files changed

+181
-0
lines changed

4 files changed

+181
-0
lines changed

README.md

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,90 @@ When you prefix a command with `appraisal`, the command is run with the
110110
appropriate Gemfile for that appraisal, ensuring the correct dependencies
111111
are used.
112112

113+
Sharing Modular Gemfiles between Appraisals
114+
-------
115+
116+
_New for version 3.0_
117+
118+
It is common for Appraisals to duplicate sets of gems, and sometimes it
119+
makes sense to DRY this up into a shared, modular, gemfile.
120+
In a scenario where you do not load your main Gemfile in your Appraisals,
121+
but you want to declare your various gem sets for e.g.
122+
`%w(coverage test documentation audit)` once each, you can re-use the same
123+
modular gemfiles for local development by referencing them from the main
124+
Gemfile.
125+
126+
To do this, use the `eval_gemfile` declaration within the necessary
127+
`appraise` block in your `Appraisals` file, which will behave the same as
128+
`eval_gemfile` does in a normal Gemfile.
129+
130+
### Example Usage
131+
132+
You could put your modular gemfiles in the `gemfiles` directory, or nest
133+
them in `gemfiles/modular/*`, which will be used for this example.
134+
135+
**Gemfile**
136+
```ruby
137+
eval_gemfile "gemfiles/modular/audit.gemfile"
138+
```
139+
140+
**gemfiles/modular/audit.gemfile**
141+
```ruby
142+
# Many gems are dropping support for Ruby < 3.1,
143+
# so we only want to run our security audit in CI on Ruby 3.1+
144+
gem "bundler-audit", "~> 0.9.2"
145+
# And other security audit gems...
146+
```
147+
148+
**Appraisals**
149+
```ruby
150+
appraise "ruby-2-7" do
151+
gem "dummy"
152+
end
153+
154+
appraise "ruby-3-0" do
155+
gem "dummy"
156+
end
157+
158+
appraise "ruby-3-1" do
159+
gem "dummy"
160+
eval_gemfile "modular/audit.gemfile"
161+
end
162+
163+
appraise "ruby-3-2" do
164+
gem "dummy"
165+
eval_gemfile "modular/audit.gemfile"
166+
end
167+
168+
appraise "ruby-3-3" do
169+
gem "dummy"
170+
eval_gemfile "modular/audit.gemfile"
171+
end
172+
173+
appraise "ruby-3-4" do
174+
gem "dummy"
175+
eval_gemfile "modular/audit.gemfile"
176+
end
177+
```
178+
179+
**Appraisal.root.gemfile**
180+
```ruby
181+
source "https://rubygems.org"
182+
183+
# Appraisal Root Gemfile is for running appraisal to generate the Appraisal Gemfiles
184+
# We do not load the standard Gemfile, as it is tailored for local development,
185+
# while appraisals are tailored for CI.
186+
187+
gemspec
188+
189+
gem "appraisal"
190+
```
191+
192+
Now when you need to update your appraisals:
193+
```shell
194+
BUNDLE_GEMFILE=Appraisal.root.gemfile bundle exec appraisal update
195+
```
196+
113197
Removing Gems using Appraisal
114198
-------
115199

lib/appraisal/appraisal.rb

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,10 @@ def initialize(name, source_gemfile)
1919
@gemfile = source_gemfile.dup
2020
end
2121

22+
def eval_gemfile(*args)
23+
gemfile.eval_gemfile(*args)
24+
end
25+
2226
def gem(*args)
2327
gemfile.gem(*args)
2428
end

lib/appraisal/bundler_dsl.rb

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ class BundlerDSL
1818
source_blocks
1919
install_if
2020
gemspec
21+
eval_gemfile
2122
]
2223

2324
def initialize
@@ -32,12 +33,17 @@ def initialize
3233
@source_blocks = OrderedHash.new
3334
@git_sources = {}
3435
@install_if = {}
36+
@eval_gemfile = []
3537
end
3638

3739
def run(&block)
3840
instance_exec(&block)
3941
end
4042

43+
def eval_gemfile(path, contents = nil)
44+
@eval_gemfile << [path, contents]
45+
end
46+
4147
def gem(name, *requirements)
4248
@dependencies.add(name, substitute_git_source(requirements))
4349
end
@@ -114,6 +120,12 @@ def git_source(source, &block)
114120

115121
private
116122

123+
def eval_gemfile_entry
124+
@eval_gemfile.map { |(p, c)| "eval_gemfile(#{p.inspect}#{", #{c.inspect}" if c})" } * "\n\n"
125+
end
126+
127+
alias_method :eval_gemfile_entry_for_dup, :eval_gemfile_entry
128+
117129
def source_entry
118130
@sources.uniq.map { |source| "source #{source.inspect}" }.join("\n")
119131
end

spec/acceptance/eval_gemfile_spec.rb

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
# frozen_string_literal: true
2+
3+
require "spec_helper"
4+
5+
RSpec.describe "eval_gemfile" do
6+
before do
7+
build_appraisal_file
8+
build_modular_gemfile
9+
build_rakefile
10+
build_gemspec
11+
end
12+
13+
it "supports eval_gemfile syntax" do
14+
write_file "Gemfile", <<-GEMFILE
15+
source "https://rubygems.org"
16+
17+
gem 'appraisal', :path => #{PROJECT_ROOT.inspect}
18+
19+
gemspec
20+
GEMFILE
21+
22+
run "bundle install --local"
23+
run "appraisal install"
24+
output = run "appraisal rake version"
25+
26+
expect(output).to include "Loaded 1.1.0"
27+
end
28+
29+
def build_modular_gemfile
30+
begin
31+
Dir.mkdir("tmp/stage/gemfiles")
32+
rescue
33+
nil
34+
end
35+
36+
write_file File.join("gemfiles", "im_with_dummy"), <<-GEMFILE
37+
# No source needed because this is a modular gemfile intended to be loaded into another gemfile,
38+
# which will define source.
39+
gem 'dummy'
40+
GEMFILE
41+
end
42+
43+
def build_appraisal_file
44+
super(<<-APPRAISALS)
45+
appraise 'stock' do
46+
gem 'rake'
47+
eval_gemfile "im_with_dummy"
48+
end
49+
APPRAISALS
50+
end
51+
52+
def build_rakefile
53+
write_file "Rakefile", <<-RAKEFILE
54+
require 'rubygems'
55+
require 'bundler/setup'
56+
require 'appraisal'
57+
58+
task :version do
59+
require 'dummy'
60+
puts "Loaded \#{$dummy_version}"
61+
end
62+
RAKEFILE
63+
end
64+
65+
def build_gemspec(path = ".")
66+
begin
67+
Dir.mkdir("tmp/stage/#{path}")
68+
rescue
69+
nil
70+
end
71+
72+
write_file File.join(path, "gemspec_project.gemspec"), <<-GEMSPEC
73+
Gem::Specification.new do |s|
74+
s.name = 'gemspec_project'
75+
s.version = '0.1'
76+
s.summary = 'Awesome Gem!'
77+
s.authors = "Appraisal"
78+
end
79+
GEMSPEC
80+
end
81+
end

0 commit comments

Comments
 (0)