259 lines
6.2 KiB
Bash
Executable File
259 lines
6.2 KiB
Bash
Executable File
#!/bin/sh
|
|
|
|
set -ue
|
|
|
|
readonly TARGET_PATH="${HOME}"
|
|
|
|
|
|
_detect_current_script_real_directory() {
|
|
realpath -e -- "$(dirname -- "$(readlink -e -- "${0}")")"
|
|
}
|
|
|
|
DOTFILES_ROOT="$(_detect_current_script_real_directory)"
|
|
readonly DOTFILES_ROOT
|
|
|
|
readonly SUB="${DOTFILES_ROOT}/home/user"
|
|
export SUB
|
|
|
|
. "${DOTFILES_ROOT}/TARGETS.sh"
|
|
|
|
|
|
_die() {
|
|
printf '%s: %s\n' "$(basename "${0}")" "${1}" >&2
|
|
exit "${2}"
|
|
}
|
|
|
|
string_get_first_char() (
|
|
printf '%.1s' "${1}"
|
|
)
|
|
|
|
string_exclude_first_char() (
|
|
echo "${1}" | tail -c+2
|
|
)
|
|
|
|
map_get_value() (
|
|
map="${1}"
|
|
key="${2}"
|
|
|
|
echo "${map}" | grep "^${key}:" | cut -d ':' -f2
|
|
)
|
|
|
|
map_get_keys() (
|
|
map="${1}"
|
|
|
|
echo "${map}" | cut -d ':' -f1
|
|
)
|
|
|
|
map_key_exists() (
|
|
map="${1}"
|
|
key="${2}"
|
|
|
|
map_get_keys "${map}" | grep "^${2}" 1>/dev/null
|
|
)
|
|
|
|
_link_files_in_sandbox() (
|
|
for targetfile in "$@"
|
|
do
|
|
printf 'installing: %s\n' "${targetfile}"
|
|
if [ "$(string_get_first_char "${targetfile}")" = "%" ]; then
|
|
files="$(map_get_value "${TARGETS}" "$(string_exclude_first_char "${targetfile}")")"
|
|
#shellcheck disable=SC2086
|
|
_link_files_in_sandbox ${files}
|
|
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() (
|
|
comparisons="$(diff -rq "${SANDBOX_PATH}" "${TARGET_PATH}")" || true
|
|
echo "${comparisons}" | grep -vE "^Only in .+" || true
|
|
)
|
|
|
|
_merge_sandbox_to_home() (
|
|
cp -RTnP "${SANDBOX_PATH}" "${TARGET_PATH}" || true
|
|
)
|
|
|
|
__install_from_sandbox() (
|
|
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
|
|
target="${1}"
|
|
hook_name="${2}"
|
|
|
|
hook_path="${DOTFILES_ROOT}/install-hooks/${target}/${hook_name}"
|
|
if [ -x "${hook_path}" ]; then
|
|
printf 'Executing %s for target "%s"\n' "${hook_name}" "${target}"
|
|
"${hook_path}"
|
|
fi
|
|
)
|
|
|
|
execute_pre_hook() (
|
|
_execute_hook_if_executable "${1}" "pre-install"
|
|
)
|
|
|
|
recursive_execute_pre_hooks() (
|
|
files="$(map_get_value "${TARGETS}" "${1}")"
|
|
|
|
for targetfile in ${files}
|
|
do
|
|
if [ "$(string_get_first_char "${targetfile}")" = "%" ]; then
|
|
recursive_execute_pre_hooks "$(string_exclude_first_char "${targetfile}")"
|
|
execute_pre_hook "$(string_exclude_first_char "${targetfile}")"
|
|
fi
|
|
done
|
|
)
|
|
|
|
execute_post_hook() (
|
|
_execute_hook_if_executable "${1}" "post-install"
|
|
)
|
|
|
|
recursive_execute_post_hooks() (
|
|
files="$(map_get_value "${TARGETS}" "${1}")"
|
|
|
|
for targetfile in ${files}
|
|
do
|
|
if [ "$(string_get_first_char "${targetfile}")" = "%" ]; then
|
|
recursive_execute_post_hooks "$(string_exclude_first_char "${targetfile}")"
|
|
execute_post_hook "$(string_exclude_first_char "${targetfile}")"
|
|
fi
|
|
done
|
|
)
|
|
|
|
install_target() (
|
|
target="${1}"
|
|
|
|
files="$(map_get_value "${TARGETS}" "${target}")"
|
|
|
|
execute_pre_hook "${target}"
|
|
recursive_execute_pre_hooks "${target}"
|
|
#shellcheck disable=SC2086
|
|
_link_files_in_sandbox ${files}
|
|
__install_from_sandbox
|
|
recursive_execute_post_hooks "${target}"
|
|
execute_post_hook "${target}"
|
|
)
|
|
|
|
is_target_installed() (
|
|
target="${1}"
|
|
not_fully_installed=false
|
|
|
|
files="$(map_get_value "${TARGETS}" "${target}")"
|
|
|
|
for targetfile in ${files}
|
|
do
|
|
if [ "$(string_get_first_char "${targetfile}")" = "%" ]; then
|
|
is_target_installed "$(string_exclude_first_char "${targetfile}")" || not_fully_installed=true
|
|
else
|
|
if [ ! -e "${TARGET_PATH}/${targetfile}" ]; then
|
|
printf '%s not linked\n' "${targetfile}"
|
|
not_fully_installed=true
|
|
fi
|
|
fi
|
|
done
|
|
if ${not_fully_installed}; then
|
|
printf 'Target "%s" not fully installed\n\n' "${target}"
|
|
return 1
|
|
fi
|
|
return 0
|
|
)
|
|
|
|
find_targets_that_depend_on() (
|
|
for target in $(map_get_keys "${TARGETS}")
|
|
do
|
|
files="$(map_get_value "${TARGETS}" "${target}")"
|
|
|
|
if map_key_exists "${TARGETS}" "%${1}"; then
|
|
printf '%s\n' "${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() (
|
|
for target in "$@"
|
|
do
|
|
die_if_installed_targets_depend_on "${target}"
|
|
|
|
files="$(map_get_value "${TARGETS}" "${target}")"
|
|
for targetfile in ${files}
|
|
do
|
|
if [ "$(string_get_first_char "${targetfile}")" = "%" ]; then
|
|
continue
|
|
fi
|
|
|
|
if [ -e "${TARGET_PATH}/${targetfile}" ]; then
|
|
unlink "${TARGET_PATH}/${targetfile}"
|
|
fi
|
|
done
|
|
done
|
|
)
|
|
|
|
cmd_no_target() (
|
|
_die "TARGET '${1}' not exists" 1
|
|
)
|
|
|
|
cmd_list() (
|
|
map_get_keys "${TARGETS}"
|
|
)
|
|
|
|
target_exists() (
|
|
target="${1}"
|
|
map_key_exists "${TARGETS}" "${target}"
|
|
)
|
|
|
|
cmd_install() (
|
|
for target in "$@"
|
|
do
|
|
if ! target_exists "${target}"; then
|
|
cmd_no_target "${target}"
|
|
fi
|
|
|
|
SANDBOX_PATH="$(mktemp -td "${USER:-user}.dotfiles_XXXXXXX")"
|
|
export SANDBOX_PATH
|
|
install_target "${target}"
|
|
rm -rf "${SANDBOX_PATH}"
|
|
done
|
|
)
|
|
|
|
cmd_help() (
|
|
echo "Dotfiles installation script:
|
|
Usage: ./install TARGET...
|
|
Usage: ./install unlink TARGET...
|
|
Usage: ./install check TARGET
|
|
Usage: ./install list"
|
|
)
|
|
|
|
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
|