#!/usr/bin/env bash set -euo pipefail if ${X_MODE:-false}; then set -x fi # ===================== # paths # ===================== export CARGO_DEFAULT_BIN="${HOME}/.cargo/bin" export BIN_DIR="${BIN_DIR:-"$CARGO_DEFAULT_BIN"}" # ===================== # logging # ===================== echoerr() { echo "$@" 1>&2 } tap() { local -r x="$(cat)" echoerr "$x" echo "$x" } log::ansi() { local bg=false case "$@" in *reset*) echo "\e[0m"; return 0 ;; *black*) color=30 ;; *red*) color=31 ;; *green*) color=32 ;; *yellow*) color=33 ;; *blue*) color=34 ;; *purple*) color=35 ;; *cyan*) color=36 ;; *white*) color=37 ;; esac case "$@" in *regular*) mod=0 ;; *bold*) mod=1 ;; *underline*) mod=4 ;; esac case "$@" in *background*) bg=true ;; *bg*) bg=true ;; esac if $bg; then echo "\e[${color}m" else echo "\e[${mod:-0};${color}m" fi } _log() { local template="$1" shift echoerr "$(printf "$template" "$@")" } _header() { local TOTAL_CHARS=60 local total=$TOTAL_CHARS-2 local size=${#1} local left=$((($total - $size) / 2)) local right=$(($total - $size - $left)) printf "%${left}s" '' | tr ' ' = printf " $1 " printf "%${right}s" '' | tr ' ' = } log::header() { _log "\n$(log::ansi bold)$(log::ansi purple)$(_header "$1")$(log::ansi reset)\n"; } log::success() { _log "$(log::ansi green)✔ %s$(log::ansi reset)\n" "$@"; } log::error() { _log "$(log::ansi red)✖ %s$(log::ansi reset)\n" "$@"; } log::warning() { _log "$(log::ansi yellow)➜ %s$(log::ansi reset)\n" "$@"; } log::note() { _log "$(log::ansi blue)%s$(log::ansi reset)\n" "$@"; } # TODO: remove header() { echoerr "$*" echoerr } die() { log::error "$@" exit 42 } no_binary_warning() { log::note "There's no precompiled binary for your platform: $(uname -a)" } installation_finish_instructions() { local -r shell="$(get_shell)" log::note -e "Finished. To call navi, restart your shell or reload the config file:\n source ~/.${shell}rc" local code if [[ "$shell" == "zsh" ]]; then code="navi widget ${shell} | source" else code='source <(navi widget '"$shell"')' fi log::note -e "\nTo add the Ctrl-G keybinding, add the following to ~/.${shell}rc:\n ${code}" } # ===================== # security # ===================== sha256() { if command_exists sha256sum; then sha256sum elif command_exists shasum; then shasum -a 256 elif command_exists openssl; then openssl dgst -sha256 else log::note "Unable to calculate sha256!" exit 43 fi } # ===================== # github # ===================== latest_version_released() { curl -s 'https://api.github.com/repos/denisidoro/navi/releases/latest' \ | grep -Eo 'releases/tag/v([0-9\.]+)' \ | sed 's|releases/tag/v||' } asset_url() { local -r version="$1" local -r variant="${2:-}" if [[ -n "$variant" ]]; then echo "https://github.com/denisidoro/navi/releases/download/v${version}/navi-v${version}-${variant}.tar.gz" else echo "https://github.com/denisidoro/navi/archive/v${version}.tar.gz" fi } download_asset() { local -r tmp_dir="$(mktemp -d -t navi-install-XXXX)" local -r url="$(asset_url "$@")" log::note "Downloading ${url}..." cd "$tmp_dir" curl -L "$url" -o navi.tar.gz tar xvzf navi.tar.gz mkdir -p "${BIN_DIR}" &>/dev/null || true mv "./navi" "${BIN_DIR}/navi" } sha_for_asset_on_github() { local -r url="$(asset_url "$@")" curl -sL "$url" | sha256 | awk '{print $1}' } error_installing() { log::error "Unable to install navi. Please check https://github.com/denisidoro/navi for alternative installation instructions" exit 33 } # ===================== # code # ===================== version_from_toml() { cat "${NAVI_HOME}/Cargo.toml" \ | grep version \ | head -n1 \ | awk '{print $NF}' \ | tr -d '"' \ | tr -d "'" } # ===================== # platform # ===================== command_exists() { type "$1" &>/dev/null } get_target() { local -r unamea="$(uname -a)" local -r archi="$(uname -sm)" local target case "$unamea $archi" in *arwin*) target="x86_64-apple-darwin" ;; *inux*x86*) target="x86_64-unknown-linux-musl" ;; *ndroid*aarch*|*ndroid*arm*) target="aarch64-linux-android" ;; *inux*aarch*|*inux*arm*) target="armv7-unknown-linux-musleabihf" ;; *) target="" ;; esac echo "$target" } get_shell() { echo $SHELL | xargs basename } # ===================== # main # ===================== export_path_cmd() { echo echo ' export PATH="${PATH}:'"$1"'"' } append_to_file() { local -r path="$1" local -r text="$2" if [ -f "$path" ]; then echo "$text" >> "$path" fi } get_navi_bin_path() { local file="${BIN_DIR}/navi" if [ -f "$file" ]; then echo "$file" return 0 fi file="${CARGO_DEFAULT_BIN}/navi" if [ -f "$file" ]; then echo "$file" return 0 fi } install_navi() { local -r target="$(get_target)" if command_exists navi; then log::success "navi is already installed" exit 0 elif command_exists brew; then brew install navi elif command_exists cargo; then cargo install navi elif [[ -n "$target" ]]; then local -r version="$(latest_version_released)" download_asset "$version" "$target" || error_installing else error_installing fi hash -r 2>/dev/null || true local navi_bin_path="$(which navi || get_navi_bin_path)" ln -s "$navi_bin_path" "${BIN_DIR}/navi" &>/dev/null || true if [ -f "${BIN_DIR}/navi" ]; then navi_bin_path="${BIN_DIR}/navi" fi local -r navi_bin_dir="$(dirname "$navi_bin_path")" echoerr log::success "Finished" log::success "navi is now available at ${navi_bin_path}" echoerr if echo "$PATH" | grep -q "$navi_bin_path"; then : else local -r cmd="$(export_path_cmd "$navi_bin_path")" append_to_file "${HOME}/.bashrc" "$cmd" append_to_file "${ZDOTDIR:-"$HOME"}/.zshrc" "$cmd" append_to_file "${HOME}/.fishrc" "$cmd" fi log::note "To call navi, restart your shell or reload your .bashrc-like config file" echo log::note "Check https://github.com/denisidoro/navi for more info" export PATH="${PATH}:${navi_bin_path}" return 0 } (return 0 2>/dev/null) || install_navi "$@"