From e42d9ba74902297c54cdef4f849c20ad7d5cd9da Mon Sep 17 00:00:00 2001 From: Adam French Date: Thu, 19 Mar 2026 00:52:39 +0000 Subject: [PATCH] Fix install/uninstall bugs and document both scripts in README Fix glob matching in install.sh shell detection (use [[ ]] instead of [ ]), add missing argument check for uninstall.sh --backup flag, and update README with install/uninstall usage section. Co-Authored-By: Claude Opus 4.6 --- README.md | 59 ++++++++++---- install.sh | 4 +- uninstall.sh | 216 +++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 261 insertions(+), 18 deletions(-) create mode 100755 uninstall.sh diff --git a/README.md b/README.md index d369d73..dc34000 100644 --- a/README.md +++ b/README.md @@ -26,11 +26,42 @@ Install all packages: yay -S $(grep -v '^\s*$' ~/scripts/packages.txt | tr '\n' ' ') ``` -## Linking Configs +## Install / Uninstall -Pick a window manager section below, then add the shared configs. +`install.sh` symlinks configs, sources shell files, and backs up any existing files it would overwrite. `uninstall.sh` reverses the process, removing symlinks and restoring backups. -### Hyprland +```bash +# Install everything (uses Hyprland as the WM) +~/scripts/install.sh all + +# Install specific targets +~/scripts/install.sh hyprland shell nvim tmux + +# Uninstall specific targets (restores from most recent backup) +~/scripts/uninstall.sh nvim shell + +# Uninstall using a specific backup +~/scripts/uninstall.sh --backup ~/scripts/backups/20260319_120000 all +``` + +Available targets: `hyprland`, `sway`, `i3`, `cursor`, `shell`, `alacritty`, `tmux`, `nvim`, `vim`, `all`. + +### tmux prerequisite + +Install the plugin manager before running `install.sh tmux`: + +```bash +git clone https://github.com/tmux-plugins/tpm ~/.tmux/plugins/tpm +``` + +### Manual linking + +If you prefer to link configs manually instead of using `install.sh`: + +
+Manual symlink commands + +#### Hyprland ```bash ln -sf ~/scripts/hypr ~/.config/hypr @@ -38,7 +69,7 @@ ln -sf ~/scripts/waybar ~/.config/waybar ln -sf ~/scripts/wofi ~/.config/wofi ``` -### Sway +#### Sway ```bash ln -sf ~/scripts/sway ~/.config/sway @@ -53,7 +84,7 @@ mv ~/scripts/waybar/config ~/scripts/waybar/config.hypr.json mv ~/scripts/waybar/waybar_sway_config.json ~/scripts/waybar/config ``` -### i3 +#### i3 ```bash ln -sf ~/scripts/i3 ~/.config/i3 @@ -61,13 +92,13 @@ ln -sf ~/scripts/i3blocks ~/.config/i3blocks ln -sf ~/scripts/i3status ~/.config/i3status ``` -### Cursor +#### Cursor ```bash ln -sf ~/scripts/hatsune-miku-windows-linux-cursors/miku-cursor-linux ~/.local/share/icons/"Miku Cursor" ``` -### Shell +#### Shell Add to `~/.bashrc` or `~/.zshrc`: @@ -76,7 +107,7 @@ source ~/scripts/bashrc # or zshrc export PATH="~/scripts/sh:$PATH" ``` -### Terminals +#### Terminals **Alacritty** - add to `~/.config/alacritty/alacritty.toml`: @@ -86,13 +117,7 @@ import = ["~/scripts/alacritty.toml"] **Kitty** - kitty.conf is used directly by the WM configs. -### tmux - -Install the plugin manager first: - -```bash -git clone https://github.com/tmux-plugins/tpm ~/.tmux/plugins/tpm -``` +#### tmux Add to `~/.tmux.conf`: @@ -100,7 +125,7 @@ Add to `~/.tmux.conf`: source ~/scripts/tmux.conf ``` -### Vim +#### Vim Add to `~/.vimrc`: @@ -108,6 +133,8 @@ Add to `~/.vimrc`: source ~/scripts/vimrc ``` +
+ ## Keybindings All window managers and tmux use vim-style hjkl navigation. `$mod` is the Super key. diff --git a/install.sh b/install.sh index df9107c..79a3185 100755 --- a/install.sh +++ b/install.sh @@ -100,11 +100,11 @@ install_cursor() { install_shell() { echo "Installing shell configs..." - if [ -f "$HOME/.zshrc" ] || [ "$SHELL" = *zsh* ]; then + if [ -f "$HOME/.zshrc" ] || [[ "$SHELL" == *zsh* ]]; then append_if_missing "$HOME/.zshrc" "source ~/scripts/zshrc" append_if_missing "$HOME/.zshrc" 'export PATH="$HOME/scripts/sh:$PATH"' fi - if [ -f "$HOME/.bashrc" ] || [ "$SHELL" = *bash* ]; then + if [ -f "$HOME/.bashrc" ] || [[ "$SHELL" == *bash* ]]; then append_if_missing "$HOME/.bashrc" "source ~/scripts/bashrc" append_if_missing "$HOME/.bashrc" 'export PATH="$HOME/scripts/sh:$PATH"' fi diff --git a/uninstall.sh b/uninstall.sh new file mode 100755 index 0000000..b919e9b --- /dev/null +++ b/uninstall.sh @@ -0,0 +1,216 @@ +#!/usr/bin/env bash +set -euo pipefail + +SCRIPTS_DIR="$HOME/scripts" +CONFIG_DIR="$HOME/.config" +BACKUP_DIR="" + +usage() { + echo "Usage: $(basename "$0") [OPTIONS] ..." + echo + echo "Targets:" + echo " hyprland Hyprland + waybar + wofi" + echo " sway Sway + waybar + wofi" + echo " i3 i3 + i3blocks + i3status" + echo " cursor Miku cursor theme" + echo " shell Remove sourced bashrc/zshrc and sh/ from PATH" + echo " alacritty Remove import from alacritty.toml" + echo " tmux Remove sourced tmux.conf" + echo " nvim Neovim (LazyVim) config" + echo " vim Remove sourced vimrc" + echo " all Everything (uses hyprland as WM)" + echo + echo "Options:" + echo " -h, --help Show this help" + echo " --backup Use specific backup directory for restoring" + echo " (default: most recent in $SCRIPTS_DIR/backups/)" +} + +# Find the backup directory to restore from +find_backup() { + if [ -n "$BACKUP_DIR" ]; then + if [ ! -d "$BACKUP_DIR" ]; then + echo "Error: backup directory not found: $BACKUP_DIR" + exit 1 + fi + return + fi + local backups_root="$SCRIPTS_DIR/backups" + if [ -d "$backups_root" ]; then + local latest + latest=$(ls -1d "$backups_root"/*/ 2>/dev/null | sort | tail -n1 || true) + if [ -n "$latest" ]; then + BACKUP_DIR="${latest%/}" + echo "Using backup: $BACKUP_DIR" + fi + fi + if [ -z "$BACKUP_DIR" ]; then + echo "No backups found, will only remove without restoring" + fi +} + +# Remove a symlink if it points into $SCRIPTS_DIR, then restore from backup if available +restore() { + local dest="$1" + local name + name=$(basename "$dest") + + if [ -L "$dest" ]; then + local target + target=$(readlink "$dest") + if [[ "$target" == "$SCRIPTS_DIR"* ]]; then + rm "$dest" + echo " Removed symlink $dest" + else + echo " Skipping $dest (symlink does not point into $SCRIPTS_DIR)" + return + fi + elif [ -e "$dest" ]; then + echo " Skipping $dest (not a symlink, won't remove)" + return + else + echo " $dest does not exist, nothing to remove" + fi + + # Restore from backup if available + if [ -n "$BACKUP_DIR" ] && [ -e "$BACKUP_DIR/$name" ]; then + cp -a "$BACKUP_DIR/$name" "$dest" + echo " Restored $dest from backup" + fi +} + +# Remove exact matching lines from a file +remove_line() { + local file="$1" line="$2" + if [ ! -f "$file" ]; then + echo " $file does not exist, nothing to remove" + return + fi + if grep -qF "$line" "$file"; then + local tmp + tmp=$(mktemp) + grep -vF "$line" "$file" > "$tmp" || true + mv "$tmp" "$file" + echo " Removed from $file: $line" + # Delete file if only whitespace remains + if [ ! -s "$file" ] || ! grep -q '[^[:space:]]' "$file"; then + rm "$file" + echo " Deleted empty $file" + fi + else + echo " Line not found in $file: $line" + fi +} + +uninstall_hyprland() { + echo "Uninstalling Hyprland configs..." + restore "$CONFIG_DIR/hypr" + restore "$CONFIG_DIR/waybar" + restore "$CONFIG_DIR/wofi" +} + +uninstall_sway() { + echo "Uninstalling Sway configs..." + restore "$CONFIG_DIR/sway" + restore "$CONFIG_DIR/waybar" + restore "$CONFIG_DIR/wofi" +} + +uninstall_i3() { + echo "Uninstalling i3 configs..." + restore "$CONFIG_DIR/i3" + restore "$CONFIG_DIR/i3blocks" + restore "$CONFIG_DIR/i3status" +} + +uninstall_cursor() { + echo "Uninstalling cursor theme..." + restore "$HOME/.local/share/icons/Miku Cursor" +} + +uninstall_shell() { + echo "Uninstalling shell configs..." + if [ -f "$HOME/.zshrc" ]; then + remove_line "$HOME/.zshrc" "source ~/scripts/zshrc" + remove_line "$HOME/.zshrc" 'export PATH="$HOME/scripts/sh:$PATH"' + fi + if [ -f "$HOME/.bashrc" ]; then + remove_line "$HOME/.bashrc" "source ~/scripts/bashrc" + remove_line "$HOME/.bashrc" 'export PATH="$HOME/scripts/sh:$PATH"' + fi +} + +uninstall_alacritty() { + echo "Uninstalling Alacritty config..." + local conf="$CONFIG_DIR/alacritty/alacritty.toml" + remove_line "$conf" 'import = ["~/scripts/alacritty.toml"]' +} + +uninstall_tmux() { + echo "Uninstalling tmux config..." + remove_line "$HOME/.tmux.conf" "source ~/scripts/tmux.conf" +} + +uninstall_nvim() { + echo "Uninstalling Neovim config..." + restore "$CONFIG_DIR/nvim" +} + +uninstall_vim() { + echo "Uninstalling vim config..." + remove_line "$HOME/.vimrc" "source ~/scripts/vimrc" +} + +if [ $# -eq 0 ]; then + usage + exit 1 +fi + +targets=() +while [ $# -gt 0 ]; do + case "$1" in + -h | --help) + usage + exit 0 + ;; + --backup) + if [ $# -lt 2 ]; then + echo "Error: --backup requires a directory argument" + exit 1 + fi + BACKUP_DIR="$2" + shift 2 + ;; + all) + targets+=(hyprland cursor shell alacritty tmux nvim vim) + shift + ;; + *) + targets+=("$1") + shift + ;; + esac +done + +find_backup + +for target in "${targets[@]}"; do + case "$target" in + hyprland) uninstall_hyprland ;; + sway) uninstall_sway ;; + i3) uninstall_i3 ;; + cursor) uninstall_cursor ;; + shell) uninstall_shell ;; + alacritty) uninstall_alacritty ;; + tmux) uninstall_tmux ;; + nvim) uninstall_nvim ;; + vim) uninstall_vim ;; + *) + echo "Unknown target: $target" + usage + exit 1 + ;; + esac +done + +echo "Done!"