diff --git a/setup.bash b/setup.bash new file mode 100755 index 00000000..d9422f8f --- /dev/null +++ b/setup.bash @@ -0,0 +1,124 @@ +#!/usr/bin/env bash + +# Dotfiles setup script (bash version) +# Main entry point for dotfiles installation + +# shellcheck disable=SC1091 # Don't follow sourced files + +# Enable strict error handling +set -euo pipefail + +# Set up environment +export DOTFILES="$HOME/Repos/ooloth/dotfiles" + +# Main installation function +main() { + printf "\nWelcome to your new Mac! This installation will perform the following steps:\n\n" + printf "1. Confirm this is a Mac\n" + printf "2. Ask you to enter your password\n" + printf "3. Confirm the Command Line Developer Tools are installed\n" + printf "4. Clone ooloth/dotfiles\n" + printf "5. Create your SSH keys\n" + printf "6. Confirm you can SSH to GitHub\n" + printf "7. Install Homebrew\n" + printf "8. Install the packages, casks, App Store apps and VS Code extensions listed in your Brewfile\n" + printf "9. Configure your Mac to use the Homebrew version of Zsh\n" + printf "10. Install rust (if not work computer)\n" + printf "11. Install uv\n" + printf "12. Install the latest version of Node via fnm and set it as the default\n" + printf "13. Install global npm dependencies\n" + printf "14. Install tmux dependencies\n" + printf "15. Install neovim dependencies\n" + printf "16. Install yazi flavors (if not work computer)\n" + printf "17. Symlink your dotfiles to your home and library directories\n" + printf "18. Update macOS system settings\n\n" + + printf "Sound good? (y/N) " + read -r key + + if [[ ! "$key" == 'y' ]]; then + printf "\nNo worries! Maybe next time." + printf "\nExiting...\n" + exit 1 + else + printf "\nExcellent! Here we go...\n\n" + fi + + # Confirm this is a Mac + printf "Confirming this is a Mac...\n\n" + + if [[ "$(uname)" != "Darwin" ]]; then + printf "Error: This script only runs on macOS.\n" + exit 1 + else + printf "āœ“ macOS confirmed.\n\n" + fi + + # Clone or update dotfiles + if [ -d "$DOTFILES" ]; then + printf "šŸ“‚ Dotfiles are already installed. Pulling latest changes.\n" + cd "$DOTFILES" + git pull + else + # Clone via https (will be converted to ssh by install/github.bash) + printf "šŸ“‚ Installing dotfiles\n" + mkdir -p "$DOTFILES" + git clone "https://github.com/ooloth/dotfiles.git" "$DOTFILES" + fi + + # Initialize dotfiles utilities now that repository is available + printf "\nšŸ”§ Initializing dotfiles utilities...\n\n" + + # Initialize dynamic machine detection + source "$DOTFILES/bin/lib/machine-detection.bash" + init_machine_detection + + # Initialize dry-run mode utilities + source "$DOTFILES/bin/lib/dry-run-utils.bash" + parse_dry_run_flags "$@" + + # Initialize enhanced error handling utilities + source "$DOTFILES/bin/lib/error-handling.bash" + + # Run comprehensive prerequisite validation + printf "Running comprehensive prerequisite validation...\n\n" + + source "$DOTFILES/bin/lib/prerequisite-validation.bash" + if ! run_prerequisite_validation; then + printf "\nāŒ Prerequisite validation failed. Please address the issues above and try again.\n" + exit 1 + fi + + printf "āœ… All prerequisites validated successfully.\n\n" + + # Run installation scripts + printf "Running installations...\n\n" + + cd "$DOTFILES/bin/install" + + # Run bash installation scripts if they exist + if [[ -f "ssh.bash" ]]; then + source ssh.bash + fi + + if [[ -f "github.bash" ]]; then + source github.bash + fi + + if [[ -f "homebrew.bash" ]]; then + source homebrew.bash + fi + + if [[ -f "symlinks.bash" ]]; then + source symlinks.bash + fi + + # TODO: Add remaining installation scripts as they are migrated to bash + + printf "\nšŸŽ‰ Setup complete!\n" +} + +# Only run main if script is executed directly +if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then + main "$@" +fi \ No newline at end of file diff --git a/test/setup/test-setup-bash.bats b/test/setup/test-setup-bash.bats new file mode 100644 index 00000000..ef532f5e --- /dev/null +++ b/test/setup/test-setup-bash.bats @@ -0,0 +1,215 @@ +#!/usr/bin/env bats + +# Test setup.bash main entry point + +setup() { + # Save original environment + export ORIGINAL_DOTFILES="${DOTFILES:-}" + export ORIGINAL_HOME="${HOME:-}" + + # Create temporary directory for testing + export TEST_TEMP_DIR + TEST_TEMP_DIR="$(mktemp -d)" + + # Set up test environment + export HOME="$TEST_TEMP_DIR/home" + export DOTFILES="$TEST_TEMP_DIR/home/Repos/ooloth/dotfiles" + mkdir -p "$HOME" + mkdir -p "$DOTFILES" +} + +teardown() { + # Restore original environment + if [[ -n "$ORIGINAL_DOTFILES" ]]; then + export DOTFILES="$ORIGINAL_DOTFILES" + else + unset DOTFILES + fi + + if [[ -n "$ORIGINAL_HOME" ]]; then + export HOME="$ORIGINAL_HOME" + else + unset HOME + fi + + # Clean up temporary directory + if [[ -n "${TEST_TEMP_DIR:-}" && -d "$TEST_TEMP_DIR" ]]; then + rm -rf "$TEST_TEMP_DIR" + fi +} + +@test "setup.bash exists and is executable" { + # Check if setup.bash exists in the real dotfiles location + [ -f "$(pwd)/setup.bash" ] + [ -x "$(pwd)/setup.bash" ] +} + +@test "setup.bash sets DOTFILES environment variable" { + # Clear DOTFILES to test that setup.bash sets it + unset DOTFILES + + # Run setup.bash in a way that exports variables + source "$(pwd)/setup.bash" + + # Check that DOTFILES is set correctly + [ -n "$DOTFILES" ] + [[ "$DOTFILES" == "$HOME/Repos/ooloth/dotfiles" ]] +} + +@test "setup.bash enables strict error handling" { + # Create a test script that sources setup.bash and checks error handling + local test_script="$TEST_TEMP_DIR/test_error.sh" + cat > "$test_script" << 'EOF' +#!/bin/bash +source "$(pwd)/setup.bash" + +# Test that undefined variable causes error +echo "$UNDEFINED_VARIABLE" +EOF + + chmod +x "$test_script" + + # Run the test script - should fail due to undefined variable + run "$test_script" + [ "$status" -ne 0 ] +} + +@test "setup.bash shows welcome message when run directly" { + # Run setup.bash with 'n' response to avoid full installation + run bash -c "echo 'n' | $(pwd)/setup.bash" + + # Check that welcome message is shown + [[ "$output" =~ "Welcome to your new Mac!" ]] + [[ "$output" =~ "Sound good?" ]] + [[ "$output" =~ "No worries! Maybe next time." ]] + [ "$status" -eq 1 ] +} + +@test "setup.bash checks for macOS platform" { + # Create a mock uname command that returns Linux + local mock_bin="$TEST_TEMP_DIR/bin" + mkdir -p "$mock_bin" + cat > "$mock_bin/uname" << 'EOF' +#!/bin/bash +echo "Linux" +EOF + chmod +x "$mock_bin/uname" + + # Run setup.bash with mocked uname + PATH="$mock_bin:$PATH" run bash -c "echo 'y' | $(pwd)/setup.bash 2>&1 || true" + + # Check for macOS error message + [[ "$output" =~ "This script only runs on macOS" ]] +} + +@test "setup.bash pulls latest changes when dotfiles already exist" { + # Create mock git command + local mock_bin="$TEST_TEMP_DIR/bin" + mkdir -p "$mock_bin" + cat > "$mock_bin/git" << 'EOF' +#!/bin/bash +if [[ "$1" == "pull" ]]; then + echo "Already up to date." +fi +EOF + chmod +x "$mock_bin/git" + + # Create the dotfiles directory to simulate it already exists + mkdir -p "$DOTFILES" + + # Run setup.bash with mocked git, answering 'y' but then it will fail on platform check + PATH="$mock_bin:$PATH" run bash -c "echo 'y' | $(pwd)/setup.bash 2>&1 || true" + + # Check that it detected existing dotfiles + [[ "$output" =~ "Dotfiles are already installed. Pulling latest changes." ]] +} + +@test "setup.bash loads dotfiles utilities after cloning" { + # Create mock utilities that export variables to verify they were loaded + mkdir -p "$DOTFILES/bin/lib" + + # Create mock machine detection + cat > "$DOTFILES/bin/lib/machine-detection.bash" << 'EOF' +#!/usr/bin/env bash +init_machine_detection() { + export MACHINE_DETECTION_LOADED="true" +} +EOF + + # Create mock dry-run utils + cat > "$DOTFILES/bin/lib/dry-run-utils.bash" << 'EOF' +#!/usr/bin/env bash +parse_dry_run_flags() { + export DRY_RUN_UTILS_LOADED="true" +} +EOF + + # Create a test script that sources setup.bash and checks the variables + local test_script="$TEST_TEMP_DIR/test_utils.sh" + cat > "$test_script" << EOF +#!/bin/bash +# Override DOTFILES to use test location +export DOTFILES="$DOTFILES" + +# Source setup.bash (not execute) +source "$(pwd)/setup.bash" + +# Check if utilities would be loaded if main ran +if [[ -f "\$DOTFILES/bin/lib/machine-detection.bash" ]] && + [[ -f "\$DOTFILES/bin/lib/dry-run-utils.bash" ]]; then + echo "Utilities found" +else + echo "Utilities not found" +fi +EOF + + chmod +x "$test_script" + run "$test_script" + + [[ "$output" =~ "Utilities found" ]] +} + +@test "setup.bash runs bash installation scripts" { + # Create mock git command + local mock_bin="$TEST_TEMP_DIR/bin" + mkdir -p "$mock_bin" + cat > "$mock_bin/git" << 'EOF' +#!/bin/bash +if [[ "$1" == "pull" ]]; then + echo "Already up to date." +fi +EOF + chmod +x "$mock_bin/git" + + # Create mock installation scripts + mkdir -p "$DOTFILES/bin/install" + + # Create mock ssh.bash that sets a variable + cat > "$DOTFILES/bin/install/ssh.bash" << 'EOF' +#!/usr/bin/env bash +echo "Running SSH installation" +export SSH_INSTALL_RAN="true" +EOF + chmod +x "$DOTFILES/bin/install/ssh.bash" + + # Create mock github.bash + cat > "$DOTFILES/bin/install/github.bash" << 'EOF' +#!/usr/bin/env bash +echo "Running GitHub installation" +export GITHUB_INSTALL_RAN="true" +EOF + chmod +x "$DOTFILES/bin/install/github.bash" + + # Create minimal mock utilities to avoid errors + mkdir -p "$DOTFILES/bin/lib" + echo 'init_machine_detection() { :; }' > "$DOTFILES/bin/lib/machine-detection.bash" + echo 'parse_dry_run_flags() { :; }' > "$DOTFILES/bin/lib/dry-run-utils.bash" + echo '' > "$DOTFILES/bin/lib/error-handling.bash" + echo 'run_prerequisite_validation() { return 0; }' > "$DOTFILES/bin/lib/prerequisite-validation.bash" + + # Run setup.bash with mocked git + PATH="$mock_bin:$PATH" run bash -c "echo 'y' | $(pwd)/setup.bash 2>&1 || true" + + # Check that installation scripts were mentioned/run + [[ "$output" =~ "Running installations" ]] +} \ No newline at end of file