Note on Oct 4, 2018: due to a change in Homebrew's brew test-bot
behaviour, the user must set HOMEBREW_TRAVIS_CI
and HOMEBREW_TRAVIS_SUDO
appropriately (it was previously using Travis-CI-provided TRAVIS
and TRAVIS_SUDO
).
This tutorial is a follow-up to the discussion we had on davidchall/homebrew-hep#114.
It relies on a fork of the test-bot
provided by davidchall; you can get it with brew tap maelvalais/test-bot
.
First:
-
the Github project must be of the form
https://github.com/<user>/homebrew-<tap>
with the following tree (I give the example of one of my formulas, touist):. โโโ touist.rb โโโ .travis.yml
-
the Bintray project must be of the form
https://bintray.com/<user>/bottles-<tap>
-
the S3 bucket can have any name
The idea is to use staged build in Travis CI, where the two stages will be
-
the testing stage where the bottles are built and tested for the different architectures with
brew test-bot
At the end of this stage, each job must upload its bottle.tar.gz and bottle.json to somewhere (e.g., free AWS S3 bucket).
-
the deploy stage (only on master) where we fetch the bottle.json and bottle.tar.gz, upload the bottle.tar.gz to Bintray and merge the multiple
bottle.json
into a commit that contains the bottle DSL with the commandbrew test-bot --ci-upload
You can see a detailed example of such a configuration in the .travis.yml
(this gist),
where bottles are built for linux, sierra and el_capitan.
You can also see an example of tap, touist/homebrew-touist,
that uses it and its .travis.yml
(homebrew-touist repo).
The Homebrew/homebrew-core has the following workflow:
Instead of pushing pr-1234
(containing the updated DSL commit) to a fork (in core's case, the fork is BrewTestBot/homebrew-core),
I propose to use the same repo. Don't worry, the pr-1234
tags won't show in people's clones. Here is the workflow:
Drawings made using Lucidchart (not free).
When a PR is opened and the formula needs a bottle, Travis CI will build the bottle (brew test-bot
) and then
run brew test-bot --ci-upload
which will:
- upload the bottle as "unpublished".
- commit and push the DSL to BrewTestBot/Homebrew-core with the pr-1234 tag.
Then a Homebrew maintainer (me) will do
brew pull --bottle --bintray-org=touist --test-bot-user=touist https://github.com/touist/homebrew-touist/pull/5
which will do two things:
- switch the bottle in bintray from "unpublished" to "published"
- fetch the
pr-1234
tag, rebase it on top of Homebrew/homebrew-core
then the mainainer push the merged PR to Homebrew/homebrew-core.
On the homebrew-core repo, whenever a formula on which some other formulae
are relying on is updated, we also must rebuild the bottles of these dependent
formulae (this is why we have the revision number _1
after the actual version
number). This seems to be automatically taken care of on the homebrew-core
tap.
On your own tap, this revision bump whenever a dependency bottle gets rebuilt can lead to broken bottles. This problem has not been solved yet (see Homebrew/brew#3346 and Homebrew/brew#2572). But we can:
- run a cron job that tests the bottles every night to check
ย if the bottle still works, using
brew test *.rb
andbrew linkage --test *.rb
; - or we could subscribe to the changes on the formulae we depend on and run a script on a Travis CI build (I could not find a way to do that yet).
- the name of the Github repo for your tap must be
<user>/homebrew-<tap>
- you need a Github OAuth2 token that you can get in your Github settings
ย (secret variable
GITHUB_TOKEN
in.travis.yml
); - the name of the Bintray repo must be
<bintray-user>/bottles-<tap>
ย (secret variableHOMEBREW_BINTRAY_KEY
in.travis.yml
); - you also need an AWS S3 bucket, it is free and takes 1 minute to create;
then you need to create a token+password and set them as secret variables in
ย the travis-ci settings (secret variables
AWS_ACCESS_KEY_ID
andAWS_SECRET_ACCESS_KEY
in.travis.yml
).
-
when using
brew test-bot
, never use--ci-pr
,--ci-master
,--ci-testing
on your mac as it is going to remove everything installed. You can still usebrew test-bot
andbrew test-bot --ci-upload
; what I would do is to make sure everything runs correctly locally (except forbrew doctor
which generally always fails on my mac) using:brew test-bot touist.rb
-
On Travis CI, mac builds are costly (in time). For trying to make the ย
.travis.yml
work, I really recommend to useos: linux
instead of a mac image. -
Also, when you find yourself stuck when trying to make the CI work, I recommend to re-run your job in debug mode in order to see what is going wrong. Again, as mac images are slow, I recommend to use the linux alternative.
-
if you see that
brew test-bot
is not building/testing any bottle, check ย that you have made the symlink (in travis-ci) at$(brew --repo touist/touist)
ย that should target$TRAVIS_BUILD_DIR
. It is important becausebrew test-bot
ย is testing the tap inside$(brew --repo)
but the actual repo cloned by travis is in$TRAVIS_BUILD_DIR
. -
the
--keep-old
option when uploading seems to be a good idea (= keeps bottles you previously built but that you don't produce anymore) but it keeps failing on stupid errors all the time; I don't use it anymore. -
adding "revision 1" to a formula does not seem to be appropriate for forcing a bottle to be rebuild
-
the tests/building of the bottle only happens when you push a series of commit on ONE single formula; if you include some edits to .travis.yml for example, the push won't trigger build of the bottle. This caused me a lot of time lost on try and errors.
-
when creating a new formula, you may get these kind of errors during the build (on PR, branch and master):
* New formulae should not require patches to build. Patches should be submitted and accepted upstream first. * GitHub fork (not canonical repository) * GitHub repository too new (<30 days old)
ย First, make sure the bottles build without errors and that the only step failing is brew audit --online
.
Then, accept the PR and make a small change to the formula to force the rebuild of the bottle. The errors
will disappear (as they are only there when the formula is new).
WARNING: in my solution, in the deploy state, I do on a linux build:
brew test-bot --ci-upload
which packs all bottles at the same time and I get weird prefix: /usr/local
and
cellar: :any_skip_relocation
. I fixed this with a little trick: change every
/usr/local
with /home/linuxbrew/.linuxbrew
so that brew test-bot --ci-upload
removes the cellar:
field when it is the default cellar.
Example of such problem:
MacOS lingeling-151109.el_capitan.bottle.json
and lingeling-151109.sierra.bottle.json
have:
{
"prefix": "/usr/local",
"cellar": "any_skip_relocation",
}
but the linux bottle lingeling-151109.x86_64_linux.bottle.json
has:
{
"prefix": "/home/linuxbrew/.linuxbrew",
"cellar": "/home/linuxbrew/.linuxbrew/Cellar",
}
and it wrongly became the following bottle DSL, probably because the linux bottle.json
is the last to be read, so it takes precedence over the previous ones:
bottle do
root_url "https://dl.bintray.com/touist/bottles-touist"
sha256 "141f132d5ed58f930ada22fbe81d63e4115b9f78b8fb2ca3fa266e30e5fdf0a3" => :sierra
sha256 "be1e33155185e36cbfbc48a743efd14d65edbfc174d2e31e4386414ea2153233" => :el_capitan
sha256 "b24481b3ac4a7b02114ea5d627aff7cd6e2bb1551e4b4f073d3aa1d580f787c8" => :x86_64_linux
end
It should have been
bottle do
root_url "https://dl.bintray.com/touist/bottles-touist"
cellar :any_skip_relocation if OS.mac?
sha256 "141f132d5ed58f930ada22fbe81d63e4115b9f78b8fb2ca3fa266e30e5fdf0a3" => :sierra
sha256 "be1e33155185e36cbfbc48a743efd14d65edbfc174d2e31e4386414ea2153233" => :el_capitan
sha256 "b24481b3ac4a7b02114ea5d627aff7cd6e2bb1551e4b4f073d3aa1d580f787c8" => :x86_64_linux
end
- When a formula is linuxbrew-only, it has the comment
# tag "linuxbrew"
in it.
As mentionned by Steven Peters here, when a dependency is updated, we must also update the formula that depends on it. In 'core', this is done by incrementing the revision number for all formulas that rely on the updated formula.
In this tap, a cron is daily testing every formula (brew install <formula> && brew linkage --test <formula>
). If a linkage breakage is detected, it automatically increments the revision number. It has been working fine for now, the only problem is to have travis-ci working every single day (I am getting better and better at that).
AWS S3 buckets are only free for the first 12 months. Are there any truly free options to store the bottles before deployment?