21

I'd like to use the GitHub Actions cache (actions/cache@v2) in order to save the cache of my brew install command, instead of re-downloading all the dependencies over and over again.

This would make my builds faster, how can I achieve it?

Francesco Borzi
  • 56,083
  • 47
  • 179
  • 252

2 Answers2

4

This is tricky because actions/cache depends on you putting the libraries in a specific folder that then can be cached and retrieved. And system files have a lot of dependencies which makes each library unique when it comes to this process.

Here are three options that you can take to accomplish this

Path 1:

We could look into installing the brew packages into a specific folder but we cannot do that per package but instead for the whole OS. But according to homebrew https://github.com/Homebrew/brew/blob/664d0c67d5947605c914c4c56ebcfaa80cb6eca0/docs/Installation.md#untar-anywhere it's not a good idea as there could be issues with packages not installing properly. Also this would basically be caching everything which at the end of the day we don't know if it'll be faster than just installing the latest packages you need.

Path 2:

Install the package as you normally would with brew and then copy binaries. The caveat here is that you will need to do this on a per library basis as dependencies some times can get out of hand. Here is an example Caching APT packages in GitHub Actions workflow by going through this process with apt-get in linux, but you can follow same process with brew

Path 3:

Find or create your own docker file image that for which you keep up to date with the latest packages that you want. This would allow you to always have the latest and this will allow you to initialize your steps inside a container build from your docker image. This will then satisfy your system dependencies.

 jobs:
   ci:
     runs-on: ubuntu-latest
     container:
       image: <your-personalized-docker-image>
Edward Romero
  • 2,905
  • 1
  • 5
  • 17
3

In your workflow job configuration:

    steps:
    - name: Update Homebrew
      run: |
        brew update --preinstall
        cat "$(brew --repository)/Library/Taps/homebrew/homebrew-core/Formula/foo.rb" > .github/brew-formulae
    - name: Configure Homebrew cache
      uses: actions/cache@v2
      with:
        path: |
          ~/Library/Caches/Homebrew/foo--*
          ~/Library/Caches/Homebrew/downloads/*--foo-*
        key: brew-${{ hashFiles('.github/brew-formulae') }}
        restore-keys: brew-
    - name: Install Homebrew dependencies
      run: |
        env HOMEBREW_NO_AUTO_UPDATE=1 brew install foo

Here ~/Library/Caches/Homebrew corresponds to the output of brew --cache.

Rather than store the entire directory to the GitHub Actions cache, the above configuration will only store the files related to the "foo" package. This is because the Homebrew cache directory on the macOS images provided by GitHub already contains files for the base Homebrew install (which you don't need to store again).

You should note that this cache is unlikely to speed up your workflow. Installing Homebrew bottles (the default) will usually have similar performance to downloading from GitHub/Azure's own storage cache since the bottle files are served on bintray's CDN.

p00ya
  • 3,659
  • 19
  • 17
  • A query: How would one extend this to two packages? I ask because GitHub Actions' macOS images seems to have broken some low-level things recently. The only solution I've found now is to do a `brew reinstall --build-from-source` of two packages (cf https://stackoverflow.com/a/19086854/1876449). Thus, caching will win by a long shot! I just want to know what bits here I might need to do to cache two packages. I'm fairly useless at GitHub Actions. I'll try to figure it out, but a guru like you might know better! – Fortran Dec 01 '20 at 15:13
  • Actually if you're using `--build-from-source` then the cache paths in this answer won't be sufficient (since in that case you'll want to cache the bottle or the keg to prevent recompilation). I'll post an update to this answer for that case. In terms of multiple packages, you can just add the additional lines to the `path` list and the package to the `brew install` command. – p00ya Dec 01 '20 at 23:42
  • Yeah. I feel dumb now. I figured out a different way, actually: ``` - name: Fix gmp and mpfr on macOS if MPI isn't cached if: steps.cache-mpi.outputs.cache-hit != 'true' && matrix.os == 'macos-10.15' run: | brew reinstall --build-from-source gmp mpfr ``` – Fortran Dec 02 '20 at 21:50
  • See example: https://github.com/DataDog/datadog-agent-macos-build/blob/master/.github/workflows/macos.yaml#L58 – Melroy van den Berg Feb 09 '22 at 22:27
  • 1
    And another example using `cache-hit`: https://github.com/jawang35/dotfiles/blob/master/.github/workflows/ci.yml#L16 – Melroy van den Berg Feb 09 '22 at 22:29
  • It says `gtkmm3 3.x.x is already installed, it's just not linked.` So I also execute `brew link gtkmm3` in GH Actions. But the build still fails! `-- Checking for module 'gtkmm-3.0' -- Package 'giomm-2.4', required by 'gtkmm-3.0', not found` :( – Melroy van den Berg Feb 10 '22 at 21:16