From 348ef2a0e7a2a369ca4a0f71a5cd7ce33619cfbe Mon Sep 17 00:00:00 2001 From: MangoPig Date: Mon, 1 Jun 2026 21:48:06 +0100 Subject: [PATCH] Harden installers and wrappers --- Scripts/base.sh | 118 ++++++++++++++++++++++++++++++++++++++++++++---- Zsh/.zshrc | 78 ++++++++++++++++++++++++++++++-- 2 files changed, 184 insertions(+), 12 deletions(-) diff --git a/Scripts/base.sh b/Scripts/base.sh index 4f1ca7f..5d7ada1 100644 --- a/Scripts/base.sh +++ b/Scripts/base.sh @@ -14,6 +14,101 @@ 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" +} + echo -e "${BLUE} LOG:${YELLOW} Initializing Base System Layer...${NC}" # Confirm Architecture @@ -205,10 +300,13 @@ if ! command -v rclone &> /dev/null; then 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" - curl -fLsS "https://downloads.rclone.org/rclone-current-linux-${RCLONE_ARCH}.zip" -o "$RCLONE_ZIP" + 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-*-linux-"${RCLONE_ARCH}"/rclone "$HOME/.local/bin/rclone" + install -m 755 "$TEMP_DIR"/rclone-${RCLONE_VERSION}-linux-"${RCLONE_ARCH}"/rclone "$HOME/.local/bin/rclone" rm -rf "$TEMP_DIR" fi fi @@ -229,8 +327,11 @@ if ! command -v earthly &> /dev/null; then ;; esac - curl -fLsS "https://github.com/earthly/earthly/releases/latest/download/earthly-linux-${EARTHLY_ARCH}" \ - -o "$HOME/.local/bin/earthly" + 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 @@ -250,7 +351,7 @@ else sudo pacman -S --noconfirm --needed docker docker-compose sudo systemctl enable --now docker elif is_debian_family; then - curl -fsSL https://get.docker.com | sh + 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." @@ -270,15 +371,14 @@ fi # 4. Zsh & Configuration if [ ! -d "$HOME/.oh-my-zsh" ]; then - echo -e "${BLUE} LOG:${YELLOW} Installing Oh My Zsh...${NC}" - sh -c "$(curl -fsSL https://raw.githubusercontent.com/ohmyzsh/ohmyzsh/master/tools/install.sh)" "" --unattended + 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" ] && git clone https://github.com/zsh-users/zsh-syntax-highlighting.git "$ZSH_CUSTOM/plugins/zsh-syntax-highlighting" -[ ! -d "$ZSH_CUSTOM/plugins/zsh-autosuggestions" ] && git clone https://github.com/zsh-users/zsh-autosuggestions "$ZSH_CUSTOM/plugins/zsh-autosuggestions" +[ ! -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 diff --git a/Zsh/.zshrc b/Zsh/.zshrc index 8c65b03..c206d3d 100644 --- a/Zsh/.zshrc +++ b/Zsh/.zshrc @@ -16,13 +16,45 @@ export DOTZSH_SYNTAX_HIGHLIGHTING_PLUGIN="$ZSH/custom/plugins/zsh-syntax-highlig # Programming Languages Root export PROG_DIR="$HOME/.programming" +typeset -gA DOTZSH_MISSING_TOOL_WARNED + +dotzsh_warn_missing_tool_once() { + local tool_key="$1" + local tool_label="$2" + local setup_hint="$3" + + if [[ -n "${DOTZSH_MISSING_TOOL_WARNED[$tool_key]:-}" ]]; then + return 0 + fi + + DOTZSH_MISSING_TOOL_WARNED[$tool_key]=1 + + print -u2 -- "$tool_label is not installed on this system." + print -u2 -- "Run: $setup_hint" + print -u2 -- "Or: just setup all" +} + # Go and GVM (Black Box) export GOPATH="$PROG_DIR/go" export GVM_ROOT="$GOPATH" gvm_load() { + local requested_command="$1" + unset -f gvm go gofmt - [[ -s "$GVM_ROOT/scripts/gvm" ]] && source "$GVM_ROOT/scripts/gvm" + + if [[ ! -s "$GVM_ROOT/scripts/gvm" ]]; then + dotzsh_warn_missing_tool_once "go" "Go / GVM" "just lang go" + return 127 + fi + + source "$GVM_ROOT/scripts/gvm" + + if ! command -v "$requested_command" >/dev/null 2>&1; then + dotzsh_warn_missing_tool_once "go" "Go / GVM" "just lang go" + return 127 + fi + "$@" } @@ -34,8 +66,22 @@ gofmt() { gvm_load gofmt "$@"; } export NVM_DIR="$PROG_DIR/node" nvm_load() { + local requested_command="$1" + unset -f nvm node npm npx - [ -s "$NVM_DIR/nvm.sh" ] && . "$NVM_DIR/nvm.sh" + + if [[ ! -s "$NVM_DIR/nvm.sh" ]]; then + dotzsh_warn_missing_tool_once "node" "Node / NVM" "just lang node" + return 127 + fi + + . "$NVM_DIR/nvm.sh" + + if ! command -v "$requested_command" >/dev/null 2>&1; then + dotzsh_warn_missing_tool_once "node" "Node / NVM" "just lang node" + return 127 + fi + "$@" } @@ -55,15 +101,41 @@ export CONDA_ROOT="$PYENV_ROOT/versions/miniforge3-latest" pyenv_load() { unset -f pyenv + + if ! command -v pyenv >/dev/null 2>&1; then + dotzsh_warn_missing_tool_once "python" "Pyenv / Python" "just lang python" + return 127 + fi + eval "$(command pyenv init -)" + + if ! command -v pyenv >/dev/null 2>&1; then + dotzsh_warn_missing_tool_once "python" "Pyenv / Python" "just lang python" + return 127 + fi + pyenv "$@" } pyenv() { pyenv_load "$@"; } conda_load() { + local requested_command="$1" + unset -f conda mamba - [ -f "$CONDA_ROOT/etc/profile.d/conda.sh" ] && source "$CONDA_ROOT/etc/profile.d/conda.sh" + + if [[ ! -f "$CONDA_ROOT/etc/profile.d/conda.sh" ]]; then + dotzsh_warn_missing_tool_once "python" "Conda / Mamba" "just lang python" + return 127 + fi + + source "$CONDA_ROOT/etc/profile.d/conda.sh" + + if ! command -v "$requested_command" >/dev/null 2>&1; then + dotzsh_warn_missing_tool_once "python" "Conda / Mamba" "just lang python" + return 127 + fi + "$@" }