upgrade or install a homebrew formula - homebrew

In my CI-setup, i would like to make sure that the newest version of a given formula is installed, regardless of whether it is already installed or not.
i'm currently using something like:
brew update
brew install FORMULA || (brew upgrade FORMULA && brew cleanup FORMULA)
What are the pitfalls with that approach? Is there a nicer approach to the problem (e.g. by first querying whether FORMULA is already installed, rather than relying on brew install to fail only if FORMULA is installed)?

I you want to install a Homebrew package if it does not already exist, and upgrade it otherwise, the best solution is to use Homebrew Bundle which is officially part of the Homebrew family. If that doesn't work for you, and you want to roll your own solution, you should reference at the suggestions below.
There are other situation where a brew install might fail, other than a package already being installed. I'm not sure, but it doesn't look like the brew install command emits an exit status other than 1 on failure, so you have two options:
Search stderr for "not installed" and check against that
Use a different approach
The most common approach I've seen used for this purpose is to check if the package is installed with the command brew ls --versions:
function install_or_upgrade {
if brew ls --versions "$1" >/dev/null; then
HOMEBREW_NO_AUTO_UPDATE=1 brew upgrade "$1"
else
HOMEBREW_NO_AUTO_UPDATE=1 brew install "$1"
fi
}
You'll want to use HOMEBREW_NO_AUTO_UPDATE=1 if you're installing multiple packages so that Homebrew does not try to update in between each install/upgrade.

I've been using the following. Depending on the use case, I'll use a shell function as follows:
function smart_brew() {
HOMEBREW_NO_AUTO_UPDATE=1 brew `brew ls --versions "$1" | wc -l | xargs expr | sed 's/0/install/' | sed 's/1/upgrade/'` "$1"
}
and sometimes as a definition in a Makefile:
define smart_brew
HOMEBREW_NO_AUTO_UPDATE=1 brew `brew ls --versions "$(1)" | wc -l | xargs expr | sed 's/0/install/' | sed 's/1/upgrade/'` "$(1)"
endef
dev:
$(call smart_brew,formula)
Same basic idea

Related

How to install node with home brew when "Brew postinstall node not working."

I cannot get node installed on my computer because it keeps failing the post-install.
Warning: The post-install step did not complete successfully
You can try again using:
brew postinstall node
when I reinput brew postinstall node I get the same error message.
Does anyone know what is going wrong?
I will suggest you to install it using nvm (node version manager) instead of homebrew. This allow to control which version is being use, brew is getting better to provide versioning but it definitely not the right chose for node
You can find more information about it here
nvm allows you to quickly install and use different versions of node
via the command line.
To install it, use:
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.1/install.sh | bash
Edit your ~/.zshrc file to include:
export NVM_DIR="$([ -z "${XDG_CONFIG_HOME-}" ] && printf %s "${HOME}/.nvm" || printf %s "${XDG_CONFIG_HOME}/nvm")"
[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh" # This loads nvm
Then you can install node using:
nvm install <version>

Homebrew: list the packages installed from taps?

Over time, I've installed a number of packages with Homebrew, mostly from the default repo of formulae (homebrew-core), but some from other locations via brew tap.
Now I'm putting together some install scripts to make my dev environment more reproducible, and I'm trying to figure out which packages can be installed by a simple brew install and which require a brew tap beforehand.
The ability to query brew has proved useful for figuring out which options I used for each package, but not for this tap-related question. Is there a way to do this without manually going through each package and seeing where it's available?
I found a couple ways that work.
brew list --full-name
Slower, but a little more informative:
brew info $(brew list) | grep '^From:' | sort
This expression would return a list of installed 3rd party packages only:
brew list --full-name -1 | grep /
…for the respective list of taps in use, try:
brew list --full-name -1 | grep / | cut -d"/" -f1 -f2 | sort | uniq
The brew list --full-name variants in the other answers did not work for me (in 2022). This, however, does:
brew list -1 | xargs brew info | grep '^From:'
Alternatively, to see only casks, use this:
brew list --cask -1 | xargs brew info --cask | grep '^From:'
... or only leaves:
brew leaves | xargs brew info | grep '^From:'

How to uninstall GNU parallel?

I installed GNU parallel with (wget pi.dk/3 -qO - || curl pi.dk/3/) | sh, but I believe it is preferable to install it with Homebrew instead, because that makes updating easier.
How do I remove everything the script created? I'm using OS X.
Try this:
(wget pi.dk/3 -qO - || curl pi.dk/3/) | bash
cd parallel-20*/
make uninstall

Installed GNU grep on OSX, but can't use

I've tried installing GNU grep on OSX, and it seems to be installed, but I can't use it.. I've done so using homebrew, Macports is having some issues currently, so I can't use that.
To install: brew tap homebrew/dupes; brew install grep
Which returns: Warning: homebrew/dupes already tapped! Warning: homebrew/dupes/grep-2.21 already installed
Symlinking seems to work to /usr/local/bin/ggrep. When I add the alias alias grep="ggrep" and do grep --version, I get -bash: ggrep: command not found. Which is true, since there is no ggrep in the folder. I've tried installing with and without --with-default-names.
The folder /usr/local/Cellar/grep/2.21/bin/ contains the following:
-r-xr-xr-x 1 Wes admin 158 Oct 14 09:27 egrep
-r-xr-xr-x 1 Wes admin 158 Oct 14 09:27 fgrep
Which is strange to me, since the documentation implies that The command has been installed with the prefix "g".
I've seen the following post, but none of the solutions work for me. Updating grep for Mac OS 10.7
Does anyone have any solutions? I really want to use GNU grep.
Output of brew unlink grep && brew link grep -v:
Unlinking /usr/local/Cellar/grep/2.21...
6 symlinks removed
Linking /usr/local/Cellar/grep/2.21...
ln -s ../Cellar/grep/2.21/bin/egrep egrep
ln -s ../Cellar/grep/2.21/bin/fgrep fgrep
ln -s ../../Cellar/grep/2.21/share/info/grep.info grep.info info /usr/local/share/info/grep.info
ln -s ../../../Cellar/grep/2.21/share/man/man1/egrep.1 egrep.1
ln -s ../../../Cellar/grep/2.21/share/man/man1/fgrep.1 fgrep.1
ln -s ../../../Cellar/grep/2.21/share/man/man1/grep.1 grep.1
6 symlinks created`
New:
brew uninstall grep; brew install grep
$ which -a grep
/usr/bin/grep
$ which -a ggrep
/usr/local/bin/ggrep
$ echo $PATH
/opt/local/bin:/opt/local/sbin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin:/usr/local/git/bin
This time, it seems something is different. ggrep is finally installed! I think the unlink/link straightened some things out.
How can I set ggrep as the default? With alias?
To make GNU grep the default install it with --with-default-names:
$ brew install grep --with-default-names
If you already have it installed use reinstall instead of install.
Ensure that /usr/local/bin (the location of GNU grep) is before /usr/bin (the location of the BSD grep) in your $PATH; which seems to be the case here.
You might have to start a new shell session afterward because Bash caches the binaries paths for the current session. This means that the first time you use grep it’ll determine which binary it’ll use depending on your $PATH and cache it. The next time it’ll use the cached value so changing your $PATH won’t change anything until you reload the shell.
Offically out of date for the answer above.
As of Homebrew version 2.0.0 the --with-default-names flag is no longer available.
from the official documentation
--with-default-names is no longer supported. It is now installed into its own directory and you will need to adjust your PATH to use it.
What you need to do is to add this command to your shell
PATH="/usr/local/opt/grep/libexec/gnubin:$PATH"
As of Febuary 8, 2020, You could either prepend the PATH and MANPATH variables as eleijonmarck proposes, or use symbolic links to a path loaded early on the PATH. If you choose to use eleijonmarck's method, I would do it differently.
You need to know where Homebrew installs grep binaries.
PATH=/usr/local/opt/grep/bin:$PATH
as opposed to libexec. The Filesystem Hierarchy specifies that the libexec directory
includes internal binaries that are not intended to be executed directly by users or shell scripts.
Also, if you are doing that, then you might want the MAN pages too.
MANPATH=/usr/local/opt/grep/share/man:$MANPATH
The other option is to use symbolic links. You might think to do this:
ln -s /usr/local/opt/grep/bin/grep /usr/local/bin/
The thing is, this won't work, because MacOS has grep as a built-in. Instead, would need to remove or replace the built-in at /usr/bin. You could rename it mv /usr/bin/grep /usr/bin/bsdgrep. This effectively makes grep not findable at /usr/bin, but still usable as bsdgrep (advantage of this technique). Assuming you added grep to /usr/local/bin, you could test your change with:
which grep && grep -V # prove old grep found first
which grep && grep -V # prove new grep found first
The disadvantage of this technique is that you must add a symbolic link per binary.

How do you migrate a Homebrew installation to a new location?

I have a Homebrew installation in $HOME/brew, and historically it has worked well. Unfortunately, over time Homebrew has become less and less tolerant of installations outside of /usr/local. Various formulae make hard assumptions about the installation prefix, and do not work properly (i.e., were not tested) with a non-standard prefix. The brew doctor command even goes so far as to warn about this now:
Warning: Your Homebrew is not installed to /usr/local
You can install Homebrew anywhere you want, but some brews may only build
correctly if you install in /usr/local. Sorry!
As such, I would now like to migrate my Homebrew installation over to /usr/local. However, I am loath to simply mv all the files, as I suspect this will cause problems. I could not find any instructions on the Homebrew site or here on migrating an existing installation to a new prefix. Of course, I could uninstall Homebrew and then reinstall it, but I would prefer not to rebuild all my kegs.
Is there any existing script or documented practice for performing such a migration?
Or is this impossible due to hardcoded absolute paths in linked binaries?
The modern way to do this is with homebrew-bundle.
brew tap Homebrew/bundle
brew bundle dump # Creates 'Brewfile' in the current directory
# later ...
brew bundle # Installs packages listed in 'Brewfile'
I just wrote a script to achieve the goal to migrate homebrew packages to a new system, which also applies for your case (named backup-homebrew.sh):
#!/bin/bash
echo '#!/bin/bash'
echo ''
echo 'failed_items=""'
echo 'function install_package() {'
echo 'echo EXECUTING: brew install $1 $2'
echo 'brew install $1 $2'
echo '[ $? -ne 0 ] && $failed_items="$failed_items $1" # package failed to install.'
echo '}'
brew tap | while read tap; do echo "brew tap $tap"; done
brew list --formula | while read item;
do
echo "install_package $item '$(brew info $item | /usr/bin/grep 'Built from source with:' | /usr/bin/sed 's/^[ \t]*Built from source with:/ /g; s/\,/ /g')'"
done
echo '[ ! -z $failed_items ] && echo The following items were failed to install: && echo $failed_items'
You should first run this script on your original system to generate a restore script:
./backup-homebrew.sh >restore-homebrew.sh && chmod +x restore-homebrew.sh
Then, after installing Homebrew on your new system (in your case the same system), simply run restore-homebrew.sh to install all those packages you have on your original system.
The benefits of this script over the one given by #ctrueden is that this script also tries to back up the installation options you used when you installed the packages.
A more detailed description is in my blog.
As suggested by Peter Eisentraut, I indeed ended up migrating my Homebrew installation by reinstalling it. You can script things a bit to retap all your extra taps, and reinstall all your previously installed kegs, without too much manual work:
#!/bin/sh
# save list of kegs for later reinstallation
brew list > kegs.txt
# back up old Homebrew installation
mv $HOME/brew $HOME/old-brew
# install Homebrew into /usr/local
ruby -e "$(curl -fsSL https://raw.github.com/mxcl/homebrew/go)"
# retap all the taps
# NB: It is not enough to move the tap repos to their new location,
# because Homebrew will not automatically recognize the new formulae.
# There might be a configuration file we could edit, but rather than
# risk an incomplete data structure, let's just retap everything.
for tapDir in $HOME/old-brew/Library/Taps/*
do (
cd $tapDir
tap=$(git remote -v | \
grep '(fetch)' | \
sed 's/.*github.com\///' | \
sed 's/ (fetch)//')
/usr/local/bin/brew tap $tap
) done
# reinstall all the kegs
/usr/local/bin/brew install $(cat kegs.txt)
# much later... ;-)
rm -rf kegs.txt $HOME/old-brew
Of course, customized Homebrew installations will have additional wrinkles. For example, if you have committed changes to any of your Homebrew-related Git repos, you may want to import that work before reinstalling your kegs or blowing away your old installation:
cd /usr/local
for f in $(find . -name '.git')
do (
repoDir=$(dirname $f)
cd $f/..
git remote add old-brew-$f $(dirname $HOME/old-brew/$f/..)
git fetch old-brew-$f
) done
Note that I only tested the second snippet above very lightly, as I personally have not customized my Homebrew in such a way.
Another aspect of Homebrew not addressed by this approach is custom flags using during your original installation. For example, to install wine you need to install various dependencies with the --universal flag, and the script above will not reinstall them with such flags enabled. See #xuhdev's answer for a solution that does so.
Or is this impossible due to hardcoded absolute paths in linked binaries?
Indeed. You'll need to reinstall everything from scratch.

Resources