Homebrew / homebrew-bundle

📦 Bundler for non-Ruby dependencies from Homebrew, Homebrew Cask and the Mac App Store.
MIT License
5.36k stars 288 forks source link

Add a way to load other brewfiles #521

Closed orf closed 5 years ago

orf commented 5 years ago

It's quite common to use a bundlefile with a dotfile repo of some kind, so that bootstrapping a new machine is simple and quick.

One thing that is lacking right now is the ability to include other brewfiles dynamically. For example, you have two machines: work and home:

# ~/.Brewfile
cask "some common cask"
# ~/.Brewfile.home
cask "some-game"
# ~/.Brewfile.work
cask "some-boring-work-stuff"

If you want to maintain a global Brewfile, and run a single command to bootstrap the machine, this is currently impossible as you cannot do something like:

# ~/.Brewfile
...
load "~/.Brewfile.#{Socket.gethostname}"

Which is quite annoying! This has come up a few times over the years, notably in this issue: https://github.com/Homebrew/homebrew-bundle/issues/158

In that issue it was said that this would be hard to implement. But I'm not convinced. We run instance_eval over the input here: https://github.com/Homebrew/homebrew-bundle/blob/master/lib/bundle/dsl.rb#L32

Couldn't we add a simple method: load_brewfile, which looks like this:

    def load_brewfile(path)
      instance_eval(File.read(path))
    end

This would obviously be an advanced option, but it's quite limiting not having this available, and if it's really not hard to implement then is there any argument against having it supported?

MikeMcQuaid commented 5 years ago

It's just evaluating Ruby so you definitely can use load or require or File.read or similar in your Brewfile if you wish to load another.

this is currently impossible as you cannot do something like:

# ~/.Brewfile
...
load "~/.Brewfile.#{Socket.gethostname}"

Why can't you?

Closing for now, may reopen if/when there's anything actionable.

orf commented 5 years ago

Sorry, I was not clear on that front. Doing that results in:

Error: Invalid Brewfile: undefined method `cask' for main:Object

With:

# ~/.Brewfile
load "~/.Brewfile.test"
# ~/.Brewfile.test
cask "test"

require gives similar errors.

orf commented 5 years ago

Adding:

# ~/.Brewfile
instance_eval(File.read(".Brewfile.test"))

Does work. But this is a bit hacky I think, no?

MikeMcQuaid commented 5 years ago

But this is a bit hacky I think, no?

I guess I consider the "Brewfile including a Brewfile" pattern itself to be a bit hacky. Glad you found a way for it to work!

OJFord commented 5 years ago

I wanted the same, changed this slightly from a post about bundler/Gemfiles:

Dir.glob(File.join(File.dirname(__FILE__), '*', '**', 'Brewfile')) do |brewfile|
    eval(IO.read(brewfile), binding)
end

First * is to ensure we don't pick up __FILE__, execute the glob again, and eventually blow our stack. I'm sure that's not the cleanest ruby, but it works.

As for it being hacky, my use-case is for config files, so I have:

$XDG_CONFIG_HOME/
+--thing1/
|  +--[...]
|   \ Brewfile
+--thing2/
|  +--[...]
|  \ Brewfile
+--[...]
\ Brewfile

pseudo-dependencies (not actual brew dependencies) and brew 'thing1' itself go in thing1/Brewfile, anything I use directly that doesn't have a config dir goes in the top level Brewfile, which ends with the snippet above to recursively find the others.