#!/usr/bin/env bash set -euo pipefail SCRIPT_DIR="$(cd -- "$(dirname -- "$0")" && pwd)" source "$SCRIPT_DIR/bash/bash_funcs" usage() { echo "Usage: $0 [link|remove|update] [--fetch]" echo " --fetch Pull Git LFS objects for the fonts submodule before linking" exit 1 } XDG_CONFIG_HOME="$HOME/.config" XDG_DATA_HOME="$HOME/.local/share" # What operation to perform: link, remove, or update. # --fetch may be given alongside any operation. OP="link" FETCH_LFS=0 for arg in "$@"; do case "$arg" in --fetch) FETCH_LFS=1 ;; *) OP="$arg" ;; esac done if [[ "$OP" != "link" && "$OP" != "remove" && "$OP" != "update" ]]; then usage fi # Ensure the fonts submodule holds real font files, not LFS pointer stubs. fetch_fonts() { if [[ "$FETCH_LFS" != "1" ]]; then return fi if [[ "$(command_exists git-lfs)" != "1" ]]; then printf "git-lfs is not installed; skipping --fetch.\n" >&2 return fi git -C "$SCRIPT_DIR" submodule update --init fonts git -C "$SCRIPT_DIR/fonts" lfs install --local git -C "$SCRIPT_DIR/fonts" lfs fetch --all git -C "$SCRIPT_DIR/fonts" lfs checkout printf "%s Fetched LFS fonts\n" "$GREEN_CHECK" } 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")" COPIED="$(status "COPIED" "$GREEN" "$BLACK")" BACKUP="$(status "BACKUP" "$YELLOW" "$BLACK")" UNLINKED="$(status "UNLINK" "$GREEN" "$BLACK")" # Create or remove a symlink. symlink() { local op="$1" local source="$SCRIPT_DIR/$2" local target="$3" local display_source="./$2" local display_target="${target/#$HOME/\~}" if [[ $op == "remove" ]]; then # Remove symlink. if [ -L "$target" ]; then rm "$target" echo "$UNLINKED $display_target" else echo "$SKIP No symlink to unlink at $display_target" fi else # Create symlink. mkdir -p "$(dirname "$target")" if [ -L "$target" ] && [ "$(readlink "$target")" = "$source" ]; then # Link already exists. echo "$SKIP Link $display_source -> $display_target exists" elif [ -L "$target" ]; then # Wrong symlink exists, just replace it. ln -sf "$source" "$target" echo "$LINKED $display_source -> $display_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 $display_source to $display_target (backed up existing)" else # Nothing there, create link. ln -sf "$source" "$target" echo "$LINKED $display_source -> $display_target" fi fi } # Curried symlink using the global OP. link() { symlink "$OP" "$@" } # 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 printf "%s %s is installed.\n" "$GREEN_CHECK" "$name" else printf "Installing %s...\n" "$name" eval "$script" fi } # Install a Python package using pipx (system-wide isolated installation). pipx_install() { local pkg="$1" pipx install "$pkg" pipx upgrade "$pkg" } if [[ "$(uname)" == "Darwin" ]]; then os="darwin" elif [[ -f /etc/os-release ]]; then # shellcheck disable=SC1091 source /etc/os-release os="${ID,,}" else os="linux" fi # Generally install or update stuff. if [[ $OP == "update" ]]; then git pull origin master || exit 1 git submodule update --init --recursive || exit 1 mainPwd="$PWD" cd "$mainPwd/pantera" && git co master && git pull origin master cd "$mainPwd/fonts" && git co master && git pull origin master cd "$mainPwd" if [[ $os == "darwin" ]]; then bash "./scripts/setup-bash" fi case $os in darwin) ensure "Homebrew" "brew" "sh -c \"$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)\"" brew bundle --verbose ensure "pipx" "pipx" "brew install pipx" mise upgrade ;; esac # Ensure Nix is installed if ! command -v nix &>/dev/null; then echo "Nix not found, running install-nix..." bash "./scripts/install-nix" fi # Install/upgrade nix-profile packages echo "Updating nix-profile packages..." nix profile upgrade --all || nix profile add ./nix-profile fi # Bash. link "bash/profile" "$HOME/.profile" link "bash/bash_profile" "$HOME/.bash_profile" link "bash/bashrc" "$HOME/.bashrc" link "bash/bash_funcs" "$XDG_DATA_HOME/meowgorithm/bash_funcs" link "pantera/env.asc" "$XDG_DATA_HOME/meowgorithm/env.asc" # Mise-en-Place link "mise" "$XDG_CONFIG_HOME/mise" # Git link "git/config" "$XDG_CONFIG_HOME/git/config" link "git/ignore" "$XDG_CONFIG_HOME/git/ignore" link "readline/inputrc" "$HOME/.inputrc" link "ssh/config" "$HOME/.ssh/config" link "rio/config.toml" "$XDG_CONFIG_HOME/rio/config.toml" link "crush" "$XDG_CONFIG_HOME/crush" link "pantera/skills" "$XDG_DATA_HOME/meowgorithm/skills" link "vim" "$XDG_CONFIG_HOME/vim" link "helix" "$XDG_CONFIG_HOME/helix" link "prettier/prettierrc.yaml" "$HOME/.prettierrc.yaml" link "emacs/init.el" "$HOME/.emacs.d/init.el" link "zellij/config.kdl" "$XDG_CONFIG_HOME/zellij/config.kdl" link "tmux/tmux.conf" "$HOME/.tmux.conf" # Neovim link "nvim/init.lua" "$XDG_CONFIG_HOME/nvim/init.lua" link "nvim/vsnip" "$XDG_CONFIG_HOME/.vsnip" link "nvim/colors" "$XDG_CONFIG_HOME/nvim/colors" link "nvim/autoload" "$XDG_CONFIG_HOME/nvim/autoload" # Kitty. link "kitty/kitty.conf" "$XDG_CONFIG_HOME/kitty/kitty.conf" if [[ $os = "darwin" ]]; then link "kitty/darwin.conf" "$XDG_CONFIG_HOME/kitty/os.conf" else link "kitty/linux.conf" "$XDG_CONFIG_HOME/kitty/os.conf" fi # Ghostty link "ghostty/config" "$XDG_CONFIG_HOME/ghostty/config" if [[ $os = "darwin" ]]; then link "ghostty/darwin" "$XDG_CONFIG_HOME/ghostty/os" else link "ghostty/linux" "$XDG_CONFIG_HOME/ghostty/os" fi # Fourmolu. link "fourmolu/fourmolu.yaml" "$XDG_CONFIG_HOME/fourmolu.yaml" # Containers (Podman/Docker) link "containers/registries.conf" "$XDG_CONFIG_HOME/containers/registries.conf" # Noctalia shell and Umbriel compositor (NixOS only). case "$os" in nixos) link "noctalia" "$XDG_CONFIG_HOME/noctalia" link "umbriel/config.toml" "$XDG_CONFIG_HOME/umbriel/config.toml" ;; esac # Mako (notification daemon). if [[ $os != "darwin" ]]; then link "mako/config" "$XDG_CONFIG_HOME/mako/config" fi link "pantera/sessions.asc" "$XDG_DATA_HOME/meowgorithm/sessions.asc" # Scripts. scripts=( "gpg-edit-key" "gpg-send" "install-crush" "upgrade-crush" "install-crush-version" "install-rio" "install-stuff" "install-v" "install-z.lua" "install-ghcup" "install-nix" "install-paru" "make-vid" "setup-mouse" "setup-wacom" "zellijls" "tm" "tmls" "uninstall-crush" "sessions" ) if [[ $os == "darwin" ]]; then scripts+=("fix-docker-desktop") fi case "$os" in darwin) ;; *) scripts+=("screen-record") scripts+=("kitty-here") scripts+=("umbriel-focus-follows-mouse") ;; esac for script in "${scripts[@]}"; do link "scripts/$script" "$HOME/.bin/$script" done fetch_fonts # Fonts if [[ -d "$HOME/Library/Fonts" ]]; then # macOS fontDir="$HOME/Library/Fonts/Meowgorithm" else # Linux fontDir="$HOME/.local/share/fonts/Meowgorithm" fi if [[ -n "${fontDir:-}" ]]; then rm -rf "${fontDir/*:?/}" mkdir -p "$fontDir" if [[ $OP != "remove" ]]; then find "$SCRIPT_DIR/fonts/" -type f \( -name "*.otf" -o -name "*.ttf" \) -exec cp {} "$fontDir/" \; if command -v fc-cache &>/dev/null; then fc-cache -f "$fontDir" fi printf "%s Fonts\n" "$COPIED" else printf "%s Fonts\n" "$UNLINKED" fi fi