#!/usr/bin/env bash set -e # Accept the `--remove` argument to delete symlinks of the files to be copied # This is useful for testing a clean installation (eg. `setup.sh --remove && setup.sh`) # Note: Normal setup overwrites all incorrect symlinks, so re-installation in place is idempotent if [ "$1" == '--remove' ]; then remove=1 else remove=0 fi DIR="$( dirname "$(readlink -f "${BASH_SOURCE[0]}")" )" SRC_DIR="$DIR/src" POST_SETUP_SCRIPTS=( vscode.sh # install vscode configs ssh.sh # set up SSH config and create key if needed gpg.sh # create GPG key if needed diff-highlight.sh # build diff-highlight script from git source ) # read file basenames to copy_files array copy_files=() files_to_remove=() while IFS= read -r -d $'\0'; do # remove leading $SRC_DIR/ from found files # this allows directories to be preserved copy_files+=( "${REPLY//$SRC_DIR\//}" ) done < <(find "$SRC_DIR" -type f -print0) # clear symlinks pointing to non-existent src files symlink_check_dirs=() while IFS= read -r -d $'\0'; do symlink_check_dirs+=( "$(sed -r -e "s|^${SRC_DIR}/?||" <<< "$REPLY")" ) done < <(find "$SRC_DIR" -type d -print0) for dir in "${symlink_check_dirs[@]}"; do while IFS= read -r -d $'\0'; do # it is possible for `readlink` to fail if the link target does not # exist (within multiple non-existent subdirectories?); such entries # aren't valid to check anyway, skip them link_target="$(readlink -f "$REPLY")" || continue # if a symlink is found which points to a SRC_DIR file but there's no # file that it's pointing at, it's an old and outdated link; delete it if [[ "$link_target" =~ ^"$SRC_DIR" && ! -f "$link_target" ]]; then echo "Removing dead dotfiles symlink: '$REPLY'" rm "$REPLY" fi done < <(find "$HOME/$dir" -maxdepth 1 -type l -print0) done if [ $remove -eq 1 ]; then for file in "${copy_files[@]}"; do if [ -L "$file" ]; then rm -v "$file" else echo "Skipping deletion of ${file} because it is not a symlink" fi done elif [ $remove -eq 0 ]; then # Ensure all files are absent in $HOME for file in "${copy_files[@]}"; do source="${SRC_DIR}/${file}" target="${HOME}/${file}" if { [ -e "$target" ] && ! [ -L "$target" ]; } \ || { [ -L "$target" ] && [[ "$source" != "$(readlink -f "$target")" ]]; }; then files_to_remove+=( "$file" ) fi done if [ "${#files_to_remove}" -ne 0 ]; then echo "Remove the following files/symlinks from home directory and re-run setup!" printf '%s\n' "${files_to_remove[@]}" exit 1 fi # Symlink files to $HOME, overwrite incorrect symlinks for file in "${copy_files[@]}"; do source="${SRC_DIR}/${file}" target="${HOME}/${file}" if [[ -h "$target" ]] && [[ "$source" == "$(readlink -f "$target")" ]]; then echo "Skipping '${source}' -> '${target}' link as it is already linked" else target_dir="$(dirname "$target")" while [ ! -d "$target_dir" ]; do target_parent="$target_dir" while [ ! -d "$(dirname "$target_parent")" ]; do target_parent="$(dirname "$target_parent")" done mkdir -vm 0700 "$target_parent" done ln -sv "$source" "$target" fi done fi for script in "${POST_SETUP_SCRIPTS[@]}"; do source "${DIR}/post-setup/${script}" done