modify install script
This commit is contained in:
124
install
124
install
@@ -1,24 +1,35 @@
|
|||||||
#!/usr/bin/env bash
|
#!/bin/bash
|
||||||
|
|
||||||
|
SCRIPT_DIR="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" >/dev/null 2>&1 &&
|
||||||
|
pwd)"
|
||||||
|
PROFILES_DIR="${SCRIPT_DIR}/profiles"
|
||||||
|
STOW_ARGS=(
|
||||||
|
"--no-folding"
|
||||||
|
"--adopt"
|
||||||
|
"--dir" "${PROFILES_DIR}"
|
||||||
|
"--target" "${HOME}"
|
||||||
|
)
|
||||||
|
SCRIPT_NAME="${0##*/}"
|
||||||
|
|
||||||
SCRIPT_DIR=$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" >/dev/null 2>&1 && pwd)
|
|
||||||
readonly SCRIPT_DIR
|
readonly SCRIPT_DIR
|
||||||
readonly PROFILES_DIR=${SCRIPT_DIR}/profiles
|
readonly PROFILES_DIR
|
||||||
readonly -a STOW_ARGS=(--no-folding --adopt --dir "${PROFILES_DIR}" --target "${HOME}")
|
readonly -a STOW_ARGS
|
||||||
readonly SCRIPT_NAME=${0##*/}
|
readonly SCRIPT_NAME
|
||||||
|
|
||||||
print_opt() {
|
print_opt() {
|
||||||
local -r option=$1
|
local -r option="$1"
|
||||||
local -r description=$2
|
local -r description="$2"
|
||||||
printf " %-22s %s\n" "${option}" "${description}"
|
printf " %-22s %s\n" "${option}" "${description}"
|
||||||
}
|
}
|
||||||
|
|
||||||
shell_quote() {
|
shell_quote() {
|
||||||
local -r string=$1
|
local -r string="$1"
|
||||||
printf "'%s'" "${string//'/'\\''}"
|
printf "'%s'" "${string//'/'\\''/}"
|
||||||
}
|
}
|
||||||
|
|
||||||
array_contains() {
|
array_contains() {
|
||||||
local -r target_item=$1; shift
|
local -r target_item="$1"
|
||||||
|
shift
|
||||||
local -a -r array=("$@")
|
local -a -r array=("$@")
|
||||||
local item
|
local item
|
||||||
for item in "${array[@]}"; do
|
for item in "${array[@]}"; do
|
||||||
@@ -30,14 +41,15 @@ array_contains() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
die() {
|
die() {
|
||||||
local -r message=$1
|
local -r message="$1"
|
||||||
printf "%s: %b\n" "${SCRIPT_NAME}" "${message}" >&2
|
printf "%s: %b\n" "${SCRIPT_NAME}" "${message}" >&2
|
||||||
exit 1
|
exit 1
|
||||||
}
|
}
|
||||||
|
|
||||||
invalid_option() {
|
invalid_option() {
|
||||||
local -r option=$1
|
local -r option="$1"
|
||||||
die "invalid option $(shell_quote "${option}")\nTry '${SCRIPT_NAME} --help' for usage."
|
die "invalid option $(shell_quote "${option}")
|
||||||
|
Try '${SCRIPT_NAME} --help' for usage."
|
||||||
}
|
}
|
||||||
|
|
||||||
check_deps() {
|
check_deps() {
|
||||||
@@ -64,7 +76,7 @@ EOF
|
|||||||
}
|
}
|
||||||
|
|
||||||
require_profiles_dir() {
|
require_profiles_dir() {
|
||||||
if [[ ! -d ${PROFILES_DIR} ]]; then
|
if [[ ! -d "${PROFILES_DIR}" ]]; then
|
||||||
cat >&2 <<EOF
|
cat >&2 <<EOF
|
||||||
Error: profiles directory not found: ${PROFILES_DIR}
|
Error: profiles directory not found: ${PROFILES_DIR}
|
||||||
|
|
||||||
@@ -79,7 +91,8 @@ More information about what is profile can be found using '${SCRIPT_NAME} --help
|
|||||||
EOF
|
EOF
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
mapfile -t PROFILES < <(find "${PROFILES_DIR}" -mindepth 1 -maxdepth 1 -type d -printf '%f\n')
|
mapfile -t PROFILES < <(find "${PROFILES_DIR}" -mindepth 1 -maxdepth 1 \
|
||||||
|
-type d -printf '%f\n')
|
||||||
readonly PROFILES
|
readonly PROFILES
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -99,17 +112,26 @@ parse_args() {
|
|||||||
exit 0
|
exit 0
|
||||||
;;
|
;;
|
||||||
--help=*)
|
--help=*)
|
||||||
local -r topic=${1#*=}
|
local -r topic="${1#*=}"
|
||||||
[[ -z ${topic} ]] && show_general_help && exit 0
|
[[ -z ${topic} ]] && show_general_help && exit 0
|
||||||
show_help_topic "${topic}"
|
show_help_topic "${topic}"
|
||||||
exit 0
|
exit 0
|
||||||
;;
|
;;
|
||||||
-l|--list-profiles) list_profiles=true; shift ;;
|
-l | --list-profiles)
|
||||||
-P|--prune-symlinks) prune_symlinks=true; shift ;;
|
list_profiles="true"
|
||||||
-v|--verbose) verbose=true; shift ;;
|
shift
|
||||||
|
;;
|
||||||
|
-P | --prune-symlinks)
|
||||||
|
prune_symlinks="true"
|
||||||
|
shift
|
||||||
|
;;
|
||||||
|
-v | --verbose)
|
||||||
|
verbose="true"
|
||||||
|
shift
|
||||||
|
;;
|
||||||
-p | --profile)
|
-p | --profile)
|
||||||
( (($# < 2)) || [[ -z "$2" ]]) && die "--profile requires an argument"
|
( (($# < 2)) || [[ -z "$2" ]]) && die "--profile requires an argument"
|
||||||
profile=$2
|
profile="$2"
|
||||||
shift 2
|
shift 2
|
||||||
;;
|
;;
|
||||||
*) invalid_option "$1" ;;
|
*) invalid_option "$1" ;;
|
||||||
@@ -118,11 +140,13 @@ parse_args() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
validate_args() {
|
validate_args() {
|
||||||
if [[ ${list_profiles} && ( ${prune_symlinks} || ${verbose} || ${profile} ) ]]; then
|
if [[ -n "${list_profiles}" ]] &&
|
||||||
|
[[ -n "${prune_symlinks}" || -n "${verbose}" || -n "${profile}" ]]; then
|
||||||
die "options '--list-profiles' and others cannot be used together"
|
die "options '--list-profiles' and others cannot be used together"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [[ ${profile} ]] && ! array_contains "${profile}" "${PROFILES[@]}"; then
|
if [[ -n "${profile}" ]] &&
|
||||||
|
! array_contains "${profile}" "${PROFILES[@]}"; then
|
||||||
die "profile $(shell_quote "${profile}") cannot be found"
|
die "profile $(shell_quote "${profile}") cannot be found"
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
@@ -133,11 +157,14 @@ show_general_help() {
|
|||||||
echo " ${SCRIPT_NAME} --help[=TOPIC]"
|
echo " ${SCRIPT_NAME} --help[=TOPIC]"
|
||||||
echo
|
echo
|
||||||
echo "Options:"
|
echo "Options:"
|
||||||
print_opt "-h, --help[=TOPIC]" "show general help or help for a specific topic"
|
print_opt "-h, --help[=TOPIC]" \
|
||||||
|
"show general help or help for a specific topic"
|
||||||
print_opt "-p, --profile PROFILE" "select profile"
|
print_opt "-p, --profile PROFILE" "select profile"
|
||||||
print_opt "-v, --verbose" "increase verbosity"
|
print_opt "-v, --verbose" "increase verbosity"
|
||||||
print_opt "-P, --prune-symlinks" "remove dangling symlinks in home directory"
|
print_opt "-P, --prune-symlinks" \
|
||||||
print_opt "-l, --list-profiles" "list available profiles (cannot be combined with other options)"
|
"remove dangling symlinks in home directory"
|
||||||
|
print_opt "-l, --list-profiles" \
|
||||||
|
"list available profiles (cannot be combined with other options)"
|
||||||
echo
|
echo
|
||||||
echo "Topics:"
|
echo "Topics:"
|
||||||
print_opt "profile" "explanation of profile structure and usage"
|
print_opt "profile" "explanation of profile structure and usage"
|
||||||
@@ -148,12 +175,12 @@ show_general_help() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
show_help_topic() {
|
show_help_topic() {
|
||||||
local topic=$1
|
local topic="$1"
|
||||||
case ${topic} in
|
case "${topic}" in
|
||||||
profile)
|
profile)
|
||||||
cat <<EOF
|
cat <<EOF
|
||||||
A profile is a directory under 'profiles/' that groups dotfiles to be installed
|
A profile is a directory under 'profiles/' that groups dotfiles to be
|
||||||
together using GNU Stow.
|
installed together using GNU Stow.
|
||||||
|
|
||||||
Contents:
|
Contents:
|
||||||
dotfiles/ files or directories to deploy (e.g. .config)
|
dotfiles/ files or directories to deploy (e.g. .config)
|
||||||
@@ -166,7 +193,8 @@ Only executable files inside hooks are executed.
|
|||||||
Profiles may inherit from a parent. The installer builds the dependency
|
Profiles may inherit from a parent. The installer builds the dependency
|
||||||
tree automatically and installs profiles in the correct order.
|
tree automatically and installs profiles in the correct order.
|
||||||
|
|
||||||
Use .stow-local-ignore to prevent linking internal files such as hooks/ and parent.
|
Use .stow-local-ignore to prevent linking internal files such as hooks/
|
||||||
|
and parent.
|
||||||
EOF
|
EOF
|
||||||
;;
|
;;
|
||||||
*) die "unknown help topic: ${topic}" ;;
|
*) die "unknown help topic: ${topic}" ;;
|
||||||
@@ -174,9 +202,9 @@ EOF
|
|||||||
}
|
}
|
||||||
|
|
||||||
run_pre_hooks() {
|
run_pre_hooks() {
|
||||||
local -r target_profile=$1
|
local -r target_profile="$1"
|
||||||
for hook in "${PROFILES_DIR}/${target_profile}"/hooks/pre/*; do
|
for hook in "${PROFILES_DIR}/${target_profile}"/hooks/pre/*; do
|
||||||
if [[ -f ${hook} && -x ${hook} ]]; then
|
if [[ -f "${hook}" && -x "${hook}" ]]; then
|
||||||
echo "Running pre-install hook ${hook##*/}"
|
echo "Running pre-install hook ${hook##*/}"
|
||||||
"${hook}"
|
"${hook}"
|
||||||
fi
|
fi
|
||||||
@@ -184,9 +212,9 @@ run_pre_hooks() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
run_post_hooks() {
|
run_post_hooks() {
|
||||||
local -r target_profile=$1
|
local -r target_profile="$1"
|
||||||
for hook in "${PROFILES_DIR}/${target_profile}"/hooks/post/*; do
|
for hook in "${PROFILES_DIR}/${target_profile}"/hooks/post/*; do
|
||||||
if [[ -f ${hook} && -x ${hook} ]]; then
|
if [[ -f "${hook}" && -x "${hook}" ]]; then
|
||||||
echo "Running post-install hook ${hook##*/}"
|
echo "Running post-install hook ${hook##*/}"
|
||||||
"${hook}"
|
"${hook}"
|
||||||
fi
|
fi
|
||||||
@@ -198,13 +226,14 @@ prune_symlinks() {
|
|||||||
|
|
||||||
pushd "${HOME}" >/dev/null || exit 1
|
pushd "${HOME}" >/dev/null || exit 1
|
||||||
local common_files
|
local common_files
|
||||||
local -r target_profile=$1
|
local -r target_profile="$1"
|
||||||
mapfile -t common_files < <(comm -12 <(find "${HOME}" -type l -printf '%P\n' 2>/dev/null | sort) \
|
mapfile -t common_files < <(comm -12 <(find "${HOME}" -type l \
|
||||||
|
-printf '%P\n' 2>/dev/null | sort) \
|
||||||
<(find "${PROFILES_DIR}/${target_profile}" -type f -printf '%P\n' | sort))
|
<(find "${PROFILES_DIR}/${target_profile}" -type f -printf '%P\n' | sort))
|
||||||
local target_file
|
local target_file
|
||||||
for target_file in "${common_files[@]}"; do
|
for target_file in "${common_files[@]}"; do
|
||||||
local -a rm_args=(--force)
|
local -a rm_args=("--force")
|
||||||
[[ ${verbose} ]] && rm_args+=(--verbose)
|
[[ ${verbose} ]] && rm_args+=("--verbose")
|
||||||
rm "${rm_args[@]}" -- "${target_file}"
|
rm "${rm_args[@]}" -- "${target_file}"
|
||||||
done
|
done
|
||||||
popd >/dev/null || exit 1
|
popd >/dev/null || exit 1
|
||||||
@@ -213,20 +242,21 @@ prune_symlinks() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
stow_profile() {
|
stow_profile() {
|
||||||
local -r target_profile=$1
|
local -r target_profile="$1"
|
||||||
if stow "${STOW_ARGS[@]}" "${target_profile}" 2>&1; then
|
if stow "${STOW_ARGS[@]}" "${target_profile}" 2>&1; then
|
||||||
echo "Profile ${target_profile} has been stowed"
|
echo "Profile ${target_profile} has been stowed"
|
||||||
else
|
else
|
||||||
echo "Failed to create symlinks pointing to dotfiles in profile ${target_profile}"
|
echo "Failed to create symlinks pointing to dotfiles" \
|
||||||
|
"in profile ${target_profile}"
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
fetch_parents() {
|
fetch_parents() {
|
||||||
local -r target_profile=$1
|
local -r target_profile="$1"
|
||||||
if [[ -s ${PROFILES_DIR}/${target_profile}/parent ]]; then
|
if [[ -s "${PROFILES_DIR}/${target_profile}/parent" ]]; then
|
||||||
local parent
|
local parent
|
||||||
parent=$(cat "${PROFILES_DIR}/${target_profile}/parent")
|
parent="$(cat "${PROFILES_DIR}/${target_profile}/parent")"
|
||||||
if [[ -s ${PROFILES_DIR}/${parent}/parent ]]; then
|
if [[ -s "${PROFILES_DIR}/${parent}/parent" ]]; then
|
||||||
fetch_parents "${parent}"
|
fetch_parents "${parent}"
|
||||||
fi
|
fi
|
||||||
echo "${parent}"
|
echo "${parent}"
|
||||||
@@ -234,7 +264,7 @@ fetch_parents() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
build_deptree() {
|
build_deptree() {
|
||||||
local -r target_profile=$1
|
local -r target_profile="$1"
|
||||||
fetch_parents "${target_profile}"
|
fetch_parents "${target_profile}"
|
||||||
echo "${target_profile}"
|
echo "${target_profile}"
|
||||||
}
|
}
|
||||||
@@ -243,7 +273,7 @@ install_dotfiles() {
|
|||||||
local target_profile
|
local target_profile
|
||||||
for target_profile in $(build_deptree "${profile}"); do
|
for target_profile in $(build_deptree "${profile}"); do
|
||||||
run_pre_hooks "${target_profile}"
|
run_pre_hooks "${target_profile}"
|
||||||
[[ ${prune_symlinks} ]] && prune_symlinks "${target_profile}"
|
[[ -n "${prune_symlinks}" ]] && prune_symlinks "${target_profile}"
|
||||||
stow_profile "${target_profile}"
|
stow_profile "${target_profile}"
|
||||||
run_post_hooks "${target_profile}"
|
run_post_hooks "${target_profile}"
|
||||||
done
|
done
|
||||||
@@ -255,7 +285,7 @@ main() {
|
|||||||
require_profiles_dir
|
require_profiles_dir
|
||||||
(($# == 0)) && show_general_help && exit 1
|
(($# == 0)) && show_general_help && exit 1
|
||||||
validate_args
|
validate_args
|
||||||
[[ ${list_profiles} ]] && echo "${PROFILES[@]}" && exit 0
|
[[ -n "${list_profiles}" ]] && echo "${PROFILES[@]}" && exit 0
|
||||||
install_dotfiles
|
install_dotfiles
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user