diff --git a/.gitignore b/.gitignore index 6d3855c..c75a5f9 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1 @@ -dotfiles/.vim/bundle/ -dotfiles/.vim/.netrwhist -dotfiles/.vim/viminfo +install/config diff --git a/bash/bash_profile b/bash/bash_profile index bead643..9584a81 100644 --- a/bash/bash_profile +++ b/bash/bash_profile @@ -1,8 +1,8 @@ #!/bin/bash # Called by bash (1) on for login shells. # Source: -# 1. profile if present (XDG_CONFIG_HOME is not defined yet) +# 1. copy environment from zsh # 2. ~/.bashrc if shell is interactive. -[ -f "${HOME}/.config/shell/profile" ] && source "${HOME}/.config/shell/profile" -[[ "$-" == *i* ]] && [ -f "${HOME}/.bashrc" ] && source "${HOME}/.bashrc" +[ -f "$ZDOTDIR/zshenv" ] && source "$ZDOTDIR/zshenv" +[[ "$-" == *i* ]] && [ -f "$HOME/.bashrc" ] && source "$HOME/.bashrc" diff --git a/bash/bashrc b/bash/bashrc index d3f10e4..9b4b71d 100644 --- a/bash/bashrc +++ b/bash/bashrc @@ -2,8 +2,8 @@ # Set up bash for interactive use (options, prompt, aliases, etc.) while read -r f; do [ -f "$f" ] && source "$f"; done < $target" - $DRY_RUN || rm -f "$1" - fi - ;; - esac - fi -} - -file_link() { # Make sure $1 is a link to $2. - target="$(realpath -s "$2")" - if [ "$(readlink "$1")" = "$target" ]; then - info "OK: $1" - else - warn "linking $1 -> $target" - $DRY_RUN || ln -sf "$target" "$1" - fi -} - -file_content() { # Ensure $1 and $2 contents are equal, updating $1 if needed. - if diff "$1" "$2" >/dev/null 2>&1; then - info "OK: $1" - else - warn "overwriting $1 with $2:" - cat "$2" - $DRY_RUN || cp -f "$2" "$1" - fi -} - -task_gather_user_info() { - heading "Gather user information" - printf 'git name [%s]: ' "$DEFAULT_GIT_USER" - read -r git_user - printf 'git e-mail [%s]: ' "$DEFAULT_GIT_EMAIL" - read -r git_email -} - -task_remove_bash_profile() { - heading "Remove pre-installed bash profile" - for f in .bash_logout .profile; do - file_absent "$HOME/$f" - done -} - -task_ensure_directories() { - heading "Ensure existence of directories used by dotfiles" - echo "$DIRS" | while read -r d; do directory_present "$d"; done -} - -task_link_dotfiles() { - heading "Link dotfiles" - echo "$DOTFILES" | while read -r src dest; do - file_link "$src" "$dest" - done -} - -task_deploy_templates() { - heading "Deploy templates" - temp_git="$(mktemp)" - cat >"$temp_git" </dev/null 2>&1; then - warn "Installing neovim plugins" - $DRY_RUN || nvim -nes -u "$XDG_CONFIG_HOME/nvim/init.vim" -c 'PlugInstall | qall!' - else - error "neovim is not installed; skipping plugin installation..." - fi -} - -main() { - $DRY_RUN && warn "Dry run: no changes will actually be made." - task_gather_user_info - task_remove_bash_profile - task_ensure_directories - task_link_dotfiles - task_deploy_templates - task_prune_broken_bin - task_link_local_bin - task_make_logins_silent - task_install_nvim_plugins -} - -main - diff --git a/git/gitconfig b/git/gitconfig index baf0e6a..c06678c 100644 --- a/git/gitconfig +++ b/git/gitconfig @@ -75,5 +75,5 @@ required = true [include] - path = ~/.local/etc/git/config.host # host-specific (from template) + path = ~/.local/etc/git/config.user # user name & e-mail (from template) path = ~/.local/etc/git/config # optional manual configurations diff --git a/install.sh b/install.sh new file mode 100755 index 0000000..1061b48 --- /dev/null +++ b/install.sh @@ -0,0 +1,37 @@ +#!/bin/sh +set -eu + +export DOTFILES="$(dirname "$(realpath "$0")")" + +. "$DOTFILES/zsh/zshenv" +. "$DOTFILES/install/functions" + +DRY_RUN=yes +while getopts 'fh' opt; do case "$opt" in + f) DRY_RUN= ;; + h) usage; exit 0;; + *) usage; exit 1;; +esac done + +export DEFAULT_GIT_USER="Fernando Schauenburg" +export DEFAULT_GIT_EMAIL="fernando@schauenburg.me" + +# Oportunity to set GIT_USER and GIT_EMAIL +[ -f "$DOTFILES/install/config" ] && . "$DOTFILES/install/config" + +greeting +deploy_alacritty +deploy_bash +deploy_bin +deploy_git +deploy_jupyter +deploy_mintty +deploy_misc +deploy_nvim +deploy_python +deploy_readline +deploy_ssh +deploy_tmux +deploy_x11 +deploy_zsh + diff --git a/install/functions b/install/functions new file mode 100644 index 0000000..b00fc62 --- /dev/null +++ b/install/functions @@ -0,0 +1,246 @@ +#!/bin/sh + +dry_run() { [ -n "$DRY_RUN" ]; } + +usage() { + echo "Usage: $(basename $0) [-h] [-f]" + echo "" + echo " -h print this help and exit" + echo " -f modify filesystem rather than dry run" +} + +############################################################################### +# Helper Functions +############################################################################### + +if [ -t 1 ]; then + rst=$(tput sgr0) + red=$(tput setaf 1) + green=$(tput setaf 2) + yellow=$(tput setaf 3) + blue=$(tput setaf 4) + magenta=$(tput setaf 5) + cyan=$(tput setaf 6) +else + rst='' + red='' + green='' + yellow='' + blue='' + magenta='' + cyan='' +fi + +heading() { printf '%s\n' "${blue}===== $1 ==========${rst}"; } +info() { printf '%s ' "$@"; printf '\n'; } +ok() { printf '%s ' "${green}OK:${rst}" "$@"; printf '\n'; } +warn() { printf '%s ' "${yellow}$@${rst}"; printf '\n'; } +error() { printf '%s ' "${red}$@${rst}"; printf '\n'; } + +is_installed() { + command -v nvim >/dev/null 2>&1 +} + +greeting() { + dry_run && info "Dry run: no changes will be made to filesytem (use -f to override)." + info "Deploying with git user $yellow${GIT_USER:-$DEFAULT_GIT_USER} <${GIT_EMAIL:-$DEFAULT_GIT_EMAIL}>$rst" + [ -t 0 ] && { + info "Press ENTER to continue (CTRL-C to cancel)..." + read k + } +} + +# Make sure directory $1 exists. +ensure_directory() { + if [ -d "$1" ]; then + ok "$1/" + else + warn "creating $1/" + dry_run || mkdir -p "$1/" + fi +} + +# Make sure file $1 does not exist. +remove_file() { + if [ ! -f "$1" ]; then + ok "$1" + else + warn "removing $1" + dry_run || rm "$1" + fi +} + +# Make sure file $1 exists (content is irrelevant). +touch_file() { + if [ -f "$1" ]; then + ok "$1" + else + warn "creating $1" + dry_run || touch "$1" + fi +} + +# Remove $1 if it's a broken link to a dotfile script. +prune_cmd() { + if [ -h "$1" ]; then # it's a symbolic link... + target="$(readlink "$1")" + case "$target" in + "$DOTFILES/bin/"*) # ... pointing into dotfiles bin + if [ ! -e "$1" ]; then # target of the link missing + warn "removing stale link $1 -> $target" + dry_run || rm -f "$1" + fi + ;; + esac + fi +} + +# Make sure $2 is a link to $1. +link() { + target="$(realpath -s "$1")" + if [ "$(readlink "$2")" = "$target" ]; then + ok "$2" + else + warn "linking $2 -> $target" + dry_run || ln -sf "$target" "$2" + fi +} + +# Ensure $1 and $2 contents are equal, updating $1 if needed. +equal_content() { + if diff "$1" "$2" >/dev/null 2>&1; then + ok "$1" + else + warn "overwriting $1 with $2:" + cat "$2" + dry_run || cp -f "$2" "$1" + fi +} + +############################################################################### +# Configuration Deployments +############################################################################### + +deploy_alacritty() { + heading 'alacritty' + ensure_directory "$XDG_CONFIG_HOME/alacritty" + link "$DOTFILES/alacritty/alacritty.yml" "$XDG_CONFIG_HOME/alacritty/alacritty.yml" +} + +deploy_bash() { + heading 'bash' + for f in .bash_logout .profile; do remove_file "$HOME/$f"; done + ensure_directory "$XDG_DATA_HOME/bash" # for history + ensure_directory "$XDG_DATA_HOME/bash-completion/completions" + link "$DOTFILES/bash/bashrc" "${HOME}/.bashrc" + link "$DOTFILES/bash/bash_profile" "${HOME}/.bash_profile" +} + +deploy_bin() { + heading 'bin' + ensure_directory "$HOME/.local/bin" + for cmd in $HOME/.local/bin/*; do prune_cmd "$cmd"; done + for cmd in "$DOTFILES/bin/"*; do + link "$cmd" "$HOME/.local/bin/$(basename "$cmd")" + done +} + +deploy_git() { + heading 'git' + ensure_directory "$XDG_CONFIG_HOME/git" + ensure_directory "${HOME}/.local/etc/git" + link "$DOTFILES/git/gitconfig" "$XDG_CONFIG_HOME/git/config" + link "$DOTFILES/git/gitignore" "$XDG_CONFIG_HOME/git/ignore" + + temp_git="$(mktemp)" + cat >"$temp_git" <