#!/usr/bin/env bash set -euo pipefail source "$PWD/modules/home/bash/bash_funcs" usage() { echo "Usage: $0 [link|remove|update]" exit 1 } # What operation to perform: link, remove, or update. OP="${1:-}" if [[ "$OP" == "" ]]; then OP="link" fi if [[ "$OP" != "link" && "$OP" != "remove" && "$OP" != "update" ]]; then usage fi GREEN_CHECK="$(printf '\x1b[32m✓\x1b[m')" BLACK=0 GREEN=2 YELLOW=3 BLUE=4 WHITE=7 # Print a status message with colors. status() { local text="$1" local bg_color="$2" local fg_color="$3" # Convert color numbers to ANSI codes local bg_code=$((bg_color < 8 ? bg_color + 40 : bg_color + 92)) local fg_code=$((fg_color < 8 ? fg_color + 30 : fg_color + 82)) local padding padding=6 printf "\x1b[1;${bg_code};${fg_code}m %-${padding}s \x1b[m" "$text" } # Rendered status messages. SKIP="$(status "SKIP" "$BLUE" "$WHITE")" LINKED="$(status "LINKED" "$GREEN" "$BLACK")" BACKUP="$(status "BACKUP" "$YELLOW" "$BLACK")" UNLINKED="$(status "UNLINK" "$GREEN" "$BLACK")" # Create or remove a symlink. link() { local op="$1" local source="$PWD/$2" local target="$3" if [[ $op == "remove" ]]; then # Remove symlink. if [ -L "$target" ]; then rm "$target" echo "$UNLINKED $target" else echo "$SKIP No symlink to unlink at $target" fi else # Create symlink. mkdir -p "$(dirname "$target")" if [ -L "$target" ] && [ "$(readlink "$target")" = "$source" ]; then # Link already exists. echo "$SKIP Link $source -> $target exists" elif [ -L "$target" ]; then # Wrong symlink exists, just replace it. ln -sf "$source" "$target" echo "$LINKED $1 -> $target (replaced wrong symlink)" elif [ -e "$target" ]; then # There's a real file/directory there. Back it up, then link. mv "$target" "$target.backup.$(date +%s)" ln -sf "$source" "$target" echo "$BACKUP Linked $source to $target (backed up existing)" else # Nothing there, create link. ln -sf "$source" "$target" echo "$LINKED $1 -> $target" fi fi } # Ensure a command exists. If it does not, install it using the given command. ensure() { local name="$1" local cmd="$2" local script="$3" # Require curl for installation checks. if ! command -v curl &>/dev/null; then printf "curl is not installed. Please install curl first.\n" return fi if ! command -v "$cmd" &>/dev/null; then echo "Installing $name..." $script else printf "%s %s is installed.\n" "$GREEN_CHECK" "$name" fi } # Install a Haskell package using cabal. cabal_install() { local pkg="$1" cabal install "$pkg" -j"$(num_cores)" --overwrite-policy=always } # Install the V programming language from source. install_v() { local here="$PWD" local targetDir="$HOME/src/v" rm -rf "$targetDir" mkdir -p "$targetDir" git clone --depth=1 https://github.com/vlang/v "$targetDir" cd "$targetDir" make sudo ./v symlink cd "$here" } os="$(which_os)" # Generally install or update stuff. if [[ $OP == "update" ]]; then ensure "GHCup" "ghcup" "curl --proto '=https' --tlsv1.2 -sSf https://get-ghcup.haskell.org | sh" cabal update ensure "Fourmolu" "fourmolu" "cabal_install fourmolu" ensure "Floskell" "floskell" "cabal_install floskell" ensure "V" "v" "install_v" printf "\n" if [[ "$(which_os)" == "darwin" ]]; then ensure "Nix" "nix" "sh <(curl --proto '=https' --tlsv1.2 -L https://nixos.org/nix/install)" ensure "Homebrew" "brew" "sh -c \"$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)\"" ensure "cabal-fmt" "cabal-fmt" "cabal install cabal-fmt -j$(num_cores)" brew bundle fi fi link "$OP" "git/config" "$HOME/.config/git/config" link "$OP" "rio/config.toml" "$HOME/.config/rio/config.toml" link "$OP" "crush/config.json" "$HOME/.config/crush/crush.json" link "$OP" "tmux/tmux.conf" "$HOME/.tmux.conf" link "$OP" "helix" "$HOME/.config/helix" # Kitty. link "$OP" "kitty/kitty.conf" "$HOME/.config/kitty/kitty.conf" if [[ $os = "darwin" ]]; then link "$OP" "kitty/darwin.conf" "$HOME/.config/kitty/os.conf" else link "$OP" "kitty/linux.conf" "$HOME/.config/kitty/os.conf" fi link "$OP" "fourmolu/fourmolu.yaml" "$HOME/.config/fourmolu.yaml"