439 lines
14 KiB
Bash
439 lines
14 KiB
Bash
#!/bin/bash
|
|
|
|
# Path: Scripts/base.sh
|
|
|
|
set -e
|
|
|
|
BLUE='\033[1;34m'
|
|
YELLOW='\033[1;33m'
|
|
GREEN='\033[1;32m'
|
|
RED='\033[1;31m'
|
|
NC='\033[0m'
|
|
|
|
DOTFILES_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
|
|
REPO_ROOT="$(dirname "$DOTFILES_DIR")"
|
|
source "$DOTFILES_DIR/lib/distro.sh"
|
|
|
|
OH_MY_ZSH_REPO="https://github.com/ohmyzsh/ohmyzsh.git"
|
|
OH_MY_ZSH_COMMIT="b26b5002633e865b70e17933536fe4dc99127898"
|
|
ZSH_SYNTAX_HIGHLIGHTING_REPO="https://github.com/zsh-users/zsh-syntax-highlighting.git"
|
|
ZSH_SYNTAX_HIGHLIGHTING_COMMIT="5eb677bb0fa9a3e60f0eff031dc13926e093df92"
|
|
ZSH_AUTOSUGGESTIONS_REPO="https://github.com/zsh-users/zsh-autosuggestions.git"
|
|
ZSH_AUTOSUGGESTIONS_COMMIT="85919cd1ffa7d2d5412f6d3fe437ebdbeeec4fc5"
|
|
RCLONE_VERSION="v1.74.2"
|
|
EARTHLY_VERSION="v0.8.16"
|
|
|
|
clone_pinned_repo() {
|
|
local repo_url="$1"
|
|
local destination="$2"
|
|
local commit="$3"
|
|
local label="$4"
|
|
|
|
if [ -d "$destination" ]; then
|
|
echo -e "${GREEN} LOG: $label already present.${NC}"
|
|
return 0
|
|
fi
|
|
|
|
echo -e "${BLUE} LOG:${YELLOW} Installing $label at pinned commit ${commit:0:12}...${NC}"
|
|
git clone "$repo_url" "$destination"
|
|
git -C "$destination" checkout "$commit"
|
|
|
|
if [ "$(git -C "$destination" rev-parse HEAD)" != "$commit" ]; then
|
|
echo -e "${RED} ERROR: Failed to pin $label to expected commit $commit${NC}"
|
|
exit 1
|
|
fi
|
|
}
|
|
|
|
install_debian_docker_from_repo() {
|
|
local docker_repo_os="$OS"
|
|
local docker_codename="${VERSION_CODENAME:-}"
|
|
local dpkg_arch
|
|
|
|
if [[ "$docker_repo_os" != "ubuntu" && "$docker_repo_os" != "debian" ]]; then
|
|
docker_repo_os="ubuntu"
|
|
fi
|
|
|
|
if [ -z "$docker_codename" ] && command -v lsb_release >/dev/null 2>&1; then
|
|
docker_codename="$(lsb_release -cs)"
|
|
fi
|
|
|
|
if [ -z "$docker_codename" ]; then
|
|
echo -e "${RED} ERROR: Unable to determine Debian/Ubuntu codename for Docker repository setup.${NC}"
|
|
exit 1
|
|
fi
|
|
|
|
dpkg_arch="$(dpkg --print-architecture)"
|
|
|
|
sudo apt-get remove -y docker.io docker-doc docker-compose docker-compose-v2 podman-docker containerd runc >/dev/null 2>&1 || true
|
|
sudo install -m 0755 -d /etc/apt/keyrings
|
|
curl -fsSL "https://download.docker.com/linux/${docker_repo_os}/gpg" | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
|
|
sudo chmod a+r /etc/apt/keyrings/docker.gpg
|
|
echo "deb [arch=${dpkg_arch} signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/${docker_repo_os} ${docker_codename} stable" |
|
|
sudo tee /etc/apt/sources.list.d/docker.list >/dev/null
|
|
sudo apt-get update
|
|
sudo DEBIAN_FRONTEND=noninteractive apt-get install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
|
|
sudo systemctl enable --now docker
|
|
}
|
|
|
|
download_and_verify_release_asset() {
|
|
local asset_url="$1"
|
|
local checksum_url="$2"
|
|
local asset_name="$3"
|
|
local destination_path="$4"
|
|
|
|
local checksum_file
|
|
local expected_checksum
|
|
local actual_checksum
|
|
|
|
checksum_file="$(mktemp)"
|
|
|
|
curl -fLsS "$asset_url" -o "$destination_path"
|
|
curl -fLsS "$checksum_url" -o "$checksum_file"
|
|
|
|
expected_checksum="$(awk -v asset="$asset_name" '$2 == asset { print $1; exit }' "$checksum_file")"
|
|
|
|
if [ -z "$expected_checksum" ]; then
|
|
rm -f "$checksum_file"
|
|
echo -e "${RED} ERROR: Could not find checksum for $asset_name in $checksum_url${NC}"
|
|
exit 1
|
|
fi
|
|
|
|
actual_checksum="$(sha256sum "$destination_path" | awk '{print $1}')"
|
|
|
|
if [ "$actual_checksum" != "$expected_checksum" ]; then
|
|
rm -f "$checksum_file"
|
|
echo -e "${RED} ERROR: Checksum verification failed for $asset_name${NC}"
|
|
exit 1
|
|
fi
|
|
|
|
rm -f "$checksum_file"
|
|
}
|
|
|
|
ensure_git_credential_helper() {
|
|
local helper_value="$1"
|
|
local existing_helpers=""
|
|
|
|
existing_helpers=$(git config --global --get-all credential.helper 2>/dev/null || true)
|
|
|
|
if printf '%s\n' "$existing_helpers" | grep -Fx -- "$helper_value" >/dev/null 2>&1; then
|
|
return 0
|
|
fi
|
|
|
|
git config --global --add credential.helper "$helper_value"
|
|
}
|
|
|
|
echo -e "${BLUE} LOG:${YELLOW} Initializing Base System Layer...${NC}"
|
|
|
|
# Confirm Architecture
|
|
ARCH=$(uname -m)
|
|
if [ "$ARCH" != "x86_64" ] && [ "$ARCH" != "aarch64" ] && [ "$ARCH" != "arm64" ]; then
|
|
echo -e "${RED} ERROR: Unsupported architecture: $ARCH${NC}"
|
|
exit 1
|
|
fi
|
|
|
|
is_wsl() {
|
|
[ -f /proc/version ] && grep -qEi "(Microsoft|WSL)" /proc/version
|
|
}
|
|
|
|
# Package Installation
|
|
PACKAGES=(
|
|
curl wget git sudo
|
|
zsh tmux
|
|
unzip tar gzip
|
|
build-essential
|
|
openssl
|
|
python bison mercurial
|
|
ripgrep fd bat fzf jq
|
|
btop httpie gnupg
|
|
zoxide stow direnv
|
|
bind nmap socat tcpdump net-tools
|
|
strace gdb hexyl
|
|
ninja-build libcurl4-openssl-dev
|
|
just
|
|
)
|
|
|
|
FINAL_PACKAGES=()
|
|
|
|
for pkg in "${PACKAGES[@]}"; do
|
|
case "$pkg" in
|
|
"build-essential")
|
|
if is_arch_family; then
|
|
FINAL_PACKAGES+=(base-devel)
|
|
elif is_debian_family; then
|
|
FINAL_PACKAGES+=(build-essential)
|
|
elif is_fedora_family; then
|
|
FINAL_PACKAGES+=(gcc gcc-c++ make patch)
|
|
elif is_macos; then
|
|
:
|
|
fi
|
|
continue
|
|
;;
|
|
"python")
|
|
if is_arch_family; then
|
|
FINAL_PACKAGES+=(python)
|
|
elif is_debian_family; then
|
|
FINAL_PACKAGES+=(python3 python3-pip python3-venv)
|
|
elif is_fedora_family; then
|
|
FINAL_PACKAGES+=(python3 python3-pip)
|
|
elif is_macos; then
|
|
FINAL_PACKAGES+=(python)
|
|
fi
|
|
continue
|
|
;;
|
|
"fd")
|
|
if is_arch_family; then
|
|
FINAL_PACKAGES+=(fd)
|
|
elif is_debian_family || is_fedora_family; then
|
|
FINAL_PACKAGES+=(fd-find)
|
|
fi
|
|
continue
|
|
;;
|
|
"bat")
|
|
FINAL_PACKAGES+=(bat)
|
|
continue
|
|
;;
|
|
"openssl")
|
|
if is_arch_family; then
|
|
FINAL_PACKAGES+=(openssl)
|
|
elif is_debian_family; then
|
|
FINAL_PACKAGES+=(libssl-dev)
|
|
elif is_fedora_family; then
|
|
FINAL_PACKAGES+=(openssl openssl-devel)
|
|
elif is_macos; then
|
|
FINAL_PACKAGES+=(openssl@3)
|
|
fi
|
|
continue
|
|
;;
|
|
"bind")
|
|
if is_arch_family; then
|
|
FINAL_PACKAGES+=(bind)
|
|
elif is_debian_family; then
|
|
FINAL_PACKAGES+=(dnsutils)
|
|
elif is_fedora_family; then
|
|
FINAL_PACKAGES+=(bind-utils)
|
|
elif is_macos; then
|
|
FINAL_PACKAGES+=(bind)
|
|
fi
|
|
continue
|
|
;;
|
|
"ninja-build")
|
|
if is_arch_family; then
|
|
FINAL_PACKAGES+=(ninja)
|
|
elif is_debian_family || is_fedora_family; then
|
|
FINAL_PACKAGES+=(ninja-build)
|
|
elif is_macos; then
|
|
FINAL_PACKAGES+=(ninja)
|
|
fi
|
|
continue
|
|
;;
|
|
"libcurl4-openssl-dev")
|
|
if is_arch_family; then
|
|
FINAL_PACKAGES+=(curl)
|
|
elif is_debian_family; then
|
|
FINAL_PACKAGES+=(libcurl4-openssl-dev)
|
|
elif is_fedora_family; then
|
|
FINAL_PACKAGES+=(libcurl-devel)
|
|
elif is_macos; then
|
|
FINAL_PACKAGES+=(curl)
|
|
fi
|
|
continue
|
|
;;
|
|
"gnupg")
|
|
if is_fedora_family; then
|
|
FINAL_PACKAGES+=(gnupg2)
|
|
elif is_macos; then
|
|
FINAL_PACKAGES+=(gnupg)
|
|
else
|
|
FINAL_PACKAGES+=(gnupg)
|
|
fi
|
|
continue
|
|
;;
|
|
*)
|
|
esac
|
|
|
|
FINAL_PACKAGES+=("$pkg")
|
|
done
|
|
|
|
if is_debian_family; then
|
|
FINAL_PACKAGES+=(ca-certificates bsdmainutils pkg-config cmake)
|
|
fi
|
|
|
|
if is_fedora_family; then
|
|
FINAL_PACKAGES+=(ca-certificates pkgconf-pkg-config cmake)
|
|
FINAL_PACKAGES+=(R-core gcc-gfortran bzip2 bzip2-devel readline-devel sqlite sqlite-devel tk-devel libffi-devel xz xz-devel ncurses-devel zlib-devel findutils llvm)
|
|
fi
|
|
|
|
if is_macos; then
|
|
FINAL_PACKAGES=(
|
|
curl wget git zsh tmux unzip
|
|
python bison mercurial
|
|
ripgrep fd bat fzf jq
|
|
btop httpie gnupg
|
|
zoxide stow direnv
|
|
bind nmap socat
|
|
hexyl ninja just
|
|
)
|
|
fi
|
|
|
|
echo -e "${BLUE} LOG:${YELLOW} Installing: ${NC}${FINAL_PACKAGES[*]}"
|
|
|
|
install_status=0
|
|
install_packages "${FINAL_PACKAGES[@]}" || install_status=$?
|
|
|
|
if [ "$install_status" -eq 42 ]; then
|
|
exit 0
|
|
elif [ "$install_status" -ne 0 ]; then
|
|
exit "$install_status"
|
|
fi
|
|
|
|
if is_debian_family || is_fedora_family; then
|
|
echo -e "${BLUE} LOG:${YELLOW} Fixing fd/bat binary names when needed...${NC}"
|
|
[ -f /usr/bin/fdfind ] && sudo ln -sf /usr/bin/fdfind /usr/local/bin/fd
|
|
[ -f /usr/bin/batcat ] && sudo ln -sf /usr/bin/batcat /usr/local/bin/bat
|
|
fi
|
|
|
|
# Installing pinned repo-managed CLI binaries
|
|
bash "$REPO_ROOT/Scripts/bin/install.sh" --all
|
|
|
|
if ! command -v rclone &> /dev/null; then
|
|
echo -e "${BLUE} LOG:${YELLOW} Installing Rclone CLI...${NC}"
|
|
|
|
if is_macos; then
|
|
brew install rclone
|
|
else
|
|
|
|
case "$ARCH" in
|
|
x86_64)
|
|
RCLONE_ARCH="amd64"
|
|
;;
|
|
aarch64|arm64)
|
|
RCLONE_ARCH="arm64"
|
|
;;
|
|
esac
|
|
|
|
TEMP_DIR="$(mktemp -d)"
|
|
RCLONE_ZIP="$TEMP_DIR/rclone.zip"
|
|
RCLONE_ASSET="rclone-${RCLONE_VERSION}-linux-${RCLONE_ARCH}.zip"
|
|
RCLONE_ASSET_URL="https://github.com/rclone/rclone/releases/download/${RCLONE_VERSION}/${RCLONE_ASSET}"
|
|
RCLONE_CHECKSUM_URL="https://github.com/rclone/rclone/releases/download/${RCLONE_VERSION}/SHA256SUMS"
|
|
|
|
download_and_verify_release_asset "$RCLONE_ASSET_URL" "$RCLONE_CHECKSUM_URL" "$RCLONE_ASSET" "$RCLONE_ZIP"
|
|
unzip -q "$RCLONE_ZIP" -d "$TEMP_DIR"
|
|
install -m 755 "$TEMP_DIR"/rclone-${RCLONE_VERSION}-linux-"${RCLONE_ARCH}"/rclone "$HOME/.local/bin/rclone"
|
|
rm -rf "$TEMP_DIR"
|
|
fi
|
|
fi
|
|
|
|
if ! command -v earthly &> /dev/null; then
|
|
echo -e "${BLUE} LOG:${YELLOW} Installing Earthly CLI...${NC}"
|
|
|
|
if is_macos; then
|
|
brew install earthly
|
|
else
|
|
|
|
case "$ARCH" in
|
|
x86_64)
|
|
EARTHLY_ARCH="amd64"
|
|
;;
|
|
aarch64|arm64)
|
|
EARTHLY_ARCH="arm64"
|
|
;;
|
|
esac
|
|
|
|
EARTHLY_ASSET="earthly-linux-${EARTHLY_ARCH}"
|
|
EARTHLY_ASSET_URL="https://github.com/earthly/earthly/releases/download/${EARTHLY_VERSION}/${EARTHLY_ASSET}"
|
|
EARTHLY_CHECKSUM_URL="https://github.com/earthly/earthly/releases/download/${EARTHLY_VERSION}/checksum.asc"
|
|
|
|
download_and_verify_release_asset "$EARTHLY_ASSET_URL" "$EARTHLY_CHECKSUM_URL" "$EARTHLY_ASSET" "$HOME/.local/bin/earthly"
|
|
chmod +x "$HOME/.local/bin/earthly"
|
|
fi
|
|
fi
|
|
|
|
# Docker Installation
|
|
if command -v docker &> /dev/null; then
|
|
echo -e "${GREEN} LOG: Docker is already installed.${NC}"
|
|
else
|
|
if is_wsl; then
|
|
echo -e "${RED} LOG: WSL Detected! Skipping Native Docker.${NC}"
|
|
echo -e "${RED} >>> Please install Docker Desktop on Windows.${NC}"
|
|
elif is_macos; then
|
|
echo -e "${YELLOW} NOTE:${NC} Docker is not installed. Please install Docker Desktop for macOS manually."
|
|
else
|
|
echo -e "${BLUE} LOG:${YELLOW} Installing Native Docker...${NC}"
|
|
if is_arch_family; then
|
|
sudo pacman -S --noconfirm --needed docker docker-compose
|
|
sudo systemctl enable --now docker
|
|
elif is_debian_family; then
|
|
install_debian_docker_from_repo
|
|
elif is_fedora_family; then
|
|
if is_atomic_fedora; then
|
|
echo -e "${YELLOW} NOTE:${NC} Skipping native Docker auto-install on rpm-ostree systems."
|
|
echo -e "${YELLOW} NOTE:${NC} After reboot, install your preferred container runtime separately if needed."
|
|
else
|
|
sudo dnf install -y moby-engine docker-compose
|
|
sudo systemctl enable --now docker
|
|
fi
|
|
fi
|
|
|
|
# Add user to group
|
|
if command -v getent >/dev/null 2>&1 && getent group docker >/dev/null 2>&1; then
|
|
sudo usermod -aG docker $(whoami)
|
|
fi
|
|
fi
|
|
fi
|
|
|
|
# 4. Zsh & Configuration
|
|
if [ ! -d "$HOME/.oh-my-zsh" ]; then
|
|
clone_pinned_repo "$OH_MY_ZSH_REPO" "$HOME/.oh-my-zsh" "$OH_MY_ZSH_COMMIT" "Oh My Zsh"
|
|
fi
|
|
|
|
# Plugins
|
|
ZSH_CUSTOM="${ZSH_CUSTOM:-$HOME/.oh-my-zsh/custom}"
|
|
mkdir -p "$ZSH_CUSTOM/plugins"
|
|
[ ! -d "$ZSH_CUSTOM/plugins/zsh-syntax-highlighting" ] && clone_pinned_repo "$ZSH_SYNTAX_HIGHLIGHTING_REPO" "$ZSH_CUSTOM/plugins/zsh-syntax-highlighting" "$ZSH_SYNTAX_HIGHLIGHTING_COMMIT" "zsh-syntax-highlighting"
|
|
[ ! -d "$ZSH_CUSTOM/plugins/zsh-autosuggestions" ] && clone_pinned_repo "$ZSH_AUTOSUGGESTIONS_REPO" "$ZSH_CUSTOM/plugins/zsh-autosuggestions" "$ZSH_AUTOSUGGESTIONS_COMMIT" "zsh-autosuggestions"
|
|
|
|
# 5. Git Credentials (WSL Bridge)
|
|
if is_wsl; then
|
|
GCM_WIN="/mnt/c/Program Files/Git/mingw64/bin/git-credential-manager.exe"
|
|
[ -f "$GCM_WIN" ] && ensure_git_credential_helper "! \"$GCM_WIN\""
|
|
else
|
|
ensure_git_credential_helper 'cache --timeout=43200'
|
|
fi
|
|
|
|
# 6. Cleanup & Secrets
|
|
rm -f "$HOME/.zshrc" "$HOME/.zsh_aliases"
|
|
mkdir -p "$REPO_ROOT/Zsh"
|
|
touch "$REPO_ROOT/Zsh/.zsh_secrets"
|
|
|
|
# 7. Set Shell
|
|
TARGET_SHELL="$(command -v zsh)"
|
|
CURRENT_LOGIN_SHELL=""
|
|
|
|
if is_macos && [ -x /bin/zsh ]; then
|
|
TARGET_SHELL="/bin/zsh"
|
|
fi
|
|
|
|
if command -v getent >/dev/null 2>&1; then
|
|
CURRENT_LOGIN_SHELL="$(getent passwd "$(whoami)" | cut -d: -f7)"
|
|
elif is_macos && command -v dscl >/dev/null 2>&1; then
|
|
CURRENT_LOGIN_SHELL="$(dscl . -read "/Users/$(whoami)" UserShell 2>/dev/null | awk '{print $2}')"
|
|
fi
|
|
|
|
if [ "$CURRENT_LOGIN_SHELL" != "$TARGET_SHELL" ]; then
|
|
if [ ! -t 0 ]; then
|
|
echo -e "${YELLOW} NOTE:${NC} Non-interactive session detected. Skipping login shell change to $TARGET_SHELL."
|
|
echo -e "${YELLOW} NOTE:${NC} Run 'chsh -s $TARGET_SHELL' manually later if you want zsh as your login shell."
|
|
elif is_macos && command -v chsh >/dev/null 2>&1; then
|
|
chsh -s "$TARGET_SHELL"
|
|
elif command -v chsh >/dev/null 2>&1; then
|
|
sudo chsh -s "$TARGET_SHELL" "$(whoami)"
|
|
elif command -v usermod >/dev/null 2>&1; then
|
|
sudo usermod -s "$TARGET_SHELL" "$(whoami)"
|
|
else
|
|
echo -e "${YELLOW} NOTE:${NC} Could not find chsh/usermod. Please change your login shell to $TARGET_SHELL manually."
|
|
fi
|
|
fi
|
|
|
|
echo -e "${GREEN} LOG: Base System Setup Complete.${NC}"
|