#!/usr/bin/env bash # # Dotfiles manager and install helper # (c) Ismo Vuorinen 2022 # Licensed under MIT, see LICENSE # # shellcheck source-path=$HOME/.dotfiles/local/bin # # Helper variables, override with ENVs like `VERBOSE=1 dfm help` : "${VERBOSE:=0}" : "${DOTFILES:=$HOME/.dotfiles}" : "${BREWFILE:=$DOTFILES/config/homebrew/Brewfile}" : "${HOSTFILES:=$DOTFILES/hosts}" SCRIPT=$(basename "$0") if ! declare -f msg > /dev/null; then # Function to print messages if VERBOSE is enabled # $1 - message (string) msg() { [ "$VERBOSE" -eq 1 ] && echo "$1" return 0 } fi if ! declare -f msg_err > /dev/null; then # Function to print error messages and exit # $1 - error message (string) msg_err() { echo "(!) ERROR: $1" >&2 exit 1 } fi if ! declare -f msg_done > /dev/null; then # Function to print done message # $1 - message (string) msg_done() { echo "✓ $1" return 0 } fi VERSION_NVM="v0.39.5" # Loads configs for better installation experience source "$DOTFILES/config/shared.sh" source "${DOTFILES}/local/bin/msgr" # -- Menu builder -- # menu_section() { LINE=$(printf '%-18s [ %-15s ]\n' "$1" "$2") echo -e " $(__log_marker) $LINE" } menu_item() { LINE=$(printf '%-15s %-30s\n' "$1" "$2") echo -e "$(__log_indent)$(__log_marker) $LINE" } # Takes a bash array ("cow:moo", "dinosaur:roar") and loops # through the keys to build menu section listing. menu_usage_header() { MENU_CMD="$1" shift MENU_ARRAY=("$@") KEYS="" for item in "${MENU_ARRAY[@]}"; do CMD=$(echo "${item}" | awk -F ":" '{print $1}') KEYS+="${CMD} | " done # "???" removes 3 last characters, being " | " from the end menu_section "$MENU_CMD" "${KEYS%???}" } # Takes the usage command "$0 dotfiles" and a # bash array ("cow:moo" "dinosaur:roar") and loops # through in building a menu for dfm command usage listing. menu_usage() { MENU_CMD="$1" shift MENU_ARRAY=("$@") msg "$MENU_CMD" for item in "${MENU_ARRAY[@]}"; do CMD=$(echo "${item}" | awk -F ":" '{print $1}') DESC=$(echo "${item}" | awk -F ":" '{print $2}') menu_item "$CMD" "$DESC" done } section_install() { USAGE_PREFIX="$SCRIPT install " MENU=( "all:Installs everything in the correct order" "antigen:Updates the antigen.zsh file" "cargo:Install rust/cargo packages" "cheat-databases:Install cheat external cheatsheet databases" "composer:Install composer" "dotenv:Install dotenv-linter" "fonts:Install programming fonts" "gh:Install GitHub CLI Extensions" "go:Install Go Packages" "imagick:Install ImageMagick CLI" "starship:Install starship.rs" "macos:Setup nice macOS defaults" "neofetch:Install neofetch" "nvm:Install Node Version Manager (nvm)" "nvm-latest:Install latest lts node using nvm" "npm:Install NPM Packages" "ntfy:Install ntfy" "ohmybash:Install oh-my-bash" "pip:Install pip/python packages" "z:Install z" ) case "$1" in all) msgr msg "Starting to install all and reloading configurations..." $0 install macos $0 install fonts $0 install antigen $0 brew install $0 install ohmyposh $0 install asdf # $0 install ohmybash # $0 install pip # $0 install cargo $0 install composer # $0 install dotenv $0 install fzf # $0 install gh # $0 install go $0 install cheat-databases $0 install imagick # $0 install neofetch # $0 install nvm # $0 install npm $0 install ntfy $0 install z msgr msg "Reloading configurations again..." source "$DOTFILES/config/shared.sh" msgr yay "All done!" ;; antigen) msg "Installing antigen..." curl -sSfL git.io/antigen -o "$DOTFILES/local/bin/antigen.zsh" \ && msg_yay "New antigen installed!" ;; asdf) msg "Installing asdf..." bash "$DOTFILES/scripts/install-asdf.sh" \ && msg_yay "asdf installed!" ;; cargo) msg "Installing cargo packages..." bash "$DOTFILES/scripts/install-cargo-packages.sh" \ && msg_yay "cargo packages installed!" ;; cheat-databases) msg "Installing cheat databases..." for database in "$DOTFILES"/scripts/install-cheat-*; do bash "$database" \ && msg_yay "Cheat: $database run" done ;; composer) msg "Installing composer..." bash "$DOTFILES/scripts/install-composer.sh" \ && msg_yay "composer installed!" ;; dotenv) msg "Installing dotenv-linter..." curl -sSfL \ https://raw.githubusercontent.com/dotenv-linter/dotenv-linter/master/install.sh \ | sh -s -- -b "$XDG_BIN_HOME" \ && msg_yay "dotenv-linter installed!" ;; fonts) msg "Installing fonts..." bash "$DOTFILES/scripts/install-fonts.sh" \ && msg_yay "Installed fonts!" ;; fzf) msg "Installing fzf..." bash "$DOTFILES/scripts/install-fzf.sh" \ && msg_yay "fzf installed!" ;; gh) msg "Installing GitHub CLI Extensions..." bash "$DOTFILES/scripts/install-gh-extensions.sh" \ && msg_yay "github cli extensions installed!" ;; go) msg "Installing Go Packages..." bash "$DOTFILES/scripts/install-go-packages.sh" \ && msg_yay "go packages installed!" ;; imagick) msg "Installing ImageMagick CLI..." curl -L https://imagemagick.org/archive/binaries/magick > "$XDG_BIN_HOME/magick" \ && chmod +x "$XDG_BIN_HOME/magick" \ && msg_yay "imagick installed!" ;; starship) msg "Installing starship.rs..." curl -sS https://starship.rs/install.sh | sh -s -- \ --bin-dir ~/.local/bin \ --yes \ && msg_yay "starship installed!" ;; macos) msg "Setting up macOS defaults..." bash "$DOTFILES/scripts/set-macos-defaults.sh" \ && msg_yay "Brewfile defined apps has been installed!" ;; neofetch) msg "Installing neofetch..." bash "$DOTFILES/scripts/install-neofetch.sh" \ && msg_yay "neofetch installed!" ;; nvm) msg "Installing nvm..." curl -o- "https://raw.githubusercontent.com/nvm-sh/nvm/$VERSION_NVM/install.sh" | bash git checkout "$DOTFILES/base/zshrc" git checkout "$DOTFILES/base/bashrc" $0 install nvm-latest msg_yay "nvm installed!" ;; nvm-latest) msg "Installing latest lts node..." if [ -n "$NVM_DIR" ]; then [ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh" fi nvm install --lts --latest-npm --default git checkout "$DOTFILES/base/zshrc" git checkout "$DOTFILES/base/bashrc" msg_yay "latest lts node installed!" ;; npm) msg "NPM Packages install started..." bash "$DOTFILES/scripts/install-npm-packages.sh" \ && msg_yay "NPM Packages have been installed!" ;; ntfy) msg "ntfy install started..." bash "$DOTFILES/scripts/install-ntfy.sh" \ && msg_yay "ntfy installed!" ;; ohmybash) msg "oh-my-bash install started..." bash "$DOTFILES/scripts/install-ohmybash.sh" \ && msg_yay "oh-my-bash installed!" ;; ohmyposh) msg "oh-my-posh install started..." bash "$DOTFILES/scripts/install-ohmyposh.sh" \ && msg_yay "oh-my-posh installed!" ;; pip) msg "pip install started..." bash "$DOTFILES/scripts/install-pip-packages.sh" ;; z) msg "Installing z..." bash "$DOTFILES/scripts/install-z.sh" \ && msg_yay "z has been installed!" ;; *) menu_usage "$USAGE_PREFIX" "${MENU[@]}" ;; esac } section_brew() { USAGE_PREFIX="$SCRIPT brew " MENU=( "install:Installs items defined in Brewfile" "update:Updates and upgrades brew packages" "updatebundle:Updates Brewfile with descriptions" "autoupdate:Setups brew auto-update and runs it immediately" "leaves:List brew leaves (installed on request)" "clean:Clean up brew packages" ) x-have brew && { case "$1" in install) brew bundle install --file="$BREWFILE" --force --quiet && msg_yay "Done!" ;; update) brew update && brew outdated && brew upgrade && brew cleanup msg_yay "Done!" ;; updatebundle) # Updates .dotfiles/homebrew/Brewfile with descriptions brew bundle dump \ --force \ --file="$BREWFILE" \ --describe && msg_yay "Done!" ;; leaves) brew leaves --installed-on-request ;; clean) brew bundle cleanup --file="$BREWFILE" && msg_yay "Done!" ;; autoupdate) brew autoupdate delete brew autoupdate start 43200 --upgrade --cleanup --immediate ;; *) menu_usage "$USAGE_PREFIX" "${MENU[@]}" ;; esac } ! x-have brew && menu_section "$USAGE_PREFIX" "brew not available on this system" } section_helpers() { USAGE_PREFIX="$SCRIPT helpers " MENU=( "path:Show \$PATH dir by dir" ) case "$1" in path) # shellcheck disable=2001 for i in $(echo "$PATH" | sed 's/:/ /g'); do echo "$i"; done ;; *) menu_usage "$USAGE_PREFIX" "${MENU[@]}" ;; esac } section_docs() { USAGE_PREFIX="$SCRIPT docs " MENU=( "tmux:Update tmux keybindings documentation" ) case "$1" in tmux) bash "$DOTFILES/local/bin/x-dfm-docs-xterm-keybindings" ;; *) menu_usage "$USAGE_PREFIX" "${MENU[@]}" ;; esac } section_dotfiles() { USAGE_PREFIX="$SCRIPT dotfiles " MENU=( "fmt:Run all formatters" "yamlfmt:Run yamlfmt to all dotfiles, which are in our control" "shfmt:Run shfmt to all dotfiles" "reset_all:Reset everything, runs all configured reset actions" "reset_nvim:Resets nvim. Deletes caches, removes nvim folders and relinks nvim folders" ) case "$1" in fmt) msg_ok "Running all formatters" $0 dotfiles yamlfmt $0 dotfiles shfmt msg_done "...done!" ;; reset_all) msg_ok "Running all reset commands" $0 dotfiles reset_nvim ;; reset_nvim) rm -rf \ ~/.local/share/nvim \ ~/.local/state/nvim \ ~/.cache/nvim \ ~/.config/nvim msg_ok "Deleted old nvim files (share, state and cache + config)" ln -s "$DOTFILES/config/nvim" ~/.config/nvim msg_ok "Linked nvim and astronvim" x-have npm && $0 install npm msg_ok "Installed packages" msg_done "nvim reset!" ;; yamlfmt) # format yaml files x-have yamlfmt && yamlfmt -conf "$DOTFILES/.yamlfmt" ! x-have yamlfmt && msg_err "yamlfmt not found" ;; shfmt) # Format shell scripts according to following rules. fd --full-path "$DOTFILES" -tx \ -E '*.pl' -E '*.php' -E '*.py' -E '*.zsh' -E 'plugins' -E 'fzf' -E 'dotbot' \ -x shfmt \ --language-dialect bash \ --func-next-line --list --write \ --indent 2 --case-indent --space-redirects \ --binary-next-line {} \; msg_yay "dotfiles have been shfmt formatted!" ;; *) menu_usage "$USAGE_PREFIX" "${MENU[@]}" ;; esac } section_check() { USAGE_PREFIX="$SCRIPT check " X_HOSTNAME=$(hostname) X_ARCH=$(uname) MENU=( "arch :Empty returns current. Exit code 0=match to current, 1=no match." "host :Empty returns current. Exit code 0=match to current, 1=no match." ) case "$1" in a | arch) [[ "$2" = "" ]] && echo "$X_ARCH" && exit 0 [[ $X_ARCH = "$2" ]] && exit 0 || exit 1 ;; h | host | hostname) [[ "$2" = "" ]] && echo "$X_HOSTNAME" && exit 0 [[ $X_HOSTNAME = "$2" ]] && exit 0 || exit 1 ;; *) menu_usage "$USAGE_PREFIX" "${MENU[@]}" ;; esac } # Secret menu for visual tests section_tests() { USAGE_PREFIX="$SCRIPT tests " MENU=( "msg:List all log functions from shared.sh" ) case "$1" in msg) msg "msg" msg_done "msg_done" msg_done_suffix "msg_done_suffix" msg_err "msg_err" msg_nested "msg_nested" msg_nested_done "msg_nested_done" msg_ok "msg_ok" msg_prompt "msg_prompt" msg_prompt_done "msg_prompt_done" msg_run "msg_run" "second_param" msg_run_done "msg_run_done" "second_param" msg_warn "msg_warn" msg_yay "msg_yay" msg_yay_done "msg_yay_done" ;; *) menu_usage "$USAGE_PREFIX" "${MENU[@]}" ;; esac } usage() { echo "" msg_prompt "Usage: $SCRIPT
" echo $" Empty prints
help." echo "" section_install echo "" section_brew echo "" section_check echo "" section_dotfiles echo "" section_docs echo "" section_helpers } # The main loop. first keyword after $0 triggers section, or help. case "$1" in install) section_install "$2" ;; brew) section_brew "$2" ;; check) section_check "$2" ;; dotfiles) section_dotfiles "$2" ;; helpers) section_helpers "$2" ;; docs) section_docs "$2" ;; tests) section_tests "$2" ;; *) usage && exit 0 ;; esac