Skip to content
This repository was archived by the owner on Mar 20, 2025. It is now read-only.

Commit 565d89d

Browse files
committed
commands/exec: allow customising environment.
Provide `HOMEBREW_BUNDLE_EXEC_ALL_KEG_ONLY_DEPS` and `HOMEBREW_BUNDLE_EXEC_FORMULA_VERSION_*` variables to allow adjusting the environment variables that are set in `brew bundle exec`.
1 parent f5acb38 commit 565d89d

File tree

2 files changed

+77
-50
lines changed

2 files changed

+77
-50
lines changed

lib/bundle/commands/exec.rb

+30-1
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,15 @@ def run(*args, global: false, file: nil)
3535

3636
Formulary.factory(entry.name)
3737
end
38-
ENV.keg_only_deps = ENV.deps.select(&:keg_only?)
38+
39+
# Allow setting all dependencies to be keg-only
40+
# (i.e. should be explicitly in HOMEBREW_*PATHs ahead of HOMEBREW_PREFIX)
41+
ENV.keg_only_deps = if ENV["HOMEBREW_BUNDLE_EXEC_ALL_KEG_ONLY_DEPS"].present?
42+
ENV.delete("HOMEBREW_BUNDLE_EXEC_ALL_KEG_ONLY_DEPS")
43+
ENV.deps
44+
else
45+
ENV.deps.select(&:keg_only?)
46+
end
3947
ENV.setup_build_environment
4048

4149
# Enable compiler flag filtering
@@ -58,6 +66,27 @@ def run(*args, global: false, file: nil)
5866
# Ensure the Ruby path we saved goes before anything else, if the command was in the PATH
5967
ENV.prepend_path "PATH", command_path if command_path.present?
6068

69+
# Replace the formula versions from the environment variables
70+
formula_versions = {}
71+
ENV.each do |key, value|
72+
match = key.match(/^HOMEBREW_BUNDLE_EXEC_FORMULA_VERSION_(.+)$/)
73+
next if match.blank?
74+
75+
formula_name = match[1]
76+
next if formula_name.blank?
77+
78+
ENV.delete(key)
79+
formula_versions[formula_name.downcase] = value
80+
end
81+
formula_versions.each do |formula_name, formula_version|
82+
ENV.each do |key, value|
83+
opt = %r{/opt/#{formula_name}([/:$])}
84+
next unless value.match(opt)
85+
86+
ENV[key] = value.gsub(opt, "/Cellar/#{formula_name}/#{formula_version}\\1")
87+
end
88+
end
89+
6190
exec(*args)
6291
end
6392
end

spec/bundle/commands/exec_command_spec.rb

+47-49
Original file line numberDiff line numberDiff line change
@@ -10,35 +10,47 @@
1010
end
1111

1212
context "when a Brewfile is found" do
13-
it "does not raise an error" do
14-
allow(described_class).to receive(:exec).and_return(nil)
15-
allow_any_instance_of(Pathname).to receive(:read)
16-
.and_return("brew 'openssl'")
13+
let(:brewfile_contents) { "brew 'openssl'" }
1714

18-
expect { described_class.run("bundle", "install") }.not_to raise_error
15+
before do
16+
allow_any_instance_of(Pathname).to receive(:read)
17+
.and_return(brewfile_contents)
1918
end
2019

21-
it "is able to run without bundle arguments" do
22-
allow(described_class).to receive(:exec).with("bundle", "install").and_return(nil)
23-
allow_any_instance_of(Pathname).to receive(:read)
24-
.and_return("brew 'openssl'")
20+
context "with valid command setup" do
21+
before do
22+
allow(described_class).to receive(:exec).and_return(nil)
23+
end
2524

26-
expect { described_class.run("bundle", "install") }.not_to raise_error
27-
end
25+
it "does not raise an error" do
26+
expect { described_class.run("bundle", "install") }.not_to raise_error
27+
end
2828

29-
it "raises an exception if called without a command" do
30-
allow(described_class).to receive(:exec).and_return(nil)
31-
allow_any_instance_of(Pathname).to receive(:read)
32-
.and_return("brew 'openssl'")
29+
it "does not raise an error when HOMEBREW_BUNDLE_EXEC_ALL_KEG_ONLY_DEPS is set" do
30+
ENV["HOMEBREW_BUNDLE_EXEC_ALL_KEG_ONLY_DEPS"] = "1"
31+
expect { described_class.run("bundle", "install") }.not_to raise_error
32+
end
3333

34-
expect { described_class.run }.to raise_error(RuntimeError)
34+
it "uses the formula version from the environment variable" do
35+
openssl_version = "1.1.1"
36+
ENV["PATH"] = "/opt/homebrew/opt/openssl/bin"
37+
ENV["HOMEBREW_BUNDLE_EXEC_FORMULA_VERSION_OPENSSL"] = openssl_version
38+
described_class.run("bundle", "install")
39+
expect(ENV["PATH"]).to include("/Cellar/openssl/1.1.1/bin")
40+
end
41+
42+
it "is able to run without bundle arguments" do
43+
allow(described_class).to receive(:exec).with("bundle", "install").and_return(nil)
44+
expect { described_class.run("bundle", "install") }.not_to raise_error
45+
end
46+
47+
it "raises an exception if called without a command" do
48+
expect { described_class.run }.to raise_error(RuntimeError)
49+
end
3550
end
3651

3752
it "raises if called with a command that's not on the PATH" do
3853
allow(described_class).to receive_messages(exec: nil, which: nil)
39-
allow_any_instance_of(Pathname).to receive(:read)
40-
.and_return("brew 'openssl'")
41-
4254
expect { described_class.run("bundle", "install") }.to raise_error(RuntimeError)
4355
end
4456

@@ -47,49 +59,35 @@
4759
expect(described_class).to receive(:which).and_return(Pathname("/usr/local/bin/bundle"))
4860
allow(ENV).to receive(:prepend_path).with(any_args).and_call_original
4961
expect(ENV).to receive(:prepend_path).with("PATH", "/usr/local/bin").once.and_call_original
50-
allow_any_instance_of(Pathname).to receive(:read)
51-
.and_return("brew 'openssl'")
5262
described_class.run("bundle", "install")
5363
end
5464

5565
describe "when running a command which exists but is not on the PATH" do
56-
it "does not raise if the command is a relative path with current directory indicator" do
57-
allow(described_class).to receive(:exec).with("./configure").and_return(nil)
58-
expect(described_class).not_to receive(:which)
59-
allow_any_instance_of(Pathname).to receive(:read)
60-
.and_return("brew 'zlib'")
61-
62-
expect { described_class.run("./configure") }.not_to raise_error
66+
let(:brewfile_contents) { "brew 'zlib'" }
67+
68+
shared_examples "allows command execution" do |command|
69+
it "does not raise" do
70+
allow(described_class).to receive(:exec).with(command).and_return(nil)
71+
expect(described_class).not_to receive(:which)
72+
expect { described_class.run(command) }.not_to raise_error
73+
end
6374
end
6475

65-
it "does not raise if the command is a relative path without current directory indicator" do
66-
allow(described_class).to receive(:exec).with("bin/install").and_return(nil)
67-
expect(described_class).not_to receive(:which)
68-
allow_any_instance_of(Pathname).to receive(:read)
69-
.and_return("brew 'zlib'")
70-
71-
expect { described_class.run("bin/install") }.not_to raise_error
72-
end
73-
74-
it "does not raise if the command is an absolute path" do
75-
allow(described_class).to receive(:exec).with("/Users/admin/Downloads/command").and_return(nil)
76-
expect(described_class).not_to receive(:which)
77-
allow_any_instance_of(Pathname).to receive(:read)
78-
.and_return("brew 'zlib'")
79-
80-
expect { described_class.run("/Users/admin/Downloads/command") }.not_to raise_error
81-
end
76+
it_behaves_like "allows command execution", "./configure"
77+
it_behaves_like "allows command execution", "bin/install"
78+
it_behaves_like "allows command execution", "/Users/admin/Downloads/command"
8279
end
8380

8481
describe "when the Brewfile contains rbenv" do
85-
before { ENV["HOMEBREW_RBENV_ROOT"] = rbenv_root.to_s }
86-
8782
let(:rbenv_root) { Pathname.new("/tmp/.rbenv") }
83+
let(:brewfile_contents) { "brew 'rbenv'" }
84+
85+
before do
86+
ENV["HOMEBREW_RBENV_ROOT"] = rbenv_root.to_s
87+
end
8888

8989
it "prepends the path of the rbenv shims to PATH before running" do
9090
allow(described_class).to receive(:exec).with("/usr/bin/true").and_return(0)
91-
allow_any_instance_of(Pathname).to receive(:read)
92-
.and_return("brew 'rbenv'")
9391
allow(ENV).to receive(:fetch).with(any_args).and_call_original
9492
allow(ENV).to receive(:prepend_path).with(any_args).once.and_call_original
9593

0 commit comments

Comments
 (0)