App-Ariza.git | resources/templates/ | installer-common-posix.sh


###############################################################################
# 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
}