#!/usr/bin/env bash set -eo pipefail DEPS_PATH="${HOME}/.local/share" SCRIPT_DIR="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" &>/dev/null && pwd)" PKG_INSTALL_CMD="" BOLD=$(tput bold) CYAN=$(tput setaf 6) GREEN=$(tput setaf 2) YELLOW=$(tput setaf 3) RED=$(tput setaf 1) RESET=$(tput sgr0) RESET_BOLD="${RESET}${BOLD}" log() { # Print a message and send it to stdout or stderr depending upon log level, also configurable with debug etc. # # Arguments: # level # - The log level, defined within a case check in this function # message # - The info message # # Usage: # log "info" "Could not find that directory" # # POSIX Compliant: # Yes # # Convert the level to uppercase local level level=$(printf "%s" "${1}" | tr '[:lower:]' '[:upper:]') local message message="${2}" case "${level}" in INFO) printf "%sINFO:%s %s%s%s\n" \ "${CYAN}" \ "${RESET}" \ "${BOLD}" \ "${message}" \ "${RESET}" >&2 return 0 ;; WARN*) printf "%sWARN:%s %s%s%s\n" \ "${YELLOW}" \ "${RESET}" \ "${BOLD}" \ "${message}" \ "${RESET}" >&2 return 0 ;; ERROR) printf "%sERROR:%s %s%s%s\n" \ "${RED}" \ "${RESET}" \ "${BOLD}" \ "${message}" \ "${RESET}" >&2 return 0 ;; # Further log levels can be added by extending this switch statement with more comparisons esac } print-break() { printf "${GREEN}%.s─${RESET}" $(seq 1 "$(tput cols)") } log "${@}" install-fzf() { local install_path="${1}/fzf" if ! [[ -e "${install_path}" ]]; then git clone --depth 1 https://github.com/junegunn/fzf.git "${install_path}" "${install_path}/install" \ --key-bindings \ --completion \ --no-update-rc \ --xdg >/dev/null else log "info" "${GREEN}FZF${RESET_BOLD} already installed, skipping" fi } install-rust() { local install_path="${1}" export CARGO_HOME="${install_path}/cargo" export RUSTUP_HOME="${install_path}/rustup" if ! [[ -e "${CARGO_HOME}" ]] || ! [[ -e "${RUSTUP_HOME}" ]]; then log "info" "Rust installs set to ${GREEN}cargo${RESET_BOLD}: ${GREEN}${CARGO_HOME}${RESET_BOLD} & ${GREEN}rustup${RESET_BOLD}: ${GREEN}${RUSTUP_HOME}${RESET_BOLD}" curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --quiet else log "info" "${GREEN}Rust${RESET_BOLD} already installed, skipping" fi if [[ -z "$(ls "${RUSTUP_HOME}/toolchains" >/dev/null)" ]]; then log "info" "No toolchain found, installing a ${GREEN}Rust${RESET_BOLD} toolchain" log "info" "Setting ${GREEN}Rust${RESET_BOLD}'s toolchain to ${GREEN}stable${RESET_BOLD}" if ! "${CARGO_HOME}/bin/rustup" default stable; then log "error" "Failed to set a ${GREEN}Rust${RESET_BOLD} toolchain" return 1 fi else log "info" "${GREEN}Rust${RESET_BOLD} toolchain found, skipping toolchain setup" fi export PATH="${PATH}:${install_path}/cargo/bin" } install-omz() { local install_path="${1}/omz" if ! [[ -e "${install_path}" ]]; then git clone https://github.com/ohmyzsh/ohmyzsh.git "${install_path}" else log "info" "${GREEN}oh-my-zsh${RESET_BOLD} already installed, skipping" fi } install-cargo-binary() { local binary="${1}" cargo install "${binary}" } install-autojump() { local install_path="${1}/autojump" cd "$(mktemp -d)" git clone "https://github.com/wting/autojump.git" "autojump" || return 1 ( cd "autojump" python3 "install.py" -d "${install_path}" ) || return 1 } check-script-deps() { local commands_to_check=( git gcc make python3 ) local ret_code=0 for cmd in "${commands_to_check[@]}"; do if ! command -v "${cmd}" >/dev/null; then log "error" "Could not find command: ${GREEN}${cmd}${RESET_BOLD}, ensure you install this dependency" ret_code=1 else log "info" "Found dependency ${GREEN}${cmd}${RESET_BOLD}" fi done return "${ret_code}" } install-from-pkg-mngr() { if [[ "${SKIP_PKG_INSTALL}" = true ]]; then return fi local pkg_to_install="${*}" local ret_code=0 log "info" "Installing ${GREEN}${pkg_to_install}${RESET_BOLD}" if ! "$(eval "${PKG_INSTALL_CMD}" "${pkg_to_install}")"; then log "error" "Failed to install ${GREEN}${pkg_to_install}${RESET_BOLD}" ret_code=1 fi if ((ret_code > 0)); then return 1 else log "info" "Successfully installed ${GREEN}${pkg_to_install}${RESET_BOLD}" fi } install-pyenv() { local install_path="${1}/pyenv" if ! [[ -e "${install_path}" ]]; then git clone https://github.com/pyenv/pyenv.git "${install_path}" || return 1 ( cd "${install_path}" src/configure && make -C src ) || return 1 else log "info" "${GREEN}Pyenv${RESET_BOLD} already installed, skipping" fi } create-dirs() { local dirs=( "${HOME}/.local/bin" "${HOME}/.local/share" ) ret_code=0 for dir in "${dirs[@]}"; do log "info" "Creating directory ${GREEN}${dir}${RESET_BOLD}" if ! mkdir -p "${dir}"; then log "error" "Failed to create directory: ${GREEN}${dir}${RESET_BOLD}" ret_code=1 fi done return "${ret_code}" } deploy-config() { local install_paths declare -A install_paths=( ["zshrc"]=".zshrc" ["ZSH-Config"]=".config/zsh" ) for install_key in "${!install_paths[@]}"; do local src_path="${SCRIPT_DIR}/${install_paths[${install_key}]}" local dest_path="${HOME}/${install_paths[${install_key}]}" if [[ -e "${dest_path}" ]]; then log "info" "${GREEN}${install_key}${RESET_BOLD} already exists at ${GREEN}${dest_path}${RESET_BOLD}, skipping" continue fi log "info" "Linking ${GREEN}${install_key}${RESET_BOLD}, ${GREEN}${src_path}${RESET_BOLD} to ${GREEN}${dest_path}${RESET_BOLD}" if ! ln -s "${src_path}" "${dest_path}"; then log "error" "Failed linking ${GREEN}${install_key}${RESET_BOLD}, ${GREEN}${src_path}${RESET_BOLD} to ${GREEN}${dest_path}${RESET_BOLD}" return 1 fi done } main() { local tasks_done=() PKG_INSTALL_CMD="${*}" if [[ -z "${PKG_INSTALL_CMD}" ]] && ! [[ "${SKIP_PKG_INSTALL}" = true ]]; then log "error" "A package installer must be passed to install missing packages, or ${GREEN}SKIP_PKG_INSTALL=true${RESET_BOLD} must be set." return 1 fi log "info" "Dependencies directory set to ${GREEN}${DEPS_PATH}${RESET_BOLD}" log "info" "Packager install command set to ${GREEN}${PKG_INSTALL_CMD}${RESET_BOLD}" mkdir -p "${DEPS_PATH}" print-break log "info" "Checking script dependencies..." if ! check-script-deps; then log "error" "Script dependencies failed, install missing dependencies and try again" exit 1 fi log "info" "Script dependencies good" print-break log "info" "Creating directories" if ! create-dirs; then log "error" "Unable to create required directories" exit 1 fi log "info" "Finished creating directories" print-break log "info" "Linking ${GREEN}python${RESET_BOLD} to ${GREEN}python3${RESET_BOLD}" ln -s "$(command -v python3)" "${HOME}/.local/bin/python" export PATH="${PATH}:${HOME}/.local/bin/" log "info" "Finished linking ${GREEN}python${RESET_BOLD} to ${GREEN}python3${RESET_BOLD}" log "info" "Installing ${GREEN}FZF${RESET_BOLD}" if ! install-fzf "${DEPS_PATH}"; then log "error" "Failed to install ${GREEN}FZF${RESET_BOLD}" exit 1 fi log "info" "Successfully installed ${GREEN}FZF${RESET_BOLD}" tasks_done+=("Install FZF") print-break log "info" "Installing Rust" if ! install-rust "${DEPS_PATH}"; then log "error" "Failed to install ${GREEN}Rust${RESET_BOLD}" exit 1 fi log "info" "Successfully installed ${GREEN}Rust${RESET_BOLD}" tasks_done+=("Install Rust") print-break log "info" "Installing Cargo programs" local cargo_binaries=( ripgrep exa fd-find bat ) for pkg in "${cargo_binaries[@]}"; do log "info" "Attempting install of ${GREEN}${pkg}${RESET_BOLD}" if ! install-cargo-binary "${pkg}"; then log "error" "Failed installation of ${GREEN}${pkg}${RESET_BOLD}" exit 1 fi done log "info" "Finished installing Cargo programs" print-break log "info" "Installing ${GREEN}Pyenv${RESET_BOLD}" if ! install-pyenv "${DEPS_PATH}"; then log "error" "Failed to install ${GREEN}Pyenv${RESET_BOLD}" exit 1 fi log "info" "Finished installing ${GREEN}Pyenv${RESET_BOLD}" print-break log "info" "Installing ${GREEN}autojump${RESET_BOLD}" if ! install-autojump "${DEPS_PATH}"; then log "error" "Failed to install ${GREEN}autojump${RESET_BOLD}" exit 1 fi log "info" "Finished installing ${GREEN}autojump${RESET_BOLD}" log "info" "Install ${GREEN}Oh My ZSH${RESET_BOLD}" install-omz "${DEPS_PATH}" log "info" "Finished installing ${GREEN}Oh My ZSH${RESET_BOLD}" print-break log "info" "Installing ${GREEN}ZSH${RESET_BOLD}" if ! command -v "zsh" >/dev/null; then if ! install-from-pkg-mngr "zsh"; then log "error" "Failed to install ${GREEN}ZSH${RESET_BOLD}" exit 1 fi else log "info" "${GREEN}ZSH${RESET_BOLD} already installed, skipping" fi log "info" "Successfully installed ${GREEN}ZSH${RESET_BOLD}" log "info" "Deploying ${GREEN}ZSH${RESET_BOLD} configuration files" if ! deploy-config; then log "error" "Failed to deploy configuration files" exit 1 fi log "info" "Successfully installed ${GREEN}ZSH${RESET_BOLD} configuration files" print-break log "info" "Finished installing ${GREEN}ZSH${RESET_BOLD} configurations and dependencies" } main "${@}"