This commit is contained in:
+127
-347
@@ -1,375 +1,155 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
############################ SETUP PARAMETERS
|
||||
set -Eeuo pipefail
|
||||
|
||||
app_name='dotfiles'
|
||||
readonly APP_NAME='dotfiles'
|
||||
readonly REPO_URI="${REPO_URI:-https://github.com/Donaldttt/dotfiles.git}"
|
||||
readonly REPO_BRANCH="${REPO_BRANCH:-main}"
|
||||
|
||||
[ -z "$APP_PATH" ] && APP_PATH="$HOME/.$app_name"
|
||||
[ -z "$REPO_URI" ] && REPO_URI="https://github.com/Donaldttt/$app_name"
|
||||
[ -z "$REPO_BRANCH" ] && REPO_BRANCH='main'
|
||||
# [ -z "$REPO_URI" ] && REPO_URI="https://gitlab.com/tangnachuan1208/$app_name"
|
||||
# [ -z "$REPO_BRANCH" ] && REPO_BRANCH='gitlab'
|
||||
[ -z "$VIM_RTPATH" ] && VIM_RTPATH="$HOME/.vim"
|
||||
SCRIPT_DIR="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" 2>/dev/null && pwd -P || true)"
|
||||
if [[ -n "$SCRIPT_DIR" && -e "$SCRIPT_DIR/.git" ]]; then
|
||||
APP_PATH="${APP_PATH:-$SCRIPT_DIR}"
|
||||
else
|
||||
APP_PATH="${APP_PATH:-$HOME/.$APP_NAME}"
|
||||
fi
|
||||
readonly APP_PATH
|
||||
readonly VIM_PATH="${VIM_PATH:-$HOME/.vim}"
|
||||
readonly MANAGED_START='# >>> dotfiles >>>'
|
||||
readonly MANAGED_END='# <<< dotfiles <<<'
|
||||
|
||||
info() { printf '\033[34m[•]\033[0m %s\n' "$*"; }
|
||||
success() { printf '\033[32m[✔]\033[0m %s\n' "$*"; }
|
||||
warn() { printf '\033[33m[!]\033[0m %s\n' "$*" >&2; }
|
||||
die() { printf '\033[31m[✘]\033[0m %s\n' "$*" >&2; exit 1; }
|
||||
|
||||
############################ BASIC SETUP TOOLS
|
||||
msg() {
|
||||
printf '%b\n' "$1" >&2
|
||||
usage() {
|
||||
cat <<'EOF'
|
||||
Usage: ./install.sh [all|vim|shell|tmux]...
|
||||
|
||||
With no arguments, installs every component. Components can be combined, for
|
||||
example: ./install.sh vim tmux
|
||||
EOF
|
||||
}
|
||||
|
||||
success() {
|
||||
msg "\33[32m[✔]\33[0m ${1}${2}"
|
||||
require() {
|
||||
command -v "$1" >/dev/null 2>&1 || die "Required program not found: $1"
|
||||
}
|
||||
|
||||
fail() {
|
||||
msg "\33[31m[✘]\33[0m ${1}${2}"
|
||||
ensure_repo() {
|
||||
require git
|
||||
if [[ -d "$APP_PATH/.git" ]]; then
|
||||
return
|
||||
fi
|
||||
[[ ! -e "$APP_PATH" ]] || die "$APP_PATH exists but is not a Git repository."
|
||||
info "Cloning $REPO_URI into $APP_PATH"
|
||||
git clone --branch "$REPO_BRANCH" --depth 1 "$REPO_URI" "$APP_PATH"
|
||||
}
|
||||
|
||||
fatal_error() {
|
||||
msg "\33[31m[✘]\33[0m ${1}${2}"
|
||||
exit 1
|
||||
backup_path() {
|
||||
local target=$1
|
||||
if [[ -e "$target" || -L "$target" ]]; then
|
||||
local backup="${target}.backup.$(date +%Y%m%d_%H%M%S)" suffix=0
|
||||
while [[ -e "$backup" || -L "$backup" ]]; do
|
||||
((suffix += 1))
|
||||
backup="${target}.backup.$(date +%Y%m%d_%H%M%S).$suffix"
|
||||
done
|
||||
mv -- "$target" "$backup"
|
||||
warn "Backed up $target to $backup"
|
||||
fi
|
||||
}
|
||||
|
||||
program_exists() {
|
||||
local ret='0'
|
||||
command -v $1 >/dev/null 2>&1 || { local ret='1'; }
|
||||
|
||||
# fail on non-zero return value
|
||||
if [ "$ret" -ne 0 ]; then
|
||||
return 1
|
||||
fi
|
||||
return 0
|
||||
link_config() {
|
||||
local source=$1 target=$2 current=''
|
||||
if [[ -L "$target" ]]; then
|
||||
current=$(readlink "$target" || true)
|
||||
[[ "$current" == "$source" ]] && return
|
||||
fi
|
||||
backup_path "$target"
|
||||
ln -s "$source" "$target"
|
||||
success "Linked $target -> $source"
|
||||
}
|
||||
|
||||
# check if file $2 contain string $1
|
||||
# return 0 if exists
|
||||
# 1 not contain string
|
||||
# 2 file not exist
|
||||
# when using this function:
|
||||
# file path should not be quoted but like this:
|
||||
# file_contains 'abc' file_path
|
||||
file_contains(){
|
||||
if [ -f "$2" ]; then
|
||||
a=`grep "$1" $2`
|
||||
[ "$a" != '' ] && return 0
|
||||
return 1
|
||||
fi
|
||||
return 2
|
||||
remove_managed_block() {
|
||||
local file=$1 temp
|
||||
[[ -f "$file" ]] || return
|
||||
temp=$(mktemp)
|
||||
awk -v start="$MANAGED_START" -v end="$MANAGED_END" '
|
||||
$0 == start { managed = 1; next }
|
||||
$0 == end { managed = 0; next }
|
||||
!managed { print }
|
||||
' "$file" > "$temp"
|
||||
cat "$temp" > "$file"
|
||||
rm -f "$temp"
|
||||
}
|
||||
|
||||
program_must_exist() {
|
||||
program_exists $1
|
||||
install_vim() {
|
||||
require vim
|
||||
require curl
|
||||
if ! vim --clean -Nu NONE -n -es \
|
||||
'+if v:version < 900 | cquit | endif' '+qall'; then
|
||||
die 'Vim 9 or newer is required.'
|
||||
fi
|
||||
|
||||
# throw error on non-zero return value
|
||||
if [ "$?" -ne 0 ]; then
|
||||
fatal_error "You must have '$1' installed to continue."
|
||||
fi
|
||||
link_config "$APP_PATH/.vimrc" "$HOME/.vimrc"
|
||||
mkdir -p "$VIM_PATH/autoload"
|
||||
local temp
|
||||
temp=$(mktemp)
|
||||
curl --fail --location --silent --show-error \
|
||||
https://raw.githubusercontent.com/junegunn/vim-plug/master/plug.vim \
|
||||
--output "$temp"
|
||||
mv "$temp" "$VIM_PATH/autoload/plug.vim"
|
||||
vim -Nu "$APP_PATH/.vimrc" -n -es \
|
||||
'+set nomore' '+PlugInstall --sync' '+qall'
|
||||
success 'Installed Vim configuration and plugins.'
|
||||
}
|
||||
|
||||
env_set() {
|
||||
if [ -z "$1" ]; then
|
||||
fatal_error "You must have your $1 environmental variable set to continue."
|
||||
exit 1
|
||||
fi
|
||||
install_shell() {
|
||||
local bashrc="$HOME/.bashrc" quoted_source
|
||||
touch "$bashrc"
|
||||
remove_managed_block "$bashrc"
|
||||
printf -v quoted_source '%q' "$APP_PATH/bash_tools.sh"
|
||||
{
|
||||
printf '\n%s\n' "$MANAGED_START"
|
||||
printf '[ -r %s ] && source %s\n' "$quoted_source" "$quoted_source"
|
||||
printf 'command -v zoxide >/dev/null 2>&1 && eval "$(zoxide init bash)"\n'
|
||||
printf '%s\n' "$MANAGED_END"
|
||||
} >> "$bashrc"
|
||||
success "Installed managed Bash block in $bashrc"
|
||||
}
|
||||
|
||||
try_symlink() {
|
||||
local source_path="$1"
|
||||
local symlink_path="$2"
|
||||
if [ -e "$source_path" ]; then
|
||||
ln -sf "$source_path" "$symlink_path"
|
||||
msg "Create symlink $symlink_path points to $source_path"
|
||||
return 0
|
||||
fi
|
||||
return 1
|
||||
install_tmux() {
|
||||
require tmux
|
||||
link_config "$APP_PATH/.tmux.conf" "$HOME/.tmux.conf"
|
||||
local tpm="$HOME/.tmux/plugins/tpm"
|
||||
if [[ ! -d "$tpm/.git" ]]; then
|
||||
[[ ! -e "$tpm" ]] || die "$tpm exists but is not a Git repository."
|
||||
mkdir -p "$(dirname "$tpm")"
|
||||
git clone --depth 1 https://github.com/tmux-plugins/tpm "$tpm"
|
||||
fi
|
||||
success 'Installed tmux configuration and TPM.'
|
||||
}
|
||||
|
||||
############################ SETUP FUNCTIONS
|
||||
main() {
|
||||
case "${1:-}" in
|
||||
-h|--help) usage; return ;;
|
||||
esac
|
||||
|
||||
backup_file() {
|
||||
local original_file=$1
|
||||
if [ -f "$original_file" ] && [ ! -L "$original_file" ]; then
|
||||
today=`date +%Y%m%d_%s`
|
||||
mv -v "$original_file" "$original_file.$today";
|
||||
success "Your file $original_file has been backed up as $original_file.$today"
|
||||
fi
|
||||
ensure_repo
|
||||
local components=("$@") component
|
||||
((${#components[@]})) || components=(all)
|
||||
for component in "${components[@]}"; do
|
||||
case "$component" in
|
||||
all)
|
||||
install_vim
|
||||
install_shell
|
||||
install_tmux
|
||||
;;
|
||||
vim) install_vim ;;
|
||||
shell) install_shell ;;
|
||||
tmux) install_tmux ;;
|
||||
*) usage; die "Unknown component: $component" ;;
|
||||
esac
|
||||
done
|
||||
}
|
||||
|
||||
# Usage: sync_repo path url
|
||||
#
|
||||
# This function try to clone git repo host in @url
|
||||
# to @path. If @path alreay exsits, it will simply
|
||||
# update the repo
|
||||
sync_repo() {
|
||||
local repo_path="$1"
|
||||
local repo_uri="$2"
|
||||
local repo_branch="$3"
|
||||
local repo_name="$4"
|
||||
|
||||
if [ ! -e "$repo_path" ]; then
|
||||
msg "Trying to clone $repo_name"
|
||||
mkdir -p "$repo_path"
|
||||
git clone -b "$repo_branch" "$repo_uri" "$repo_path"
|
||||
ret="$?"
|
||||
success "Successfully cloned $repo_name."
|
||||
else
|
||||
msg "Trying to update $repo_name"
|
||||
cd "$repo_path" && git pull origin "$repo_branch"
|
||||
ret="$?"
|
||||
success "Successfully updated $repo_name"
|
||||
fi
|
||||
}
|
||||
|
||||
# simply clone a repo
|
||||
sync_repo_default() {
|
||||
local repo_uri="$1"
|
||||
local repo_path="$2"
|
||||
|
||||
if [ ! -e "$repo_path" ]; then
|
||||
msg "Trying to clone repo"
|
||||
mkdir -p "$repo_path"
|
||||
git clone "$repo_uri" "$repo_path"
|
||||
ret="$?"
|
||||
success "Successfully cloned repo."
|
||||
else
|
||||
msg "Trying to update repo"
|
||||
cd "$repo_path" && git pull
|
||||
ret="$?"
|
||||
success "Successfully updated repo"
|
||||
fi
|
||||
}
|
||||
|
||||
setup_vimplug() {
|
||||
|
||||
# if vimplug is already installed
|
||||
# comment out since this can update vimplug
|
||||
#if [ -e "~/.vim/autoload/plug.vim" ]; then
|
||||
# return 0
|
||||
#fi
|
||||
|
||||
mkdir -p "$VIM_RTPATH/autoload"
|
||||
# setup for vim
|
||||
curl -fLo ~/.vim/autoload/plug.vim --create-dirs \
|
||||
https://raw.githubusercontent.com/junegunn/vim-plug/master/plug.vim > /dev/null 2>&1
|
||||
|
||||
local system_shell="$SHELL"
|
||||
export SHELL='/bin/sh'
|
||||
|
||||
vim \
|
||||
"+set nomore" \
|
||||
"+PlugInstall!" \
|
||||
"+qall"
|
||||
|
||||
export SHELL="$system_shell"
|
||||
success "Plugins' updated/installed using vim-plug \n(Be careful some plugins may not be installed since they require to use neovim or higher vim version. To intall them, run :PlugInstall inside correct version)"
|
||||
}
|
||||
|
||||
set_up_nvim() {
|
||||
if program_exists "nvim"; then
|
||||
nvim_dir="$HOME/.config/nvim"
|
||||
nvim_config="$nvim_dir/init.vim"
|
||||
mkdir -p $nvim_dir
|
||||
msg "Found neovim."
|
||||
if [ -f "$nvim_config" ]; then
|
||||
msg "Found init.vim. backing up it"
|
||||
mv $nvim_config "${nvim_config}.backup"
|
||||
fi
|
||||
echo "set runtimepath^=~/.vim runtimepath+=~/.vim/after
|
||||
let &packpath=&runtimepath
|
||||
source ~/.vimrc" > $nvim_config
|
||||
success "Neovim config setup successed"
|
||||
else
|
||||
fail "Neovim config is not setup(neovim not installed)"
|
||||
fi
|
||||
}
|
||||
|
||||
# tmux plugin manager
|
||||
set_up_tpm() {
|
||||
|
||||
echo "Install TPM(tmux plugin manager)?('y' install; 'other keys' nope)"
|
||||
read -r respond
|
||||
if [ "$respond" = "y" ] || [ "$respond" = "yes" ]; then
|
||||
local url="https://github.com/tmux-plugins/tpm"
|
||||
sync_repo_default "$url" "$HOME/.tmux/plugins/tpm"
|
||||
success "TPM installed"
|
||||
else
|
||||
fail "TPM not installed"
|
||||
fi
|
||||
}
|
||||
|
||||
set_up_tmux() {
|
||||
if program_exists "tmux"; then
|
||||
backup_file "$HOME/.tmux.conf"
|
||||
try_symlink "$APP_PATH/.tmux.conf" "$HOME/.tmux.conf" && \
|
||||
success "Tmux config setup successed"
|
||||
set_up_tpm
|
||||
else
|
||||
fail "Tmux config is not setup(tmux not installed)"
|
||||
fi
|
||||
}
|
||||
|
||||
set_up_vim() {
|
||||
if ! program_exists "vim" && ! program_exists "nvim" ; then
|
||||
fatal_error "You must have 'vim' or 'nvim(neovim)' installed to continue."
|
||||
fi
|
||||
program_must_exist "git"
|
||||
program_must_exist "curl"
|
||||
|
||||
sync_repo "$APP_PATH" \
|
||||
"$REPO_URI" \
|
||||
"$REPO_BRANCH" \
|
||||
"$app_name"
|
||||
|
||||
backup_file "$HOME/.vimrc"
|
||||
backup_file "$HOME/.vimrc.bundles"
|
||||
|
||||
try_symlink "$APP_PATH/.vimrc" "$HOME/.vimrc"
|
||||
try_symlink "$APP_PATH/.vimrc.bundles" "$HOME/.vimrc.bundles"
|
||||
|
||||
success "Setting up symlinks for vim configuration"
|
||||
set_up_nvim
|
||||
setup_vimplug
|
||||
}
|
||||
|
||||
# zoxide requres extra shell configuration setup
|
||||
set_up_zoxide_shell_config() {
|
||||
local source_cmd_zsh='eval "$(zoxide init zsh)"'
|
||||
local source_cmd_bash='eval "$(zoxide init bash)"'
|
||||
if program_exists "zsh"; then
|
||||
# if not already contain source command
|
||||
file_contains "$source_cmd_zsh" ~/.zshrc
|
||||
status_code=$?
|
||||
if [ "$status_code" != '0' ]; then
|
||||
echo "Install zoxide source command ($source_cmd_zsh) for zsh shell?('y' install; others no)"
|
||||
read -r respond
|
||||
if [ "$respond" = "y" ] || [ "$respond" = "yes" ]; then
|
||||
echo "$source_cmd_zsh" >> $HOME/.zshrc
|
||||
success "add ($source_cmd_zsh) to zsh"
|
||||
else
|
||||
fail "not adding ($source_cmd_zsh) to zsh"
|
||||
fi
|
||||
else
|
||||
success "($source_cmd_zsh) is already in zsh"
|
||||
fi
|
||||
fi
|
||||
|
||||
if program_exists "bash"; then
|
||||
# if not already contain source command
|
||||
file_contains "$source_cmd_bash" ~/.bashrc
|
||||
status_code=$?
|
||||
if [ "$status_code" != '0' ]; then
|
||||
echo "Install zoxide source command ($source_cmd_bash) for bash shell?('y' install; others no)"
|
||||
read -r respond
|
||||
if [ "$respond" = "y" ] || [ "$respond" = "yes" ]; then
|
||||
echo "$source_cmd_bash" >> $HOME/.bashrc
|
||||
success "add ($source_cmd_bash) to bash"
|
||||
else
|
||||
fail "not adding ($source_cmd_bash) to bash"
|
||||
fi
|
||||
else
|
||||
success "($source_cmd_bash) is already in bash"
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
set_up_zoxide() {
|
||||
# https://github.com/ajeetdsouza/zoxide
|
||||
|
||||
if program_exists "zoxide"; then
|
||||
success "zoxide is already installed"
|
||||
set_up_zoxide_shell_config
|
||||
return 0
|
||||
fi
|
||||
|
||||
echo "Install zoxide (smart cd like autojump)?('y' install; 'other keys' nope)"
|
||||
read -r respond
|
||||
if [ "$respond" = "y" ] || [ "$respond" = "yes" ]; then
|
||||
program_must_exist "curl"
|
||||
# This will install zoxide into ~/.local
|
||||
curl -sS https://raw.githubusercontent.com/ajeetdsouza/zoxide/main/install.sh | bash
|
||||
set_up_zoxide_shell_config
|
||||
|
||||
else
|
||||
fail "zoxide is not installed"
|
||||
fi
|
||||
}
|
||||
set_up_fish(){
|
||||
local source_cmd="source $APP_PATH/config/fish/config.fish"
|
||||
local fish_config="$HOME/.config/fish/config.fish"
|
||||
if program_exists "fish"; then
|
||||
# if not already contain source command
|
||||
file_contains "$source_cmd" $fish_config
|
||||
status_code=$?
|
||||
if [ "$status_code" != '0' ]; then
|
||||
echo "Install fish configuration script for fish shell?('y' install; 'other keys' nope)"
|
||||
read -r respond
|
||||
if [ "$respond" = "y" ] || [ "$respond" = "yes" ]; then
|
||||
echo "$source_cmd" >> $fish_config
|
||||
success "fish configuration is sourced in $fish_config"
|
||||
else
|
||||
fail "Configuration is not sourced in fish"
|
||||
fi
|
||||
else
|
||||
success "fish script is already sourced"
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
set_up_bash_tool(){
|
||||
local source_cmd="source $APP_PATH/bash_tools.sh"
|
||||
local source_cmd2="source ~/.$app_name/bash_tools.sh"
|
||||
if program_exists "zsh"; then
|
||||
# if not already contain source command
|
||||
file_contains "$source_cmd" ~/.zshrc
|
||||
status_code=$?
|
||||
if [ "$status_code" != '0' ]; then
|
||||
file_contains "$source_cmd2" ~/.zshrc
|
||||
status_code=$?
|
||||
fi
|
||||
if [ "$status_code" != '0' ]; then
|
||||
echo "Install terminal configuration script for zsh shell?('y' install; 'other keys' nope)"
|
||||
read -r respond
|
||||
if [ "$respond" = "y" ] || [ "$respond" = "yes" ]; then
|
||||
echo "$source_cmd" >> $HOME/.zshrc
|
||||
success "Terminal configuration is sourced in zsh"
|
||||
else
|
||||
fail "Configuration is not sourced in zsh"
|
||||
fi
|
||||
else
|
||||
success "zsh script is already sourced"
|
||||
fi
|
||||
fi
|
||||
|
||||
if program_exists "bash"; then
|
||||
# if not already contain source command
|
||||
file_contains "$source_cmd" ~/.bashrc
|
||||
status_code=$?
|
||||
if [ "$status_code" != '0' ]; then
|
||||
file_contains "$source_cmd2" ~/.bashrc
|
||||
status_code=$?
|
||||
fi
|
||||
if [ "$status_code" != '0' ]; then
|
||||
echo "Install terminal configuration script for bash shell?('y' install; 'other keys' nope)"
|
||||
read -r respond
|
||||
if [ "$respond" = "y" ] || [ "$respond" = "yes" ]; then
|
||||
echo "$source_cmd" >> $HOME/.bashrc
|
||||
success "Terminal configuration is sourced in bash"
|
||||
else
|
||||
fail "Configuration is not sourced in bash"
|
||||
fi
|
||||
else
|
||||
success "bash script is already sourced"
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
############################ MAIN()
|
||||
env_set "$HOME"
|
||||
|
||||
set_up_vim
|
||||
set_up_tmux
|
||||
set_up_bash_tool
|
||||
set_up_fish
|
||||
set_up_zoxide
|
||||
|
||||
msg "\nThanks for installing $app_name."
|
||||
main "$@"
|
||||
|
||||
Reference in New Issue
Block a user