Skip to content

Instantly share code, notes, and snippets.

@mavaddat
Last active May 17, 2022 13:35
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save mavaddat/f4f83b23386111bedd7f8199949601af to your computer and use it in GitHub Desktop.
Save mavaddat/f4f83b23386111bedd7f8199949601af to your computer and use it in GitHub Desktop.
The broken ruby can be fixed by re-prioritizing the paths or fixing broken snaps. Start the script using `./fixBrokenRuby.sh`

Common Issues with Homebrew

This is a list of commonly encountered problems with packages on Homebrew, known issues, and their solutions. The Homebrew logo

brew complains about absence of “Command Line Tools”

You need to have the Xcode Command Line Utilities installed (and updated): run xcode-select --install in the terminal.

Ruby: bad interpreter: /usr/bin/ruby^M: no such file or directory

You cloned with git, and your Git configuration is set to use Windows line endings. See this page: https://help.github.com/articles/dealing-with-line-endings

Ruby: bad interpreter: /usr/bin/ruby

You don’t have a /usr/bin/ruby or it is not executable. It’s not recommended to let this persist; you’d be surprised how many .apps, tools and scripts expect your macOS-provided files and directories to be unmodified since macOS was installed.

brew update complains about untracked working tree files

After running brew update, you receive a Git error warning about untracked files or local changes that would be overwritten by a checkout or merge, followed by a list of files inside your Homebrew installation.

This is caused by an old bug in in the update code that has long since been fixed. However, the nature of the bug requires that you do the following:

cd "$(brew --repository)"
git reset --hard FETCH_HEAD

If brew doctor still complains about uncommitted modifications, also run this command:

cd "$(brew --repository)/Library"
git clean -fd

launchctl refuses to load launchd plist files

When trying to load a plist file into launchctl, you receive an error that resembles

Bug: launchctl.c:2325 (23930):13: (dbfd = open(g_job_overrides_db_path, [...]
launch_msg(): Socket is not connected

or

Could not open job overrides database at: /private/var/db/launchd.db/com.apple.launchd/overrides.plist: 13: Permission denied
launch_msg(): Socket is not connected

These are likely due to one of four issues:

  1. You are using iTerm. The solution is to use Terminal.app when interacting with launchctl.
  2. You are using a terminal multiplexer such as tmux or screen. You should interact with launchctl from a separate Terminal.app shell.
  3. You are attempting to run launchctl while logged in remotely. You should enable screen sharing on the remote machine and issue the command using Terminal.app running on that machine.
  4. You are su‘ed as a different user.

brew upgrade errors out

When running brew upgrade, you see something like this:

$ brew upgrade
Error: undefined method `include?' for nil:NilClass
Please report this bug:
    https://docs.brew.sh/Troubleshooting
/usr/local/Library/Homebrew/formula.rb:393:in `canonical_name'
/usr/local/Library/Homebrew/formula.rb:425:in `factory'
/usr/local/Library/Contributions/examples/brew-upgrade.rb:7
/usr/local/Library/Contributions/examples/brew-upgrade.rb:7:in `map'
/usr/local/Library/Contributions/examples/brew-upgrade.rb:7
/usr/local/bin/brew:46:in `require'
/usr/local/bin/brew:46:in `require?'
/usr/local/bin/brew:79

This happens because an old version of the upgrade command is hanging around for some reason. The fix:

cd "$(brew --repository)/Library/Contributions/examples"
git clean -n # if this doesn't list anything that you want to keep, then
git clean -f # this will remove untracked files

Python: easy-install.pth cannot be linked

Warning: Could not link <formula>. Unlinking...
Error: The `brew link` step did not complete successfully
The formula built, but is not symlinked into /usr/local
You can try again using `brew link <formula>'

Possible conflicting files are:
/usr/local/lib/python2.7/site-packages/site.py
/usr/local/lib/python2.7/site-packages/easy-install.pth
==> Could not symlink file: /homebrew/Cellar/<formula>/<version>/lib/python2.7/site-packages/site.py
Target /usr/local/lib/python2.7/site-packages/site.py already exists. You may need to delete it.
To force the link and overwrite all other conflicting files, do:
  brew link --overwrite formula_name

To list all files that would be deleted:
  brew link --overwrite --dry-run formula_name

Don’t follow the advice here but fix by using Language::Python.setup_install_args in the formula as described in Python for Formula Authors.

Upgrading macOS

Upgrading macOS can cause errors like the following:

  • dyld: Library not loaded: /usr/local/opt/icu4c/lib/libicui18n.54.dylib
  • configure: error: Cannot find libz

Following a macOS upgrade it may be necessary to reinstall the Xcode Command Line Tools and brew upgrade all installed formula:

xcode-select --install
brew upgrade

Other local issues

If your Homebrew installation gets messed up (and fixing the issues found by brew doctor doesn’t solve the problem), reinstalling Homebrew may help to reset to a normal state. To easily reinstall Homebrew, use Homebrew Bundle to automatically restore your installed formulae and casks. To do so, run brew bundle dump, uninstall, reinstall and run brew bundle install.

#! /bin/bash
# This script will fix the Homebrew installation on a Mac
# $1 is the broken executable
# $2 is the number of re-tries
# check if any brews have problems
BREW_PROBLEMS=$(brew doctor | grep -c "Your Homebrew is not working")
# if there are problems, fix them
if [ $BREW_PROBLEMS -gt 0 ]; then
echo "Fixing Homebrew..."
brew doctor
brew update
brew upgrade
brew cleanup
brew cask cleanup
# check if any brews have problems
BREW_PROBLEMS=$(brew doctor | grep -c "Your Homebrew is not working")
# if we have retries, then re-run the script
if [ $BREW_PROBLEMS -gt 0 ]; then
if [ $2 -gt 0 ]; then
echo "Retrying Homebrew fix..."
bash $0 $1 $(( $2 - 1 ))
else
echo "Failed to fix Homebrew"
exit 1
fi
fi
else
echo "Homebrew is OK."
fi
# Re-install $1
if [ -n "$1" ]; then
echo "Re-installing $1..."
# Remove the broken executable
brew uninstall $1
# Re-install the broken executable
brew install $1
fi
# if the exit code is not 0, re-run the script
if [ $? -ne 0 ]; then
if [ -n "$2" ]; then
if [ $2 -gt 0 ]; then
echo "Re-trying $0..."
bash $0 $1 $(($2 - 1))
else
echo "Failed to fix $1."
fi
fi
fi
#! /bin/bash
# Check if Ruby installed via Snap
sys_ruby=`which ruby`
if [ "$sys_ruby" = "/snap/bin/ruby" ]; then
echo "Ruby is installed via Snap"
# check if snaps are broken
python3 ./fixSnaps.py ruby 3 # retries 3 times
# Check if Ruby installed via Homebrew
elif [ "$sys_ruby" = "/usr/local/bin/ruby" ]; then
echo "Ruby is installed via Homebrew"
# check if brews are broken
python3 ./fixBrews.py ruby 3 # retries 3 times
fi
# Assume path problem
./reorderPathForExe.sh
#! /usr/bin/python3
import subprocess
import os
import sys
# the executable that is not working
BROKE_EXE = sys.argv[1] if len(sys.argv) > 1 else None
# how many times to re-try
RETRY_COUNT = sys.argv[2] if len(sys.argv) > 2 else 1
def get_broken_snaps_or_exit(exe=None) -> list:
"""
Provides a list of the broken snaps
"""
# If the executable is not broken, exit
if (
exe and
subprocess.call([exe, "--version"]) * subprocess.call([exe, "-v"]) == 0
):
print("{} is not broken".format(BROKE_EXE))
exit(0)
raw_snap_out = None
raw_snap_out = subprocess.check_output(["snap", "list"])
snap_out = raw_snap_out.decode('utf-8').splitlines()
# Return list where 'Notes' field says "broken"
broken = [x for x in snap_out[1:] if "broken" in x]
if len(broken) == 0:
print("No broken snaps found")
exit(0)
return broken
# Get the list of broken snaps
broken_snaps = get_broken_snaps_or_exit(BROKE_EXE)
def _fix_snaps_or_exit(snaps: list) -> None:
# run sudo snap remove on each broken snap
result = None
for snap in snaps:
snap_name = snap.split()[0]
print("Removing broken snap: %s" % snap_name)
result = subprocess.run(["sudo", "snap", "remove", snap_name])
if result.returncode != 0:
print("Error removing snap: %s" % snap_name)
exit(1)
# add a sleep to give the snap a chance to finish
subprocess.run(["sleep", "1"])
# run sudo snap install on each removed snap
for snap in snaps:
# re-add the snap
print("Adding snap: %s" % snap_name)
# check if we need "--classic" flag
has_classic = "classic" in subprocess.check_output(
["snap", "info", snap_name]
).stdout.decode('utf-8')
if has_classic:
result = subprocess.run(
["sudo", "snap", "install", snap_name, "--classic"]
)
else:
result = subprocess.run(["sudo", "snap", "install", snap_name])
if result.returncode != 0:
print("Error adding snap: %s" % snap_name)
exit(1)
# add a sleep to give the snap a chance to finish
subprocess.run(["sleep", "1"])
def fix_snaps_or_exit(snaps: list) -> None:
# check if we have root access
if os.geteuid() != 0:
print("Run this script as root to fix broken snaps")
exit(1)
# get the core snap
core_snaps = [x for x in snaps if "canonical" in x]
_fix_snaps_or_exit(core_snaps)
# get the other snaps
other_snaps = [x for x in snaps if "canonical" not in x]
_fix_snaps_or_exit(other_snaps)
# Fix the broken snaps
fix_snaps_or_exit(broken_snaps)
# if we still have broken snaps, we need to retry
if len(get_broken_snaps_or_exit(BROKE_EXE)) > 0 and RETRY_COUNT > 1:
print("Retrying to fix broken snaps")
# run the script again
subprocess.run(["./fixSnaps.py", BROKE_EXE, str(RETRY_COUNT - 1)])
else:
print("Failed to fix broken snaps")
exit(1)
#! /bin/bash
#$1 is the executable that isn't working correctly
# This script is used to modify the path priority for
# the executable that is not working correctly
# Store the output of `which` application for the executable
execPaths=($(which -a $1))
execPath="" # this will be the path that the user selects to prioritize
# if the argument is not found, exit
if [ $? -ne 0 ]; then
echo "Error: $1 not found"
exit 1
fi
# if there is only one path, then there are no alternatives, nothing to do
if [ ${#execPaths[@]} -eq 1 ]; then
echo "You have only one path to determine $1 ('${execPaths[0]}')"
exit 1
elif [ ${#execPaths[@]} -eq 2 ]; then
echo -e "\e[0m\e[36mCurrently, '${execPaths[0]}' is the priority for $1."
echo -e "Would you rather prioritize '${execPaths[1]}' instead (y/N)?\e[0m"
read answer
if [ "$answer" != "y" ]; then
exit 1
fi
execPath=${execPaths[1]}
else
# tell the user what paths are determining the executable
echo -e "\e[1;32mThe following paths determine which $1:"
#loop through the paths
for execPath in ${execPaths[@]}; do
echo "$execPath"
done
PS3="Please enter the number of the path you would like to prioritize: "
echo -e "\e[0m\e[36mWhich path do you want to \e[0m\e[36m\e[1mprioritize?\e[0m\e[36m"
select execPath in ${execPaths[@]:1}; do
# if the user selects a path, break out of the loop
if [ $? -eq 0 ]; then
break
fi
done
fi
# calculate unintended consequences
affected=($(ls $execPath))
bffected=( $(ls ${execPaths[0]}) )
# calculate set intersection of `affected` and `bffected`
common=($(comm -12 <(printf '%s\n' "${affected[@]}" | LC_ALL=C sort) <(printf '%s\n' "${bffected[@]}" | LC_ALL=C sort)))
# warn about consequences
echo -e "\e[31mWarning: Path re-ordering will also affect the following files:"
for file in $common; do
echo "$file"
done
echo -e "Do you want to continue? (y/N)\e[0m"
read answer
if [ "$answer" != "y" ]; then
exit 1
fi
# reorder the system path to prioritize the selected path
# first, remove the path from the system path
PATH=$(echo $PATH | sed -e "s;:$exePath;;")
# then, add the path to the front of the system path
PATH="$exePath:$PATH"
# ask if the user wants to save the new path
echo "Do you want to save the new path to your bash profile? (y/N)"
read answer
if [ "$answer" != "y" ]; then
echo "Before export,"
declareNewPath $1
export PATH=$PATH
echo "After export (which is not included in your bash profile),"
declareNewPath $1
exit 0
fi
# save the new path to the bash profile
echo "export PATH=\"$PATH\"" >> ~/.bash_profile
declareNewPath $1
function declareNewPath() {
# tell the user what the new path is that determines the executable
echo "System declares that new path to $1 is:"
echo "$( which $1 )"
exit 0
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment