dotfiles/install.sh
Jeff Clement a12da0c244
fixes
2025-03-06 17:12:57 -07:00

276 lines
7.4 KiB
Bash
Executable file

#!/usr/bin/env bash
#
# A script to bootstrap dotfiles with a mapping of repo-file -> home-file:
# - Clones the repo if needed (when run outside ~/.dotfiles).
# - Installs packages (different for macOS vs. Ubuntu/Debian).
# - Changes shell to Fish if it's not already.
# - Symlinks dotfiles (with custom filename mapping), prompting to replace existing ones.
set -euo pipefail
################################################################################
# VARIABLES
################################################################################
DOTFILES_REPO="https://git.straybits.ca/jeff/dotfiles.git"
DOTFILES_DIR="$HOME/.dotfiles"
# Packages for macOS:
PACKAGES_MAC=(
fish
fzf
bat
ack
fd
age
macvim
mc
lazygit
)
# Packages for Linux (assuming Ubuntu/Debian):
PACKAGES_LINUX=(
git
fish
curl
fzf
bat
ack
fd-find
age
vim-tiny
mc
)
#------------------------------------------------------------------------------
# Symlinking Dotfiles With Different Filenames
#
# In this associative array:
# - The key is the file/directory name as it appears in the dotfiles repo.
# - The value is the target path you want symlinked in your home directory.
#------------------------------------------------------------------------------
SYMLINKS=(
"vim/config.vim:$HOME/.vim_runtime/my_configs.vim"
"vim/plugins:$HOME/.vim_runtime/my_plugins"
"fish:$HOME/.config/fish"
"starship.toml:$HOME/.config/starfish.toml"
)
TARGET_SHELL="fish"
################################################################################
# FUNCTIONS
################################################################################
detect_os() {
local os
os="$(uname -s)"
case "${os}" in
Linux*) echo "linux" ;;
Darwin*) echo "mac" ;;
*) echo "unsupported" ;;
esac
}
install_package_linux() {
local pkg="$1"
# Check if installed:
if ! dpkg -s "$pkg" >/dev/null 2>&1; then
echo "Installing ${pkg} via apt..."
sudo apt-get update -y
sudo apt-get install -y "${pkg}"
else
echo "[OK] ${pkg} is already installed (Linux)."
fi
}
install_package_mac() {
local pkg="$1"
# Check if installed:
if ! brew ls --versions "$pkg" >/dev/null; then
echo "Installing ${pkg} via Homebrew..."
brew install "${pkg}"
else
echo "[OK] ${pkg} is already installed (macOS)."
fi
}
install_packages() {
local os="$1"
if [ "${os}" = "mac" ]; then
# Make sure Homebrew is installed first
if ! command -v brew >/dev/null 2>&1; then
echo "Homebrew not found, installing..."
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
eval "$(/opt/homebrew/bin/brew shellenv)"
fi
# Install each macOS package if missing
for pkg in "${PACKAGES_MAC[@]}"; do
install_package_mac "${pkg}"
done
elif [ "${os}" = "linux" ]; then
if ! command -v apt-get >/dev/null 2>&1; then
echo "Error: This script currently only supports apt-based distros on Linux."
exit 1
fi
# Install each Linux package if missing
for pkg in "${PACKAGES_LINUX[@]}"; do
install_package_linux "${pkg}"
done
# No packages for lazygit on Linux :(
install_lazygit_linux
fi
}
install_lazygit_linux() {
LAZYGIT_VERSION=$(curl -s "https://api.github.com/repos/jesseduffield/lazygit/releases/latest" | \grep -Po '"tag_name": *"v\K[^"]*')
arch="$(uname -m)"
case "$arch" in
x86_64)
LAZYGIT_ARCH="Linux_x86_64"
;;
aarch64)
LAZYGIT_ARCH="Linux_arm64"
;;
*)
echo "Unsupported architecture: $arch"
exit 1
;;
esac
echo "Downloading LazyGit v${LAZYGIT_VERSION} for ${LAZYGIT_ARCH}..."
curl -Lo lazygit.tar.gz \
"https://github.com/jesseduffield/lazygit/releases/download/v${LAZYGIT_VERSION}/lazygit_${LAZYGIT_VERSION}_${LAZYGIT_ARCH}.tar.gz"
tar xf lazygit.tar.gz lazygit
sudo install lazygit -D -t /usr/local/bin/
rm -f lazygit.tar.gz lazygit
}
install_awesome_vim() {
if [ ! -d "$HOME/.vim_runtime" ]; then
echo "Cloning Amix's vimrc..."
git clone --depth=1 https://github.com/amix/vimrc.git "$HOME/.vim_runtime"
echo "Installing Awesome Vimrc..."
sh "$HOME/.vim_runtime/install_awesome_vimrc.sh"
else
echo "~/.vim_runtime already exists, skipping clone."
fi
}
install_starship() {
if ! command -v starship >/dev/null 2>&1; then
echo "Installing Starship"
curl -sS https://starship.rs/install.sh | sh
else
echo "Starship already installed"
fi
}
change_shell_to_fish() {
local fish_path
fish_path="$(command -v fish)"
if ! grep -Fxq "${fish_path}" /etc/shells; then
echo "Adding ${fish_path} to /etc/shells..."
# Some systems might require sudo for writing to /etc/shells
echo "${fish_path}" | sudo tee -a /etc/shells
else
echo "Fish ${fish_path} already in /etc/shells..."
fi
if [[ $SHELL != *"${TARGET_SHELL}"* ]]; then
# Check if fish is actually installed
echo "Changing default shell to '${TARGET_SHELL}'..."
sudo chsh -s "$(command -v fish)" "${USER}"
else
echo "[OK] Shell is already '${TARGET_SHELL}'."
fi
}
create_symlinks() {
# Iterate over the SYMLINKS associative array
for pair in "${SYMLINKS[@]}"; do
local src="${DOTFILES_DIR}/${pair%%:*}"
local dest="${pair#*:}"
# Ensure the parent directory of the destination exists
mkdir -p "$(dirname "${dest}")"
# Early exit if the link already exists
if [ -e "${dest}" ] && [ -L "${dest}" ]; then
echo " -> Link '${dest}' to '${src}' exists."
continue
fi
if [ -e "${dest}" ]; then
# A real file or directory is in the way
read -rp "File '${dest}' already exists. Overwrite? [y/N] " answer
if [[ "${answer}" =~ ^[Yy]$ ]]; then
rm -rf "${dest}"
ln -s "${src}" "${dest}"
echo " -> Overwrote '${dest}' with symlink to '${src}'."
else
echo " -> Skipped '${dest}'."
fi
else
ln -s "${src}" "${dest}"
echo " -> Linked '${dest}' to '${src}'."
fi
done
}
deploy_dotfiles() {
local os
os="$(detect_os)"
if [ "${os}" = "unsupported" ]; then
echo "Unsupported OS: $(uname -s). Exiting."
exit 1
fi
echo "1. Installing necessary packages..."
install_packages "${os}"
echo "2. Intalling awesome vim..."
install_awesome_vim
echo "3. Intalling Starship..."
install_starship
echo "4. Changing shell to '${TARGET_SHELL}' (if needed)..."
change_shell_to_fish
echo "5. Creating symlinks..."
create_symlinks
echo "Done!"
}
################################################################################
# MAIN SCRIPT
################################################################################
# If the script is not being run from ~/.dotfiles, we assume the user just curl'ed it.
if [ "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" != "${DOTFILES_DIR}" ]; then
echo "Script is not in ${DOTFILES_DIR}; assuming first-time setup..."
if [ ! -d "${DOTFILES_DIR}" ]; then
echo "Cloning dotfiles repo into ${DOTFILES_DIR}..."
git clone "${DOTFILES_REPO}" "${DOTFILES_DIR}"
else
echo "${DOTFILES_DIR} already exists. Pulling latest changes..."
git -C "${DOTFILES_DIR}" pull --rebase
fi
echo "Running deployment from cloned repo..."
cd "${DOTFILES_DIR}"
bash install.sh # re-invoke the script from within ~/.dotfiles
exit 0
else
echo "Script running from ${DOTFILES_DIR} - proceeding with deployment..."
deploy_dotfiles
fi