Creating Brew external command

Recently I had the need to create a homebrew external command to extend homebrew default behavior. My goal is to extend brew deps command by creating a new command brew deps-group. Executing the command will list all top-level packages in homebrew that is related to each other by common dependencies, doing so will let me know if it safe for me to actually delete the package without breaking other packages.

Structure

To achieve this, the overall file structure consist of:

  1. Formula - this tells homebrew about where to get file for your packages and their dependencies.
  2. Application - actual implementation and I wrote my simple app in Python.
homebrew-deps-group/ 
|------ Formula/
|      |--- brew-deps-group.rb
|------ brew-deps-group.rb
|------ brew-deps-group.py

I noticed that your package needs to start with homebrew-app-name.

Formula

Explanation in comments

require "formula"

# this will translate to brew-deps-group as the formula name
class BrewDepsGroup < Formula
  homepage "https://github.com/shulhi/homebrew-deps-group"
  # this is the source of your package
  # when you run brew install, it will be installed from this source
  url "https://github.com/shulhi/homebrew-deps-group.git"

  # my package depends on python
  depends_on :python if MacOS.version <= :snow_leopard

  # this is the dependency for my formula
  resource "networkx" do
    url "https://pypi.python.org/packages/source/n/networkx/networkx-1.9.1.tar.gz"
    sha1 "ac2db3b29c7c4d16435f2a7ebe90fc8bd687b59c"
  end

  # skip cleaning path in formula
  skip_clean "bin"

  def install
    # since my python application has other dependency, we need to install it
    # and ensure it is installed in /vendor
    # to avoid pollution with system packages
    ENV.prepend_create_path "PYTHONPATH", libexec/"vendor/lib/python2.7/site-packages"
    %w[networkx].each do |r|
      resource(r).stage do
          system "python", *Language::Python.setup_install_args(libexec/"vendor")
      end
    end

    # if your python application has setup.py, then uncomment this line
    # this will install your application in the correct location
    # ENV.prepend_create_path "PYTHONPATH", libexec/"lib/python2.7/site-packages"
    # system "python", *Language::Python.setup_install_args(libexec)

    # copies your python application to bin
    # since we don't have any, I don't need this
    # instead, we are just going to copy the actual file (see next section below)
    # bin.install Dir[libexec/"bin/*"]
    # bin.env_script_all_files(libexec/"bin", :PYTHONPATH => ENV["PYTHONPATH"])

    # copies all files to bin so we can execute the files from other scripts
    bin.install 'brew-deps-group.py'
    bin.install 'brew-deps-group.rb'
    (bin + 'brew-deps-group.py').chmod 0755
    (bin + 'brew-deps-group.rb').chmod 0755
  end
end

Entry point

So our application/command needs an entry point, i.e. when I run brew deps-group, some file should be executed right? In our case, this will call brew-deps-group.rb which in turn will call up brew-deps-group.py.

require 'keg'
require 'formula'

module BrewDepsGroup
  module_function

  def run
    if ['-h', '?', '--help'].include? ARGV.first
      puts USAGE
      exit 0
    end
    
    if ['-a', '--all'].include? ARGV.first
      # call our python executable file
      # we don't need python file.py since the file is already executable and is in our path
      system "brew-deps-group.py", "all"
    end
  end
end

BrewDepsGroup.run
exit 0
#! /usr/bin/env python
# our dependency we declared in our formula can now be used
import networkx as nx

def main(argv):
    option = argv[0]
    # do something
    print "hello world"

if __name__ == '__main__':
    main(sys.argv[1:])

If you need the full source code, you can find it here