227 lines
5.3 KiB
Bash
Executable File
227 lines
5.3 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
|
|
set -ueo pipefail
|
|
shopt -s nullglob
|
|
|
|
declare -r TARGET_PATH="$HOME"
|
|
|
|
|
|
_detect_current_script_real_directory() {
|
|
realpath -e -- "$(dirname -- "$(readlink -e -- "${BASH_SOURCE[0]:-$0}")")"
|
|
}
|
|
|
|
declare DOTFILES_ROOT
|
|
DOTFILES_ROOT="$(_detect_current_script_real_directory)"
|
|
readonly DOTFILES_ROOT
|
|
|
|
declare -xr SUB="${DOTFILES_ROOT}/home/user"
|
|
|
|
source "${DOTFILES_ROOT}/TARGETS.sh"
|
|
|
|
|
|
_die() {
|
|
echo "${0}: ${1}" >&2
|
|
exit $2
|
|
}
|
|
|
|
_link_files_in_sandbox() {
|
|
local targetfile
|
|
for targetfile in "$@"
|
|
do
|
|
echo "installing: ${targetfile}"
|
|
if [[ "${targetfile::1}" = "%" ]]; then
|
|
_link_files_in_sandbox ${TARGETS["${targetfile:1}"]}
|
|
else
|
|
if [[ ! "$(dirname "$targetfile")" = "." ]]; then
|
|
mkdir -p "${SANDBOX_PATH}/$(dirname "$targetfile")"
|
|
fi
|
|
ln -sT "${SUB}/${targetfile}" "${SANDBOX_PATH}/${targetfile}"
|
|
fi
|
|
done
|
|
}
|
|
|
|
_compare_sandbox_to_home() {
|
|
local comparisons
|
|
comparisons="$(diff -rq "$SANDBOX_PATH" "$TARGET_PATH")"
|
|
echo "$comparisons" | grep -vE "^Only in .+" || true
|
|
}
|
|
|
|
_merge_sandbox_to_home() {
|
|
cp -RTnP "$SANDBOX_PATH" "$TARGET_PATH" || true
|
|
}
|
|
|
|
__install_from_sandbox() {
|
|
local comparisons
|
|
comparisons="$(_compare_sandbox_to_home)"
|
|
|
|
if [[ -n "$comparisons" ]]; then
|
|
echo "$comparisons" >&2
|
|
_die "Found conflicting files. Exiting" 1
|
|
fi
|
|
|
|
echo "Merging to home..."
|
|
_merge_sandbox_to_home
|
|
echo "Successfully installed"
|
|
}
|
|
|
|
_execute_hook_if_executable() {
|
|
# all hooks gets SUB and SANDBOX_PATH env variables
|
|
local -r target="$1"
|
|
local -r hook_name="$2"
|
|
|
|
local hook_path="${DOTFILES_ROOT}/install-hooks/${target}/${hook_name}"
|
|
if [[ -x "$hook_path" ]]; then
|
|
echo "Executing ${hook_name} for target '${target}'"
|
|
"$hook_path"
|
|
fi
|
|
}
|
|
|
|
execute_pre_hook() {
|
|
_execute_hook_if_executable "$1" "pre-install"
|
|
}
|
|
|
|
recursive_execute_pre_hooks() {
|
|
local targetfile
|
|
for targetfile in ${TARGETS["$1"]}
|
|
do
|
|
if [[ "${targetfile::1}" = "%" ]]; then
|
|
recursive_execute_pre_hooks "${targetfile:1}"
|
|
execute_pre_hook "${targetfile:1}"
|
|
fi
|
|
done
|
|
}
|
|
|
|
execute_post_hook() {
|
|
_execute_hook_if_executable "$1" "post-install"
|
|
}
|
|
|
|
recursive_execute_post_hooks() {
|
|
local targetfile
|
|
for targetfile in ${TARGETS["$1"]}
|
|
do
|
|
if [[ "${targetfile::1}" = "%" ]]; then
|
|
recursive_execute_post_hooks "${targetfile:1}"
|
|
execute_post_hook "${targetfile:1}"
|
|
fi
|
|
done
|
|
}
|
|
|
|
install_target() {
|
|
local -r target="$1"
|
|
|
|
execute_pre_hook "$target"
|
|
recursive_execute_pre_hooks "$target"
|
|
_link_files_in_sandbox ${TARGETS["$target"]}
|
|
__install_from_sandbox
|
|
recursive_execute_post_hooks "$target"
|
|
execute_post_hook "$target"
|
|
}
|
|
|
|
is_target_installed() {
|
|
local not_fully_installed=false
|
|
|
|
local targetfile
|
|
for targetfile in ${TARGETS["$1"]}
|
|
do
|
|
if [[ "${targetfile::1}" = "%" ]]; then
|
|
is_target_installed "${targetfile:1}" || not_fully_installed=true
|
|
else
|
|
if [[ ! -e "$TARGET_PATH/$targetfile" ]]; then
|
|
echo "${targetfile} not linked"
|
|
not_fully_installed=true
|
|
fi
|
|
fi
|
|
done
|
|
if $not_fully_installed; then
|
|
echo "Target '${1}' not fully installed"
|
|
echo
|
|
return 1
|
|
fi
|
|
return 0
|
|
}
|
|
|
|
find_targets_that_depend_on() {
|
|
local target
|
|
for target in "${!TARGETS[@]}"
|
|
do
|
|
if [[ " ${TARGETS["$target"]} " =~ " %${1} " ]]; then
|
|
echo "$target"
|
|
fi
|
|
done
|
|
}
|
|
|
|
die_if_installed_targets_depend_on() {
|
|
for reverse_dependecy in $(find_targets_that_depend_on "$1")
|
|
do
|
|
if is_target_installed "$reverse_dependecy" >/dev/null; then
|
|
_die "target '${reverse_dependecy}' is depends on installed target '${1}'. Exiting..." 1
|
|
fi
|
|
done
|
|
}
|
|
|
|
cmd_unlink() {
|
|
local target targetfile
|
|
for target in "$@"
|
|
do
|
|
die_if_installed_targets_depend_on "$target"
|
|
|
|
for targetfile in ${TARGETS["$target"]}
|
|
do
|
|
if [[ "${targetfile::1}" = "%" ]]; then
|
|
continue
|
|
fi
|
|
|
|
if [[ -e "${TARGET_PATH}/${targetfile}" ]]; then
|
|
unlink "${TARGET_PATH}/${targetfile}"
|
|
fi
|
|
done
|
|
done
|
|
}
|
|
|
|
cmd_no_target() {
|
|
_die "TARGET not exists" 1
|
|
}
|
|
|
|
cmd_list() {
|
|
echo "${!TARGETS[@]}"
|
|
}
|
|
|
|
target_exists() {
|
|
local -r target="$1"
|
|
[[ " ${!TARGETS[*]} " =~ " ${target} " ]]
|
|
}
|
|
|
|
cmd_install() {
|
|
local target
|
|
for target in "$@"
|
|
do
|
|
if target_exists "$target"; then
|
|
SANDBOX_PATH="$(mktemp -td "${USER:-user}.dotfiles_XXXXXXX")"
|
|
export SANDBOX_PATH
|
|
install_target "$target"
|
|
rm -rf "$SANDBOX_PATH"
|
|
else
|
|
cmd_no_target
|
|
fi
|
|
done
|
|
}
|
|
|
|
cmd_help() {
|
|
echo "Dotfiles installation script:
|
|
Usage: ./install TARGET...
|
|
Usage: ./install unlink TARGET...
|
|
Usage: ./install check TARGET
|
|
Usage: ./install list"
|
|
}
|
|
|
|
unset executed_command
|
|
readonly executed_command="$1"
|
|
|
|
case "$executed_command" in
|
|
unlink) shift; cmd_unlink "$@" ;;
|
|
check) shift; is_target_installed "$@" ;;
|
|
list) shift; cmd_list "$@" ;;
|
|
help) shift; cmd_help "$@" ;;
|
|
*) shift; cmd_install "$executed_command" "$@" ;;
|
|
esac
|
|
exit 0 |