refactor(setup): idempotent symlinks/copies, diff flag, flat COPIES

This commit is contained in:
2026-04-23 21:38:56 +02:00
parent 201ca63d45
commit dcb5ec9713
+52 -43
View File
@@ -15,6 +15,7 @@ PRINT_HELP=false
FORCE=false FORCE=false
REMOVE_EXISTING=false REMOVE_EXISTING=false
IGNORE_EXISTING=false IGNORE_EXISTING=false
DIFF=false
ERROR=false ERROR=false
# Define packages # Define packages
@@ -77,8 +78,9 @@ SYMLINKS=(
typeset -a COPIES typeset -a COPIES
COPIES=( COPIES=(
".codex/config.toml" ".codex/config.toml"
".config/gtk-3.0" ".config/gtk-3.0/bookmarks"
".config/gtk-4.0" ".config/gtk-3.0/settings.ini"
".config/gtk-4.0/settings.ini"
".gtkrc-2.0" ".gtkrc-2.0"
) )
@@ -95,7 +97,7 @@ typeset -A SYMLINK_MAP
SYMLINK_MAP[zsh/rc]=".zshrc" SYMLINK_MAP[zsh/rc]=".zshrc"
error() { error() {
msg="$1" local msg="$@"
ERROR=true ERROR=true
echo "Error: $msg" >&2 echo "Error: $msg" >&2
} }
@@ -117,10 +119,10 @@ remove_symlink() {
echo "Removing symlink: $link -> $src" echo "Removing symlink: $link -> $src"
rm "$link" rm "$link"
elif test -e "$link"; then elif test -e "$link"; then
error "object to be removed is not a symlink:" error "object to be removed is not a symlink:" \
error "${link}: $(stat -c '%F' -- "$link")" "${link}: $(stat -c '%F' -- "$link")"
return 1 return 0
fi fi
} }
@@ -141,14 +143,12 @@ create_symlink() {
rel_dst="$2" rel_dst="$2"
if test -z "$rel_src"; then if test -z "$rel_src"; then
error "missing src argument:" error "missing src argument: $0 $@"
error "$0 $@"
return 1 return 1
fi fi
if test -z "$rel_dst"; then if test -z "$rel_dst"; then
error "missing dst argument:" error "missing dst argument: $0 $@"
error "$0 $@"
return 1 return 1
fi fi
@@ -157,8 +157,7 @@ create_symlink() {
dst_parent="$(dirname -- "$dst")" dst_parent="$(dirname -- "$dst")"
if ! test -e "$src"; then if ! test -e "$src"; then
error "the following source path does not exist:" error "the following source path does not exist: $src"
error "$src"
return 1 return 1
fi fi
@@ -168,26 +167,26 @@ create_symlink() {
fi fi
if test -L "$dst"; then if test -L "$dst"; then
if test "$(readlink -f -- "$dst")" = "$src"; then
return 0
fi
if $FORCE; then if $FORCE; then
if test "$(readlink -f "$dst")" != "$src"; then remove_symlink "$rel_dst"
remove_symlink "$2"
else
return 0
fi
elif $IGNORE_EXISTING; then elif $IGNORE_EXISTING; then
return 0 return 0
else else
error "symbolic link already exists:" error "symlink exists but points elsewhere:" \
error "$dst" "$dst -> $(readlink -f -- "$dst")"
return 1 return 0
fi fi
elif test -e "$dst"; then elif test -e "$dst"; then
error "path already exists that is not a symlink:" error "path already exists but is not a symlink:" \
error "${dst}: $(stat -c '%F' -- "$dst")" "${dst}: $(stat -c '%F' -- "$dst")"
return 1 return 0
fi fi
echo "Creating link: $dst -> $rel_src" echo "Creating link: $dst -> $src"
ln -s "$src" "$dst" ln -s "$src" "$dst"
} }
@@ -206,14 +205,12 @@ copy_item() {
rel_dst="$2" rel_dst="$2"
if test -z "$rel_src"; then if test -z "$rel_src"; then
error "missing src argument:" error "missing src argument: $0 $@"
error "$0 $@"
return 1 return 1
fi fi
if test -z "$rel_dst"; then if test -z "$rel_dst"; then
error "missing dst argument:" error "missing dst argument: $0 $@"
error "$0 $@"
return 1 return 1
fi fi
@@ -221,9 +218,8 @@ copy_item() {
dst="${DEST_DIR}/$rel_dst" dst="${DEST_DIR}/$rel_dst"
dst_parent="$(dirname -- "$dst")" dst_parent="$(dirname -- "$dst")"
if ! test -e "$src"; then if ! test -f "$src"; then
error "the following source path does not exist:" error "source is not a regular file: $src"
error "$src"
return 1 return 1
fi fi
@@ -233,19 +229,26 @@ copy_item() {
fi fi
if test -e "$dst"; then if test -e "$dst"; then
if cmp -s "$src" "$dst"; then
return 0
fi
if $DIFF; then
diff -u "$dst" "$src" || true
fi
if $FORCE; then if $FORCE; then
remove_path "$dst" remove_path "$dst"
elif $IGNORE_EXISTING; then elif $IGNORE_EXISTING; then
return 0 return 0
else else
error "path already exists:" error "file already exists and differs: ${dst}"
error "${dst}" return 0
return 1
fi fi
fi fi
echo "Copying item: from $rel_src to ${dst_parent}/" echo "Copying item: from $rel_src to ${dst_parent}/"
cp -r "$src" "${dst_parent}/" cp "$src" "${dst_parent}/"
} }
create_all_symlinks() { create_all_symlinks() {
@@ -270,8 +273,7 @@ install_system_file() {
rel="$1" rel="$1"
if test -z "$rel"; then if test -z "$rel"; then
error "missing path argument:" error "missing path argument: $0 $@"
error "$0 $@"
return 1 return 1
fi fi
@@ -280,8 +282,7 @@ install_system_file() {
dst_parent="$(dirname -- "$dst")" dst_parent="$(dirname -- "$dst")"
if ! test -e "$src"; then if ! test -e "$src"; then
error "the following source path does not exist:" error "the following source path does not exist: $src"
error "$src"
return 1 return 1
fi fi
@@ -295,14 +296,17 @@ install_system_file() {
return 0 return 0
fi fi
if $DIFF; then
sudo diff -u "$dst" "$src" || true
fi
if $FORCE; then if $FORCE; then
echo "Overwriting: $dst" echo "Overwriting: $dst"
elif $IGNORE_EXISTING; then elif $IGNORE_EXISTING; then
return 0 return 0
else else
error "system file already exists and differs (use --force to overwrite):" error "system file already exists and differs: $dst"
error "$dst" return 0
return 1
fi fi
fi fi
@@ -319,7 +323,7 @@ install_all_system_files() {
check_terminfo() { check_terminfo() {
if ! infocmp tmux-256color > /dev/null; then if ! infocmp tmux-256color > /dev/null; then
error "Missing terminfo for tmux-256color. Try installing ncurses-term." error "Missing terminfo for tmux-256color. Try installing ncurses-term."
return 1 return 0
fi fi
} }
@@ -332,6 +336,7 @@ print_help() {
echo " -f, --force Overwrite any existing links" echo " -f, --force Overwrite any existing links"
echo " -i, --ignore-existing Ignore existing symlinks" echo " -i, --ignore-existing Ignore existing symlinks"
echo " -r, --remove-existing Remove any existing symlinks" echo " -r, --remove-existing Remove any existing symlinks"
echo " -d, --diff Show unified diff when a managed file differs"
} }
while [ $# -gt 0 ]; do while [ $# -gt 0 ]; do
@@ -352,6 +357,10 @@ while [ $# -gt 0 ]; do
IGNORE_EXISTING=true IGNORE_EXISTING=true
shift shift
;; ;;
-d|--diff)
DIFF=true
shift
;;
*) *)
error "unknown option: $1" error "unknown option: $1"
PRINT_HELP=true PRINT_HELP=true