This post discusses how to create a homebrew installer to package any script, application or tool, in this case a Kotlin (Java) based application. This was done for a Kotlin framework called Slate Kit, and here on Git.
Building scripts, applications and tools is a part of software engineering and a pretty common task. This process though typically involves a runnable application, installing, uninstalling, upgrading and packaging some dependencies/resources. This can be trivial or somewhat complex. Fortunately, there are package managers like homebrew that handle most of the boilerplate. While homebrew is great, and easy to use as an end-user who installs software, I found the documentation and guides on creating an installer somewhat difficult to navigate.
Before we get into the details of coding an installer, we need to first understand some terms and concepts. Most of these are represented here at Homebrew Terms. However, I want to elaborate on a few that are relevant for this post. These are also represented in the diagram below
- Package: A package is not listed in the homebrew terminology section but it is basically the script, application or tool that you want to install and use. This contains your executable and all the resources.
- Formula: A formula is a Ruby installation script that defines and installs your package on Mac OS. Since homebrew is Ruby based, the formula is a Ruby script and it typically just moves your package executable and resources into the various install directories.
Tap : A tap is a git repository containing 1 or more Formulas. You direct homebrew to load your tap(git repo) as the source for your formulas. A tap must be prefixed with
Version: The version number of your package to be installed, e.g.
This is a quick example of the entire process of setting up your tap, installing your package and then using your package. In this case the tap is
slatekit/slatekit ( homebrew automatically interprets this using the
homebrew- prefix, so this actually is
slatekit/homebrew-slatekit, the formula is also called
slatekit ( slatekit.rb ) and finally the executable script is slatekit ( a bash script that runs the Kotlin application ).
# Set tap (shortcut to github.com/slatekit/homebrew-slatekit) :> brew tap slatekit/slatekit # Install formula slatekit.rb to install the package :> brew install slatekit # Finally use the package ( slatekit script ) :> slatekit new app -name="Sample1" -packageName="mycompany.apps"
You need to first create your git repo for the actual package that you plan to install and use. Its libraries, dependencies, and resources need to be stored in one git repo. In this case its Slate Kit CLI. There is a file called slatekit and this represents a shell script that is the actual tool that will be used. You also need to create a release of this package so there is a zip/tar containing all items in the repo, for example v1.34.5. Since this is a Kotlin/Java based application, the repo contains the libraries( jars ) and some resources, and an executable. This Kotlin/Java application was originally built using gradle
'application' plugin and running
gradle distZip to package the slatekit shell script all the libraries.
You need to create another git repo containing the homebrew formula that will perform the install. The tap can contain 1 or more formulas. In this example the tap is slatekit/homebrew-slatekit. This tap provides homebrew a place to look for formulas. Some formulas are submitted to Homebrew for proper approval and available without requiring a tap ( later step ), but this example uses a custom tap so that you don't need to go through the approval process.
NOTE: The name of the git repo must start with
Now you need to create a formula that defines your package and performs the installation steps. A formula contains a reference to the url/version of the package above, so brew can download the package and unzip it so the formula can install the contents. The formula in this example is slatekit.rb. The most relevant things in the formula are the url, sha, and the steps in the install method.
class Slatekit < Formula url "https://github.com/slatekit/slatekit-cli/archive/v1.34.5.tar.gz" sha256 "e95375f92a8c0e86082d9b22e11bd6a414ee0de9df77dda84d6bb6dc21061647" # ... def install bin.install 'slatekit' prefix.install Dir["lib"] prefix.install Dir["conf"] prefix.install Dir["templates"] puts "Completed install" end end
There are 2 ways to get the sha for the package url, e.g. the
e95375f92a8c0e86082d9b22e11bd6a414ee0de9df77dda84d6bb6dc21061647 in the forumla above.
curl -L https://github.com/slatekit/slatekit- cli/archive/v1.34.5.tar.gz > 1.34.5.tar.gz shasum -a 256 1.34.5.tar.gz
Run the brew create command below and the script is created in /usr/local/Homebrew/Library/taps/homebrew/homebrew-core/Formula/slatekit-cli.rb which will have the sha.
brew create https://github.com/slatekit/slatekit- cli/archive/v1.34.5.tar.gz
There a few important things regarding whats happening in the install step. This is where you specify what to install from the package version that was downloaded.
version since homebrew provides versioning of packages, this package directory will correspond to the version in the formula e.g. v1.34.5 and installed to
bin.install: This call will move the slatekit shell script to the bin directory
/usr/local/Cellar/slatkit/1.34.5/bin, make it executable, and available on the command line to call.
prefix.install This call will install the directory from the unzipped package into the folder for this version. e.g. the
libdirectory from package is moved to
Finally, once you have all this set up, you can simply run the following commands to install the tap and package.
brew tap slatekit/slatekit brew install slatekit
The first line
brew tap slatekit/slatekit is really a call to download the tap and formula from https://github.com/slatekit/homebrew-slatekit. The second call
brew install slatekit is call to run the formula slatekit.rb
You may experience a few issues during install
Now with the package installed, you can immediately start using it. In this case, slatekit generates new projects. So we can use it like this :
slatekit new app -name="MyApp1" -packageName="company1.apps" slatekit new api -name="MyAPI1" -packageName="company1.apis" slatekit new job -name="MyJob1" -packageName="company1.jobs" slatekit new cli -name="MyCLI1" -packageName="company1.apps"
If you want to uninstall everything, you can do
brew uninstall slatekit brew untap slatekit/slatekit
Lastly, if you want to upgrade your package to a new version, then you simply have to publish a new version of your package, change the url, sha in your formula and then run the following:
brew upgrade slatekit
Well, thats finally it. I created a homebrew installer to allow for a command line tool to generate slatekit projects making it very easy for new users to get started quickly. It sounds like quite a lot at first, but there are only 3 concepts, the package( your script, app, tool), the tap (git repo for formulas), and the formula (installer). Hope this helps!