From 63cdf2685ea74400bfbf7bb3ab5643713f540dc3 Mon Sep 17 00:00:00 2001 From: whiteman808 Date: Sat, 21 Feb 2026 06:00:18 +0100 Subject: [PATCH] modify install script --- install | 344 ++++++++++++++++++++++++++++++-------------------------- 1 file changed, 187 insertions(+), 157 deletions(-) diff --git a/install b/install index 2b86d0b..2add000 100755 --- a/install +++ b/install @@ -1,48 +1,60 @@ -#!/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 PROFILES_DIR=${SCRIPT_DIR}/profiles -readonly -a STOW_ARGS=(--no-folding --adopt --dir "${PROFILES_DIR}" --target "${HOME}") -readonly SCRIPT_NAME=${0##*/} +readonly PROFILES_DIR +readonly -a STOW_ARGS +readonly SCRIPT_NAME print_opt() { - local -r option=$1 - local -r description=$2 - printf " %-22s %s\n" "${option}" "${description}" + local -r option="$1" + local -r description="$2" + printf " %-22s %s\n" "${option}" "${description}" } shell_quote() { - local -r string=$1 - printf "'%s'" "${string//'/'\\''}" + local -r string="$1" + printf "'%s'" "${string//'/'\\''/}" } array_contains() { - local -r target_item=$1; shift - local -a -r array=("$@") - local item - for item in "${array[@]}"; do - if [[ ${item} == "${target_item}" ]]; then - return 0 - fi - done - return 1 + local -r target_item="$1" + shift + local -a -r array=("$@") + local item + for item in "${array[@]}"; do + if [[ ${item} == "${target_item}" ]]; then + return 0 + fi + done + return 1 } die() { - local -r message=$1 - printf "%s: %b\n" "${SCRIPT_NAME}" "${message}" >&2 - exit 1 + local -r message="$1" + printf "%s: %b\n" "${SCRIPT_NAME}" "${message}" >&2 + exit 1 } invalid_option() { - local -r option=$1 - die "invalid option $(shell_quote "${option}")\nTry '${SCRIPT_NAME} --help' for usage." + local -r option="$1" + die "invalid option $(shell_quote "${option}") +Try '${SCRIPT_NAME} --help' for usage." } check_deps() { - if ! command -v stow >/dev/null 2>&1; then - cat >&2 </dev/null 2>&1; then + cat >&2 <&2 <&2 < 0 )); do - case $1 in - -h) - show_general_help - exit 0 - ;; - --help) - if (( $# >= 2 )) && [[ $2 ]] && [[ $2 != -* ]]; then - show_help_topic "$2" - else - show_general_help - fi - exit 0 - ;; - --help=*) - local -r topic=${1#*=} - [[ -z ${topic} ]] && show_general_help && exit 0 - show_help_topic "${topic}" - exit 0 - ;; - -l|--list-profiles) list_profiles=true; shift ;; - -P|--prune-symlinks) prune_symlinks=true; shift ;; - -v|--verbose) verbose=true; shift ;; - -p|--profile) - ( (( $# < 2 )) || [[ -z "$2" ]] ) && die "--profile requires an argument" - profile=$2 - shift 2 - ;; - *) invalid_option "$1" ;; - esac - done + while (($# > 0)); do + case $1 in + -h) + show_general_help + exit 0 + ;; + --help) + if (($# >= 2)) && [[ $2 ]] && [[ $2 != -* ]]; then + show_help_topic "$2" + else + show_general_help + fi + exit 0 + ;; + --help=*) + local -r topic="${1#*=}" + [[ -z ${topic} ]] && show_general_help && exit 0 + show_help_topic "${topic}" + exit 0 + ;; + -l | --list-profiles) + list_profiles="true" + shift + ;; + -P | --prune-symlinks) + prune_symlinks="true" + shift + ;; + -v | --verbose) + verbose="true" + shift + ;; + -p | --profile) + ( (($# < 2)) || [[ -z "$2" ]]) && die "--profile requires an argument" + profile="$2" + shift 2 + ;; + *) invalid_option "$1" ;; + esac + done } validate_args() { - if [[ ${list_profiles} && ( ${prune_symlinks} || ${verbose} || ${profile} ) ]]; then - die "options '--list-profiles' and others cannot be used together" - fi + if [[ -n "${list_profiles}" ]] && + [[ -n "${prune_symlinks}" || -n "${verbose}" || -n "${profile}" ]]; then + die "options '--list-profiles' and others cannot be used together" + fi - if [[ ${profile} ]] && ! array_contains "${profile}" "${PROFILES[@]}"; then - die "profile $(shell_quote "${profile}") cannot be found" - fi + if [[ -n "${profile}" ]] && + ! array_contains "${profile}" "${PROFILES[@]}"; then + die "profile $(shell_quote "${profile}") cannot be found" + fi } show_general_help() { - echo "Usage: ${SCRIPT_NAME} [options]" - echo " ${SCRIPT_NAME} -l | --list-profiles" - echo " ${SCRIPT_NAME} --help[=TOPIC]" - echo - echo "Options:" - print_opt "-h, --help[=TOPIC]" "show general help or help for a specific topic" - print_opt "-p, --profile PROFILE" "select profile" - print_opt "-v, --verbose" "increase verbosity" - print_opt "-P, --prune-symlinks" "remove dangling symlinks in home directory" - print_opt "-l, --list-profiles" "list available profiles (cannot be combined with other options)" - echo - echo "Topics:" - print_opt "profile" "explanation of profile structure and usage" - echo - echo "Profile:" - echo " Directory containing dotfiles plus optional parent and hooks/." - echo " Used by the installer to group and deploy related configurations." + echo "Usage: ${SCRIPT_NAME} [options]" + echo " ${SCRIPT_NAME} -l | --list-profiles" + echo " ${SCRIPT_NAME} --help[=TOPIC]" + echo + echo "Options:" + print_opt "-h, --help[=TOPIC]" \ + "show general help or help for a specific topic" + print_opt "-p, --profile PROFILE" "select profile" + print_opt "-v, --verbose" "increase verbosity" + print_opt "-P, --prune-symlinks" \ + "remove dangling symlinks in home directory" + print_opt "-l, --list-profiles" \ + "list available profiles (cannot be combined with other options)" + echo + echo "Topics:" + print_opt "profile" "explanation of profile structure and usage" + echo + echo "Profile:" + echo " Directory containing dotfiles plus optional parent and hooks/." + echo " Used by the installer to group and deploy related configurations." } show_help_topic() { - local topic=$1 - case ${topic} in - profile) - cat </dev/null || exit 1 - local common_files - local -r target_profile=$1 - 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)) - local target_file - for target_file in "${common_files[@]}"; do - local -a rm_args=(--force) - [[ ${verbose} ]] && rm_args+=(--verbose) - rm "${rm_args[@]}" -- "${target_file}" - done - popd >/dev/null || exit 1 + pushd "${HOME}" >/dev/null || exit 1 + local common_files + local -r target_profile="$1" + 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)) + local target_file + for target_file in "${common_files[@]}"; do + local -a rm_args=("--force") + [[ ${verbose} ]] && rm_args+=("--verbose") + rm "${rm_args[@]}" -- "${target_file}" + done + popd >/dev/null || exit 1 - echo "OK" + echo "OK" } stow_profile() { - local -r target_profile=$1 - if stow "${STOW_ARGS[@]}" "${target_profile}" 2>&1; then - echo "Profile ${target_profile} has been stowed" - else - echo "Failed to create symlinks pointing to dotfiles in profile ${target_profile}" - fi + local -r target_profile="$1" + if stow "${STOW_ARGS[@]}" "${target_profile}" 2>&1; then + echo "Profile ${target_profile} has been stowed" + else + echo "Failed to create symlinks pointing to dotfiles" \ + "in profile ${target_profile}" + fi } fetch_parents() { - local -r target_profile=$1 - if [[ -s ${PROFILES_DIR}/${target_profile}/parent ]]; then - local parent - parent=$(cat "${PROFILES_DIR}/${target_profile}/parent") - if [[ -s ${PROFILES_DIR}/${parent}/parent ]]; then - fetch_parents "${parent}" - fi - echo "${parent}" + local -r target_profile="$1" + if [[ -s "${PROFILES_DIR}/${target_profile}/parent" ]]; then + local parent + parent="$(cat "${PROFILES_DIR}/${target_profile}/parent")" + if [[ -s "${PROFILES_DIR}/${parent}/parent" ]]; then + fetch_parents "${parent}" fi + echo "${parent}" + fi } build_deptree() { - local -r target_profile=$1 - fetch_parents "${target_profile}" - echo "${target_profile}" + local -r target_profile="$1" + fetch_parents "${target_profile}" + echo "${target_profile}" } install_dotfiles() { - local target_profile - for target_profile in $(build_deptree "${profile}"); do - run_pre_hooks "${target_profile}" - [[ ${prune_symlinks} ]] && prune_symlinks "${target_profile}" - stow_profile "${target_profile}" - run_post_hooks "${target_profile}" - done + local target_profile + for target_profile in $(build_deptree "${profile}"); do + run_pre_hooks "${target_profile}" + [[ -n "${prune_symlinks}" ]] && prune_symlinks "${target_profile}" + stow_profile "${target_profile}" + run_post_hooks "${target_profile}" + done } main() { - check_deps - parse_args "$@" - require_profiles_dir - (( $# == 0 )) && show_general_help && exit 1 - validate_args - [[ ${list_profiles} ]] && echo "${PROFILES[@]}" && exit 0 - install_dotfiles + check_deps + parse_args "$@" + require_profiles_dir + (($# == 0)) && show_general_help && exit 1 + validate_args + [[ -n "${list_profiles}" ]] && echo "${PROFILES[@]}" && exit 0 + install_dotfiles } main "$@"