Can I use Homebrew?

I’m used to using Homebrew as my package manager - can I use it on Platform.sh?

You can run the installer during your build hook using the install script for Linux in that repository.

Modify your build hook to include:

hooks:
    build: |
        # Retrieve the Homebrew install script.
        bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install.sh)"
        
        # Add brew's environment variables to the profile so they're available during SSH.
        echo "eval $($PLATFORM_APP_DIR/.linuxbrew/bin/brew shellenv)" >> $PLATFORM_APP_DIR/.profile

        # Source the brew command.
        eval $($PLATFORM_APP_DIR/.linuxbrew/bin/brew shellenv)

        # Opt out of Homebrew analytics
        brew analytics off

        # Homebrew recommends installing gcc if you wish.
        brew install gcc

        # Then you can install your packages with Homebrew.
        brew install hello

Since sudo is not available on Platform.sh, the installer places Homebrew in a .linuxbrew directory within app. The eval lines export a set of environment variables specific to Homebrew to the environment and to a local .profile (so that brew can be run during SSH).

export HOMEBREW_CELLAR="/app/.linuxbrew/Cellar";
export HOMEBREW_REPOSITORY="/app/.linuxbrew/Homebrew";
export PATH="/app/.linuxbrew/bin:/app/.linuxbrew/sbin${PATH+:$PATH}";
export MANPATH="/app/.linuxbrew/share/man${MANPATH+:$MANPATH}:";
export INFOPATH="/app/.linuxbrew/share/info:${INFOPATH:-}";

Note

You can see one use case of installing Pyodbc for Django as an example.

Homebrew has changed their install.sh file so these instructions no longer work. However… there is an alternative way of installing straight from the tarball which does work

    echo "Installing homebrew"
    mkdir -p /app/.linuxbrew

    echo "Downloading tarball from master repository"
    curl -SsL https://github.com/Homebrew/brew/tarball/master -o brew.tar.gz
    
    echo "Unpacking into .linuxbrew folder"
    tar xzf brew.tar.gz --strip-components 1 -C /app/.linuxbrew/
    rm brew.tar.gz

    eval $(/app/.linuxbrew/bin/brew shellenv)
    brew analytics off

I’ve spent some time cleaning up some code and making a simple bash script that can be used to simply install one or more brew packages.

Create a file called install_brew_packages.sh with the following contents into the root of your project (where your .platform.app.yaml lives).

run() {
    # Run the compilation process.
    # cd $PLATFORM_CACHE_DIR || exit 1;

    # if .linuxbrew folder exists, we can assume everything is installed
    cache_folder="${PLATFORM_CACHE_DIR}/.linuxbrew"
    cache_file="$cache_folder/cached_components"
    echo "Cache folder $cache_folder"
    echo "Cache file $cache_file"
    echo "Current folder $(pwd)"
    # check if we have brew installed
    if [ ! -f "$cache_folder/bin/brew" ]; 
    then
        install_brew
    else
        copy_lib_from_cache
    fi
    

    load_brew
    
    # Check to see if the cache file exists
    if [ ! -f ${cache_file} ]; 
    then 
        echo "" > $cache_file
    fi
    
    # check if cache_contents actually matches what we want
    cache_contents=$(<"$cache_file")
    if [[ "$cache_contents" != "$@" ]]; then
        # Not the same components, so reinstall
        install_components $@
        copy_lib_to_cache $@
        echo "$@" > $cache_file
    fi
    
    
    # Always write the .profile file, so that we can be sure that all bins are immediately ready for use
    write_profile
    
}

copy_lib_to_cache() {
    echo "Copy to cache..."
    cp -Rf $PLATFORM_APP_DIR/.linuxbrew $PLATFORM_CACHE_DIR
}

copy_lib_from_cache() {
    echo "Copy from cache..."
    cp -Rf $PLATFORM_CACHE_DIR/.linuxbrew $PLATFORM_APP_DIR
}

install_brew() {
    echo "Installing homebrew"
    mkdir -p /app/.linuxbrew

    echo "Downloading tarball from master repository"
    curl -SsL https://github.com/Homebrew/brew/tarball/master -o brew.tar.gz
    
    echo "Unpacking into .linuxbrew folder"
    tar xzf brew.tar.gz --strip-components 1 -C /app/.linuxbrew/
    rm brew.tar.gz
    
    copy_lib_to_cache
}

load_brew() {
    eval $(/app/.linuxbrew/bin/brew shellenv)
    brew analytics off
}

install_components() {
  components_to_install="$@"
  echo "Installing components: '$components_to_install'"
  for f in $components_to_install
  do
		  echo "Installing component: '$f'"
      install_component $f
  done
}

install_component() {
    brew install $1
}

write_profile() {
  touch /app/.profile
  echo 'export HOMEBREW_CELLAR="/app/.linuxbrew/Cellar";' >> /app/.profile
  echo 'export HOMEBREW_REPOSITORY="/app/.linuxbrew/Homebrew";' >> /app/.profile
  echo 'export PATH="/app/.linuxbrew/bin:/app/.linuxbrew/sbin${PATH+:$PATH}";' >> /app/.profile
  echo 'export MANPATH="/app/.linuxbrew/share/man${MANPATH+:$MANPATH}:";' >> /app/.profile
  echo 'export INFOPATH="/app/.linuxbrew/share/info:${INFOPATH:-}";' >> /app/.profile
}

echo "Installing: $@"
run $@

You can then call the script in the build hook. The script takes 1 or more arguments. Simply specify the packages you want to install.

Building from source with brew is rather slow, but the script will automatically use the cache folder. This will ensure that the subsequent builds are fast.

To install duf and lnav you can do this in the build hook:

hooks:
    build: |
        set -e
        bash install_brew_packages.sh duf lnav

Full source can be found here as well https://github.com/matthiaz/platformsh-tools#install_brew_packagessh

1 Like