App-Ariza.git | t/golden/ | installer-uninstall.sh
#!/bin/sh
# Example App uninstaller — macOS and Linux.
#
# Generated by ariza from App::ExampleApp's ariza.toml. Do not edit this
# file: edit ariza's resources/templates/uninstall-posix.sh.j2 and re-run
# `ariza installers --app=<this repository>`.
#
# curl -fsSL https://raw.githubusercontent.com/example-org/App-ExampleApp/HEAD/uninstall.sh | sh
# sh uninstall.sh
#
# Removes exactly what install.sh created: the versions directory, the
# `current` symlink, the ~/.local/bin link, and the marked PATH block in
# your shell rc files. Anything Example App itself wrote — your data,
# your configuration — is left alone, and this says where it is rather
# than deciding for you.
set -eu
APP_DISPLAY='Example App'
APP_EXEC='exampleapp'
ARIZA_PATH_MARKER="# >>> $APP_EXEC PATH >>>"
ARIZA_PATH_END="# <<< $APP_EXEC PATH <<<"
ARIZA_ROOT="${XDG_DATA_HOME:-$HOME/.local/share}/$APP_EXEC"
ARIZA_BIN_DIR="$HOME/.local/bin"
ARIZA_STATE="${XDG_STATE_HOME:-$HOME/.local/state}/$APP_EXEC"
############################# common runtime ##################################
###############################################################################
# ariza installer runtime (POSIX)
#
# Inlined verbatim into install.sh and uninstall.sh at render time, so a
# generated installer is one self-contained file with nothing to source
# and nothing to download before it can start.
#
# Adapted from the pre-bundle installers' template/lib/common-posix.sh --
# the registry, the package-manager probes and the terminal detection are
# gone (a bundle installs none of that), the PATH marker machinery is
# kept, because getting it wrong duplicates blocks in people's rc files.
#
# Callers must have set: APP_DISPLAY, APP_EXEC, ARIZA_PATH_MARKER,
# ARIZA_PATH_END.
###############################################################################
# Colour only when someone is watching. `curl … | sh` in a CI log is not.
if [ -t 1 ]; then
ariza_c_blue='\033[34m'; ariza_c_green='\033[32m'; ariza_c_red='\033[31m'
ariza_c_yellow='\033[33m'; ariza_c_reset='\033[0m'
else
ariza_c_blue=''; ariza_c_green=''; ariza_c_red=''
ariza_c_yellow=''; ariza_c_reset=''
fi
ariza_log() { printf "${ariza_c_blue}==>${ariza_c_reset} %s\n" "$*"; }
ariza_ok() { printf "${ariza_c_green}ok${ariza_c_reset} %s\n" "$*"; }
ariza_warn() { printf "${ariza_c_yellow}!!${ariza_c_reset} %s\n" "$*" >&2; }
ariza_err() { printf "${ariza_c_red}error${ariza_c_reset} %s\n" "$*" >&2; exit 1; }
ariza_have() { command -v "$1" >/dev/null 2>&1; }
# ---- downloading -------------------------------------------------------------
ariza_download() {
# $1 = url, $2 = destination. Fails on any HTTP error rather than
# writing a 404 page to disk and calling it an archive.
if ariza_have curl; then
curl --fail --location --silent --show-error --retry 3 -o "$2" "$1"
elif ariza_have wget; then
wget --quiet --tries=3 -O "$2" "$1"
else
ariza_err "need curl or wget to download $1"
fi
}
# ---- checksums ---------------------------------------------------------------
ariza_sha256() {
# $1 = file. Echoes the lowercase hex digest, or returns 1 if this
# machine has no tool that can compute one.
if ariza_have sha256sum; then
sha256sum "$1" | cut -d' ' -f1
elif ariza_have shasum; then
shasum -a 256 "$1" | cut -d' ' -f1
else
return 1
fi
}
ariza_first_word() {
# $1 = a `<hex> <filename>` checksum file, as shasum -c reads.
awk 'NR == 1 { print $1; exit }' "$1"
}
ariza_verify_sha256() {
# $1 = file, $2 = expected hex. Fatal on any disagreement.
_actual=$(ariza_sha256 "$1" || true)
[ -n "$_actual" ] || ariza_err "no sha256sum or shasum on this machine, so the download cannot be verified"
if [ "$_actual" != "$2" ]; then
ariza_err "checksum mismatch for $(basename "$1"): expected $2, got $_actual -- not installing"
fi
}
# ---- shell rc PATH wiring ----------------------------------------------------
#
# One marker block, named after the executable rather than after the
# project, so uninstalling one app never removes the line another app is
# relying on. Re-running never duplicates it: the marker is grepped for
# first, and the whole block is skipped when the directory is already on
# PATH, which is the common case on a second install.
ariza_persist_path() {
# $1 = directory to put on PATH.
#
# Records whether the directory was on PATH BEFORE this function
# touched anything, in ARIZA_PATH_WAS_PRESENT. The parting message
# keys on that record, not on $PATH: this function's last act is to
# export the directory into this script's own PATH, so a check made
# after it runs always says "present" and the tell-the-user branch
# becomes unreachable — which is exactly how the first real install
# ended with a command the user's terminal could not find and no
# line telling them why.
_dir=$1
case ":${PATH:-}:" in
*":$_dir:"*)
# Already there for this shell, so a login shell almost
# certainly has it too. Adding a block would be one more
# thing to clean up later for no gain.
ARIZA_PATH_WAS_PRESENT=1
return 0
;;
esac
ARIZA_PATH_WAS_PRESENT=0
_block=$(printf '%s\ncase ":$PATH:" in\n *":%s:"*) ;;\n *) PATH="%s:$PATH"; export PATH ;;\nesac\n%s\n' \
"$ARIZA_PATH_MARKER" "$_dir" "$_dir" "$ARIZA_PATH_END")
_rc_seen=0
for _rc in "$HOME/.zshrc" "$HOME/.bashrc" "$HOME/.bash_profile" "$HOME/.profile"; do
[ -f "$_rc" ] || continue
_rc_seen=$((_rc_seen + 1))
grep -Fq "$ARIZA_PATH_MARKER" "$_rc" 2>/dev/null && continue
# An unterminated last line would otherwise swallow the marker,
# and a decorative blank line before the block would survive the
# uninstaller — accumulating one per install/uninstall cycle.
[ -n "$(tail -c 1 "$_rc")" ] && printf '\n' >>"$_rc"
printf '%s\n' "$_block" >>"$_rc"
ariza_ok "added $_dir to PATH in $_rc"
done
if [ "$_rc_seen" -eq 0 ]; then
ariza_warn "no shell rc file found -- add this to yours by hand:"
printf ' export PATH="%s:$PATH"\n' "$_dir" >&2
fi
PATH="$_dir:$PATH"
export PATH
}
ariza_unpersist_path() {
# Remove our own marker block, and only ours, from every rc file that
# has one. Rewritten through the existing file rather than moved over
# it, so the rc file keeps its mode and ownership.
for _rc in "$HOME/.zshrc" "$HOME/.bashrc" "$HOME/.bash_profile" "$HOME/.profile"; do
[ -f "$_rc" ] || continue
grep -Fq "$ARIZA_PATH_MARKER" "$_rc" 2>/dev/null || continue
_tmp=$(mktemp "${TMPDIR:-/tmp}/ariza-rc.XXXXXX")
awk -v m="$ARIZA_PATH_MARKER" -v em="$ARIZA_PATH_END" '
$0 == m { skip = 1; next }
$0 == em { skip = 0; next }
!skip { print }
' "$_rc" >"$_tmp"
cat "$_tmp" >"$_rc"
rm -f "$_tmp"
ariza_ok "removed the PATH block from $_rc"
done
}
###############################################################################
ariza_usage() {
cat <<USAGE
Usage: uninstall.sh [-h|--help]
Removes $ARIZA_ROOT, the $ARIZA_BIN_DIR/$APP_EXEC link, and the
$APP_EXEC PATH block from your shell rc files. Nothing else is touched.
USAGE
}
ariza_remove_bin_link() {
_bin="$ARIZA_BIN_DIR/$APP_EXEC"
if [ -L "$_bin" ]; then
# Only ours. Someone may have their own $APP_EXEC on PATH, and a
# tool that deletes a link it did not create is a tool nobody
# runs twice.
_target=$(readlink "$_bin" || true)
case $_target in
"$ARIZA_ROOT"/*)
rm -f "$_bin"
ariza_ok "removed $_bin"
;;
*)
ariza_warn "$_bin points at $_target, which is not ours -- leaving it alone"
;;
esac
elif [ -e "$_bin" ]; then
ariza_warn "$_bin is not a symlink -- leaving it alone"
fi
}
main() {
case ${1:-} in
-h|--help) ariza_usage; exit 0 ;;
'') ;;
*) ariza_err "unknown option '$1' -- run with --help" ;;
esac
ariza_log "uninstalling $APP_DISPLAY"
if [ -d "$ARIZA_ROOT" ]; then
rm -rf "$ARIZA_ROOT"
ariza_ok "removed $ARIZA_ROOT"
else
ariza_log "nothing installed at $ARIZA_ROOT"
fi
ariza_remove_bin_link
ariza_unpersist_path
printf '\n'
ariza_ok "$APP_DISPLAY uninstalled"
printf ' Not touched, because this installer never wrote them:\n'
printf ' %s\n' "$ARIZA_STATE"
printf ' any data or configuration %s created for you\n' "$APP_DISPLAY"
}
main "$@"