📜 ⬆️ ⬇️

Distribution of applications. Part 1: Creating a Formula for Homebrew


Introduction to the series


I recently had a problem, how to distribute one console utility? My usual tools like pip , npm and gem not suitable due to the language of the utility itself - bash . Then it became clear that you need to distribute your application, including through system package managers. For Mac - due to the lack of built-in - there are several such package managers. And each of them has its own characteristics and disadvantages. And in the first part I want to elaborate on Homebrew , and how to create packages for it.

Well, to install applications on Linux, you will need to build packages of such formats: .tar.gz , .deb and .rpm . What I will discuss in the second part.

Terminology


First of all, we will agree on terms , because there are quite specific words in the world of Homebrew. In the article I will use the original terms to look not very funny. So:

TermDescriptionExample
FormulaPackage definition/usr/local/Library/Formula/foo.rb
Keg (Keg)Prefix to install Formula/usr/local/Cellar/foo/0.1
opt prefix (prefix)Symlink to the active version of Keg/ usr / local / opt / foo
CellarThis sets all Keg/ usr / local / Cellar
Tap (Crane)Additional repositories with Formula or plugins/ usr / local / Library / Taps / homebrew / homebrew-versions
BottleAssembled version of Kegqt-4.8.4.mavericks.bottle.tar.gz

How Homebrew works


It is impossible to disregard the device of the instrument itself, one of its creators told about it perfectly:
')
Homebrew is based on two things: Git and Ruby. When you install Homebrew for the first time, it creates a copy of the official repository in /usr/local . Every time you update Homebrew with the brew update command, git pull happens, and they will show you which formulas have changed. When you install something with the brew install command, Homebrew downloads the archive (later the files will be taken from the cache), checks its hash and then installs. But in fact, Formula can be much more complex: it can have dependencies, loading features: svn, ftp, and so on. And Homebrew, when possible, provides already collected packages (automatically created by the bot).

Creating your own Formula


Suppose you already have some release on github (and if not, create it with the git tag ). I will work on the example of my script. Now the easiest way to create a Formula is to call the brew create https://github.com/sobolevn/git-secret/archive/v0.1.0.tar.gz command brew create https://github.com/sobolevn/git-secret/archive/v0.1.0.tar.gz :

That's what happens
 # Documentation: https://github.com/Homebrew/homebrew/blob/master/share/doc/homebrew/Formula-Cookbook.md # http://www.rubydoc.info/github/Homebrew/homebrew/master/Formula # PLEASE REMOVE ALL GENERATED COMMENTS BEFORE SUBMITTING YOUR PULL REQUEST! class GitSecret < Formula desc "" homepage "" url "https://github.com/sobolevn/git-secret/archive/v0.1.0.tar.gz" version "0.1.0" sha256 "107c5555c203ad3b1498e2995fa8aa81942840189316d13933fcf0947180d10b" # depends_on "cmake" => :build depends_on :x11 # if your formula requires any X11/XQuartz components def install # ENV.deparallelize # if your formula fails when building in parallel # Remove unrecognized options if warned by configure system "./configure", "--disable-debug", "--disable-dependency-tracking", "--disable-silent-rules", "--prefix=#{prefix}" # system "cmake", ".", *std_cmake_args system "make", "install" # if this fails, try separate make/make install steps end test do # `test do` will create, run in and delete a temporary directory. # # This test will fail and we won't accept that! It's enough to just replace # "false" with the main program this formula installs, but it'd be nice if you # were more thorough. Run the test with `brew test git-secret`. Options passed # to `brew install` such as `--HEAD` also need to be provided to `brew test`. # # The installed folder is not in the path, so use the entire path to any # executables being tested: `system "#{bin}/program", "do", "something"`. system "false" end end 


In the future, to edit the formula, you can use the brew edit command.
Note the main class that inherits the abstract Formula * class. It has several important attributes:


Options


Sometimes it becomes necessary to add various options during installation, whether to add optional packages, what parameters to set during assembly, and so on. In Homebrew there is an option for such a case. Say, here's an example of the options from the formula git.rb * :

Sample options
  option "with-blk-sha1", "Compile with the block-optimized SHA1 implementation" option "without-completions", "Disable bash/zsh completions from 'contrib' directory" option "with-brewed-openssl", "Build with Homebrew OpenSSL instead of the system version" option "with-brewed-curl", "Use Homebrew's version of cURL library" option "with-brewed-svn", "Use Homebrew's version of SVN" option "with-persistent-https", "Build git-remote-persistent-https from 'contrib' directory" 


To access the current options from code, use the build.with? "option_name" build.with? "option_name" or build.without? "option_name" build.without? "option_name" .

Dependencies


Your application may have dependencies, for them there is a special method depends_on * . It is important to note that dependencies can be different * both according to the type of installation, and according to accessories: only the formula or the system package will fit. There are several types of dependencies (for installation):


Dependency example
  depends_on "jpeg" depends_on "gtk+" => :optional depends_on "readline" => :recommended depends_on "boost" => "with-icu" depends_on :x11 => :optional 


For those dependencies that are not part of Homebrew, there is a special resource block * , which must be declared url and sha256 .

"Pack in advance"


Homebrew has a wonderful bot * that walks by formulas and collects them in Bottles. In order to tell him what to collect, use the special bottle * block, where you can specify the url to download, the path to save the file, and the sha256 parameters for different versions of Mac OS X. A full example from the documentation:

Bottomles
 bottle do root_url "https://example.com" prefix "/opt/homebrew" cellar "/opt/homebrew/Cellar" revision 4 sha256 "4921af80137af9cc3d38fd17c9120da882448a090b0a8a3a19af3199b415bfca" => :yosemite sha256 "c71db15326ee9196cd98602e38d0b7fb2b818cdd48eede4ee8eb827d809e09ba" => :mavericks sha256 "85cc828a96735bdafcf29eb6291ca91bac846579bcef7308536e0c875d6c81d7" => :mountain_lion end 


And you can specify bottle :unneeded and the assembly will not be made. By the way, to check the correctness of the assembly, our good bot performs the tests that we specify for the formula. About them below.

Installing the latest version from the repository


Attribute head * - allows you to add a link to the repository, so that you can install the latest version directly from the current branch (which can be defined using the parameter :branch ). To install a formula this way, you need to use brew install --HEAD $formula .

Formula installation process


Here it is time to talk about the main method - install * , which executes the commands necessary for installation. Basically, it recycles a call to system commands using Kernel#system * . Here we should mention the special values ​​of variables that can be transferred to your script for its configuration, they are available by reference .

Communication with the user


To display messages * there are special features:


And there is also the caveats * method, which will write the user all the details about your package after a successful installation or when running the brew info $formula command.

Installation Testing


Things are easy: check whether the formula is established, whether it has gathered. To check the correctness of work in Homebrew there is a special test block * . It is executed inside the sendbox, and if it is executed, the formula is considered correctly assembled. As the creators themselves say, they don’t mind if the test only checks the version. The main thing is that the program has gathered. But it will be better if it passes the test more impressive. It is important to understand that it is not the functionality that is being tested, but only the assembly. Here is an example of a test:

Sample test
  test do (testpath/".zshrc").write "source `brew --prefix`/share/antigen.zsh" system "/bin/zsh", "--login", "-i", "-c", "antigen help" end 


To invoke the test locally, use the brew test $formula command.

Formula goes into the world


That's all, our formula is almost ready! Now you need to make sure that it passes internal checks from Homebrew: run the brew audit --strict --online $formula . If there are no errors, then continue. If there is, then rule. We immediately prompt that you need to change. Now the formula is ready to enter the open world. And from the open world there are several ways to install the formula:


More about each.

Fill the formula in the official repository of Homebrew


Difficult way. Our formula must satisfy many of the requirements of Homebrew to be accepted. Here are some of them:


But according to the authors, there are exceptions .

If your formula fits, now you need to upload it to the official repository. About how to make Pull-Request will tell the official guide to participate in the project, there are many subtleties and rules.

Well, if it does not fit, or you intentionally do not want to send it there, then you need to create your Tap.

Create your Tap


Tap * is essentially just a repository in which you store your own formulas. To create a Tap, we create a repository on github with the prefix homebrew- . For example: homebrew-mytap . The formulas themselves can be either in the root or in the Formula folder (or HomebrewFormula ). Now to establish your formula there are two ways, first:


Or one team:


Done!

Conclusion


I tried to skim and look at a number of the key features of Homebrew. Of course, you can’t tell just one time, such topics as patches, compiler choice, writing your own loading strategies and many subtleties when working with Python, Ruby (and other) programs that have their own features and capabilities boxes. " But those who wish can always read the relevant articles in the official documentation, which, by the way, is good.

Writing your formulas is very simple, because Homebrew provides a very high-quality API. Even if you do not know Ruby, then everything should turn out pretty quickly. That's what happened with me.

Source: https://habr.com/ru/post/279687/


All Articles