From a7359a4d2c6d04956774ad2f9d5986bcdfbfd1b5 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 17 Dec 2025 01:46:37 +0000 Subject: [PATCH 1/2] Initial plan From 38814c114f12ce643e2d38d74daef942316b79ef Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 17 Dec 2025 01:55:14 +0000 Subject: [PATCH 2/2] fix: apply shfmt formatting with tabs to all shell scripts Co-authored-by: andrewmcodes <18423853+andrewmcodes@users.noreply.github.com> --- install/brewfile.sh | 210 ++++++++++++++++---------------- install/chezmoi.sh | 190 ++++++++++++++--------------- install/homebrew.sh | 110 ++++++++--------- install/mise.sh | 198 +++++++++++++++---------------- scripts/bootstrap.sh | 172 +++++++++++++-------------- scripts/coverage.sh | 106 ++++++++--------- scripts/lib/common.sh | 86 +++++++------- scripts/lib/detect.sh | 68 +++++------ scripts/lib/validation.sh | 244 +++++++++++++++++++------------------- 9 files changed, 692 insertions(+), 692 deletions(-) diff --git a/install/brewfile.sh b/install/brewfile.sh index 5545039..b272f69 100755 --- a/install/brewfile.sh +++ b/install/brewfile.sh @@ -17,137 +17,137 @@ BREWFILE_PATH="${BREWFILE_PATH:-${HOME}/.local/share/chezmoi/Brewfile}" # Check if Brewfile exists is_brewfile_present() { - [[ -f "$BREWFILE_PATH" ]] + [[ -f "$BREWFILE_PATH" ]] } # Validate Brewfile syntax validate_brewfile() { - local brewfile="${1:-$BREWFILE_PATH}" + local brewfile="${1:-$BREWFILE_PATH}" - log_info "Validating Brewfile at: $brewfile" + log_info "Validating Brewfile at: $brewfile" - if [[ ! -f "$brewfile" ]]; then - log_error "Brewfile not found: $brewfile" - return 1 - fi + if [[ ! -f "$brewfile" ]]; then + log_error "Brewfile not found: $brewfile" + return 1 + fi - # Basic validation: check for common Homebrew keywords - if ! grep -qE "^(tap|brew|cask|mas)" "$brewfile"; then - log_warning "Brewfile may be empty or malformed" - fi + # Basic validation: check for common Homebrew keywords + if ! grep -qE "^(tap|brew|cask|mas)" "$brewfile"; then + log_warning "Brewfile may be empty or malformed" + fi - log_success "Brewfile validation passed" + log_success "Brewfile validation passed" } # Install packages from Brewfile install_from_brewfile() { - local brewfile="${1:-$BREWFILE_PATH}" - - log_info "Installing from Brewfile: $brewfile" - - if ! command -v brew >/dev/null 2>&1; then - log_error "Homebrew not installed" - return 1 - fi - - if [[ ! -f "$brewfile" ]]; then - log_error "Brewfile not found: $brewfile" - return 1 - fi - - if brew bundle --file="$brewfile"; then - log_success "Brewfile installation complete" - return 0 - else - log_error "brew bundle failed" - return 1 - fi + local brewfile="${1:-$BREWFILE_PATH}" + + log_info "Installing from Brewfile: $brewfile" + + if ! command -v brew >/dev/null 2>&1; then + log_error "Homebrew not installed" + return 1 + fi + + if [[ ! -f "$brewfile" ]]; then + log_error "Brewfile not found: $brewfile" + return 1 + fi + + if brew bundle --file="$brewfile"; then + log_success "Brewfile installation complete" + return 0 + else + log_error "brew bundle failed" + return 1 + fi } # Dump current packages to Brewfile dump_brewfile() { - local output_file="${1:-$BREWFILE_PATH}" - - log_info "Dumping current Homebrew packages to: $output_file" - - if ! command -v brew >/dev/null 2>&1; then - log_error "Homebrew not installed" - return 1 - fi - - # Create backup if file exists - if [[ -f "$output_file" ]]; then - local backup_file - backup_file="${output_file}.backup.$(date +%Y%m%d-%H%M%S)" - cp "$output_file" "$backup_file" - log_info "Backed up existing Brewfile to: $backup_file" - fi - - if brew bundle dump --force --file="$output_file"; then - log_success "Brewfile dumped successfully" - return 0 - else - log_error "brew bundle dump failed" - return 1 - fi + local output_file="${1:-$BREWFILE_PATH}" + + log_info "Dumping current Homebrew packages to: $output_file" + + if ! command -v brew >/dev/null 2>&1; then + log_error "Homebrew not installed" + return 1 + fi + + # Create backup if file exists + if [[ -f "$output_file" ]]; then + local backup_file + backup_file="${output_file}.backup.$(date +%Y%m%d-%H%M%S)" + cp "$output_file" "$backup_file" + log_info "Backed up existing Brewfile to: $backup_file" + fi + + if brew bundle dump --force --file="$output_file"; then + log_success "Brewfile dumped successfully" + return 0 + else + log_error "brew bundle dump failed" + return 1 + fi } # Cleanup unused packages cleanup_brewfile() { - local brewfile="${1:-$BREWFILE_PATH}" - - log_info "Cleaning up unused Homebrew packages..." - - if ! command -v brew >/dev/null 2>&1; then - log_error "Homebrew not installed" - return 1 - fi - - if [[ ! -f "$brewfile" ]]; then - log_error "Brewfile not found: $brewfile" - return 1 - fi - - if brew bundle cleanup --file="$brewfile" --force; then - log_success "Brewfile cleanup complete" - return 0 - else - log_warning "Cleanup had issues (may be expected)" - return 1 - fi + local brewfile="${1:-$BREWFILE_PATH}" + + log_info "Cleaning up unused Homebrew packages..." + + if ! command -v brew >/dev/null 2>&1; then + log_error "Homebrew not installed" + return 1 + fi + + if [[ ! -f "$brewfile" ]]; then + log_error "Brewfile not found: $brewfile" + return 1 + fi + + if brew bundle cleanup --file="$brewfile" --force; then + log_success "Brewfile cleanup complete" + return 0 + else + log_warning "Cleanup had issues (may be expected)" + return 1 + fi } # Main entry point main() { - log_header "Brewfile Management" - - local action="${1:-install}" - - case "$action" in - install) - validate_brewfile - install_from_brewfile - ;; - dump) - dump_brewfile - ;; - cleanup) - cleanup_brewfile - ;; - validate) - validate_brewfile - ;; - *) - log_error "Unknown action: $action" - log_info "Usage: $0 {install|dump|cleanup|validate}" - return 1 - ;; - esac - - log_success "Brewfile operation complete" + log_header "Brewfile Management" + + local action="${1:-install}" + + case "$action" in + install) + validate_brewfile + install_from_brewfile + ;; + dump) + dump_brewfile + ;; + cleanup) + cleanup_brewfile + ;; + validate) + validate_brewfile + ;; + *) + log_error "Unknown action: $action" + log_info "Usage: $0 {install|dump|cleanup|validate}" + return 1 + ;; + esac + + log_success "Brewfile operation complete" } # Allow sourcing for tests if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then - main "$@" + main "$@" fi diff --git a/install/chezmoi.sh b/install/chezmoi.sh index 33e7ec4..852adfa 100755 --- a/install/chezmoi.sh +++ b/install/chezmoi.sh @@ -18,141 +18,141 @@ DOTFILES_REPO="${DOTFILES_REPO:-andrewmcodes/dotfiles}" # Check if chezmoi is installed is_chezmoi_installed() { - command -v chezmoi >/dev/null 2>&1 + command -v chezmoi >/dev/null 2>&1 } # Get chezmoi version get_chezmoi_version() { - if is_chezmoi_installed; then - chezmoi --version | awk '{print $3}' - else - echo "not installed" - fi + if is_chezmoi_installed; then + chezmoi --version | awk '{print $3}' + else + echo "not installed" + fi } # Install chezmoi via Homebrew install_chezmoi_via_homebrew() { - log_info "Installing chezmoi via Homebrew..." - - if ! command -v brew >/dev/null 2>&1; then - log_error "Homebrew not found" - return 1 - fi - - if brew install chezmoi; then - log_success "Chezmoi installed via Homebrew" - return 0 - else - log_error "Failed to install chezmoi via Homebrew" - return 1 - fi + log_info "Installing chezmoi via Homebrew..." + + if ! command -v brew >/dev/null 2>&1; then + log_error "Homebrew not found" + return 1 + fi + + if brew install chezmoi; then + log_success "Chezmoi installed via Homebrew" + return 0 + else + log_error "Failed to install chezmoi via Homebrew" + return 1 + fi } # Install chezmoi via curl install_chezmoi_via_curl() { - log_info "Installing chezmoi via curl installer..." - - if sh -c "$(curl -fsLS ${CHEZMOI_INSTALL_URL})"; then - # Add to PATH for current session - export PATH="${HOME}/.local/bin:${PATH}" - log_success "Chezmoi installed via curl" - return 0 - else - log_error "Failed to install chezmoi via curl" - return 1 - fi + log_info "Installing chezmoi via curl installer..." + + if sh -c "$(curl -fsLS ${CHEZMOI_INSTALL_URL})"; then + # Add to PATH for current session + export PATH="${HOME}/.local/bin:${PATH}" + log_success "Chezmoi installed via curl" + return 0 + else + log_error "Failed to install chezmoi via curl" + return 1 + fi } # Install chezmoi (tries Homebrew first on macOS, falls back to curl) install_chezmoi() { - if is_chezmoi_installed; then - log_success "Chezmoi already installed ($(get_chezmoi_version))" - return 0 - fi - - # Try Homebrew first on macOS, fall back to curl - if is_macos && command -v brew >/dev/null 2>&1; then - install_chezmoi_via_homebrew || install_chezmoi_via_curl - else - install_chezmoi_via_curl - fi - - if is_chezmoi_installed; then - log_success "Chezmoi installation complete ($(get_chezmoi_version))" - return 0 - else - log_error "Chezmoi installation failed" - return 1 - fi + if is_chezmoi_installed; then + log_success "Chezmoi already installed ($(get_chezmoi_version))" + return 0 + fi + + # Try Homebrew first on macOS, fall back to curl + if is_macos && command -v brew >/dev/null 2>&1; then + install_chezmoi_via_homebrew || install_chezmoi_via_curl + else + install_chezmoi_via_curl + fi + + if is_chezmoi_installed; then + log_success "Chezmoi installation complete ($(get_chezmoi_version))" + return 0 + else + log_error "Chezmoi installation failed" + return 1 + fi } # Initialize chezmoi with repository init_chezmoi() { - local repo="${1:-$DOTFILES_REPO}" - - log_info "Initializing chezmoi with repo: $repo" - - if [[ -d "${HOME}/.local/share/chezmoi/.git" ]]; then - log_success "Chezmoi already initialized" - return 0 - fi - - if chezmoi init "$repo"; then - log_success "Chezmoi initialized" - return 0 - else - log_error "Failed to initialize chezmoi" - return 1 - fi + local repo="${1:-$DOTFILES_REPO}" + + log_info "Initializing chezmoi with repo: $repo" + + if [[ -d "${HOME}/.local/share/chezmoi/.git" ]]; then + log_success "Chezmoi already initialized" + return 0 + fi + + if chezmoi init "$repo"; then + log_success "Chezmoi initialized" + return 0 + else + log_error "Failed to initialize chezmoi" + return 1 + fi } # Apply chezmoi dotfiles apply_chezmoi() { - log_info "Applying chezmoi dotfiles..." - - log_info "Showing changes..." - chezmoi diff || log_info "Chezmoi diff completed" - - log_info "Applying changes..." - if chezmoi apply; then - log_success "Chezmoi dotfiles applied" - return 0 - else - log_error "Failed to apply chezmoi dotfiles" - return 1 - fi + log_info "Applying chezmoi dotfiles..." + + log_info "Showing changes..." + chezmoi diff || log_info "Chezmoi diff completed" + + log_info "Applying changes..." + if chezmoi apply; then + log_success "Chezmoi dotfiles applied" + return 0 + else + log_error "Failed to apply chezmoi dotfiles" + return 1 + fi } # Verify chezmoi setup verify_chezmoi() { - log_info "Verifying chezmoi setup..." + log_info "Verifying chezmoi setup..." - if [[ ! -d "${HOME}/.local/share/chezmoi" ]]; then - log_error "Chezmoi source directory not found" - return 1 - fi + if [[ ! -d "${HOME}/.local/share/chezmoi" ]]; then + log_error "Chezmoi source directory not found" + return 1 + fi - log_info "Checking chezmoi status..." - chezmoi status || log_info "Chezmoi status check complete" + log_info "Checking chezmoi status..." + chezmoi status || log_info "Chezmoi status check complete" - log_success "Chezmoi verification complete" + log_success "Chezmoi verification complete" } # Main entry point main() { - log_header "Chezmoi Installation" + log_header "Chezmoi Installation" - local repo="${1:-$DOTFILES_REPO}" + local repo="${1:-$DOTFILES_REPO}" - install_chezmoi - init_chezmoi "$repo" - apply_chezmoi - verify_chezmoi + install_chezmoi + init_chezmoi "$repo" + apply_chezmoi + verify_chezmoi - log_success "Chezmoi setup complete" + log_success "Chezmoi setup complete" } # Allow sourcing for tests if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then - main "$@" + main "$@" fi diff --git a/install/homebrew.sh b/install/homebrew.sh index fe089f1..b9ae6c3 100755 --- a/install/homebrew.sh +++ b/install/homebrew.sh @@ -17,85 +17,85 @@ HOMEBREW_INSTALL_URL="https://raw.githubusercontent.com/Homebrew/install/HEAD/in # Check if Homebrew is installed is_homebrew_installed() { - command -v brew >/dev/null 2>&1 + command -v brew >/dev/null 2>&1 } # Get Homebrew prefix based on architecture get_homebrew_prefix() { - if [[ "$(uname -m)" == "arm64" ]]; then - echo "/opt/homebrew" - else - echo "/usr/local" - fi + if [[ "$(uname -m)" == "arm64" ]]; then + echo "/opt/homebrew" + else + echo "/usr/local" + fi } # Install Homebrew install_homebrew() { - local install_url="${1:-$HOMEBREW_INSTALL_URL}" - - log_info "Installing Homebrew..." - - if is_homebrew_installed; then - log_success "Homebrew already installed at $(command -v brew)" - return 0 - fi - - if ! is_macos && ! is_linux; then - log_error "Homebrew only supports macOS and Linux" - return 1 - fi - - log_info "Downloading Homebrew installer..." - NONINTERACTIVE=1 /bin/bash -c "$(curl -fsSL "$install_url")" - - # Add to PATH for current session - local prefix - prefix="$(get_homebrew_prefix)" - export PATH="${prefix}/bin:${PATH}" - - if is_homebrew_installed; then - log_success "Homebrew installed successfully" - log_info "Homebrew prefix: ${prefix}" - return 0 - else - log_error "Homebrew installation failed" - return 1 - fi + local install_url="${1:-$HOMEBREW_INSTALL_URL}" + + log_info "Installing Homebrew..." + + if is_homebrew_installed; then + log_success "Homebrew already installed at $(command -v brew)" + return 0 + fi + + if ! is_macos && ! is_linux; then + log_error "Homebrew only supports macOS and Linux" + return 1 + fi + + log_info "Downloading Homebrew installer..." + NONINTERACTIVE=1 /bin/bash -c "$(curl -fsSL "$install_url")" + + # Add to PATH for current session + local prefix + prefix="$(get_homebrew_prefix)" + export PATH="${prefix}/bin:${PATH}" + + if is_homebrew_installed; then + log_success "Homebrew installed successfully" + log_info "Homebrew prefix: ${prefix}" + return 0 + else + log_error "Homebrew installation failed" + return 1 + fi } # Verify Homebrew installation verify_homebrew() { - log_info "Verifying Homebrew installation..." + log_info "Verifying Homebrew installation..." - if ! is_homebrew_installed; then - log_error "Homebrew not found in PATH" - return 1 - fi + if ! is_homebrew_installed; then + log_error "Homebrew not found in PATH" + return 1 + fi - log_info "Running: brew --version" - brew --version + log_info "Running: brew --version" + brew --version - log_info "Running: brew doctor" - if brew doctor; then - log_success "brew doctor passed" - else - log_warning "brew doctor reported issues (may be non-critical)" - fi + log_info "Running: brew doctor" + if brew doctor; then + log_success "brew doctor passed" + else + log_warning "brew doctor reported issues (may be non-critical)" + fi - log_success "Homebrew verification complete" + log_success "Homebrew verification complete" } # Main entry point main() { - log_header "Homebrew Installation" + log_header "Homebrew Installation" - install_homebrew - verify_homebrew + install_homebrew + verify_homebrew - log_success "Homebrew setup complete" + log_success "Homebrew setup complete" } # Allow sourcing for tests if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then - main "$@" + main "$@" fi diff --git a/install/mise.sh b/install/mise.sh index b529bd0..20bd377 100755 --- a/install/mise.sh +++ b/install/mise.sh @@ -19,143 +19,143 @@ TOOL_VERSIONS_PATH="${HOME}/.tool-versions" # Check if mise is installed is_mise_installed() { - command -v mise >/dev/null 2>&1 + command -v mise >/dev/null 2>&1 } # Get mise version get_mise_version() { - if is_mise_installed; then - mise --version | awk '{print $2}' - else - echo "not installed" - fi + if is_mise_installed; then + mise --version | awk '{print $2}' + else + echo "not installed" + fi } # Install mise via Homebrew install_mise_via_homebrew() { - log_info "Installing mise via Homebrew..." - - if ! command -v brew >/dev/null 2>&1; then - log_error "Homebrew not found. Install Homebrew first." - return 1 - fi - - if brew install mise; then - log_success "Mise installed via Homebrew" - return 0 - else - log_error "Failed to install mise via Homebrew" - return 1 - fi + log_info "Installing mise via Homebrew..." + + if ! command -v brew >/dev/null 2>&1; then + log_error "Homebrew not found. Install Homebrew first." + return 1 + fi + + if brew install mise; then + log_success "Mise installed via Homebrew" + return 0 + else + log_error "Failed to install mise via Homebrew" + return 1 + fi } # Install mise via curl install_mise_via_curl() { - log_info "Installing mise via curl installer..." - - if curl -fsSL "${MISE_INSTALL_URL}" | sh; then - # Add to PATH for current session - export PATH="${HOME}/.local/bin:${PATH}" - log_success "Mise installed via curl" - return 0 - else - log_error "Failed to install mise via curl" - return 1 - fi + log_info "Installing mise via curl installer..." + + if curl -fsSL "${MISE_INSTALL_URL}" | sh; then + # Add to PATH for current session + export PATH="${HOME}/.local/bin:${PATH}" + log_success "Mise installed via curl" + return 0 + else + log_error "Failed to install mise via curl" + return 1 + fi } # Install mise (tries Homebrew first on macOS, falls back to curl) install_mise() { - if is_mise_installed; then - log_success "Mise already installed ($(get_mise_version))" - return 0 - fi - - # Try Homebrew first on macOS, fall back to curl - if is_macos && command -v brew >/dev/null 2>&1; then - install_mise_via_homebrew || install_mise_via_curl - else - install_mise_via_curl - fi - - if is_mise_installed; then - log_success "Mise installation complete ($(get_mise_version))" - return 0 - else - log_error "Mise installation failed" - return 1 - fi + if is_mise_installed; then + log_success "Mise already installed ($(get_mise_version))" + return 0 + fi + + # Try Homebrew first on macOS, fall back to curl + if is_macos && command -v brew >/dev/null 2>&1; then + install_mise_via_homebrew || install_mise_via_curl + else + install_mise_via_curl + fi + + if is_mise_installed; then + log_success "Mise installation complete ($(get_mise_version))" + return 0 + else + log_error "Mise installation failed" + return 1 + fi } # Activate mise (configure shell integration) activate_mise() { - log_info "Activating mise..." + log_info "Activating mise..." - # This function adds mise to shell config if needed - # For testing, we just verify it's in PATH - if ! is_mise_installed; then - log_error "Mise not found in PATH" - return 1 - fi + # This function adds mise to shell config if needed + # For testing, we just verify it's in PATH + if ! is_mise_installed; then + log_error "Mise not found in PATH" + return 1 + fi - log_success "Mise activated" + log_success "Mise activated" } # Install mise tools from configuration install_mise_tools() { - local config_path="${1:-$MISE_CONFIG_PATH}" - - log_info "Installing mise tools from config..." - - if [[ ! -f "$config_path" ]]; then - log_warning "Mise config not found at: $config_path" - log_info "Checking for .tool-versions..." - - if [[ -f "$TOOL_VERSIONS_PATH" ]]; then - config_path="$TOOL_VERSIONS_PATH" - log_info "Using .tool-versions instead" - else - log_error "No mise config or .tool-versions found" - return 1 - fi - fi - - log_info "Installing tools from: $config_path" - if mise install; then - log_success "Mise tools installed" - return 0 - else - log_error "Failed to install some mise tools" - return 1 - fi + local config_path="${1:-$MISE_CONFIG_PATH}" + + log_info "Installing mise tools from config..." + + if [[ ! -f "$config_path" ]]; then + log_warning "Mise config not found at: $config_path" + log_info "Checking for .tool-versions..." + + if [[ -f "$TOOL_VERSIONS_PATH" ]]; then + config_path="$TOOL_VERSIONS_PATH" + log_info "Using .tool-versions instead" + else + log_error "No mise config or .tool-versions found" + return 1 + fi + fi + + log_info "Installing tools from: $config_path" + if mise install; then + log_success "Mise tools installed" + return 0 + else + log_error "Failed to install some mise tools" + return 1 + fi } # Verify mise tools installation verify_mise_tools() { - log_info "Verifying installed tools..." - - if mise list; then - log_success "Mise tools verification complete" - return 0 - else - log_warning "Could not list mise tools" - return 1 - fi + log_info "Verifying installed tools..." + + if mise list; then + log_success "Mise tools verification complete" + return 0 + else + log_warning "Could not list mise tools" + return 1 + fi } # Main entry point main() { - log_header "Mise Installation" + log_header "Mise Installation" - install_mise - activate_mise - install_mise_tools - verify_mise_tools + install_mise + activate_mise + install_mise_tools + verify_mise_tools - log_success "Mise setup complete" + log_success "Mise setup complete" } # Allow sourcing for tests if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then - main "$@" + main "$@" fi diff --git a/scripts/bootstrap.sh b/scripts/bootstrap.sh index a29a4e3..fe7f1c7 100755 --- a/scripts/bootstrap.sh +++ b/scripts/bootstrap.sh @@ -16,7 +16,7 @@ source "${SCRIPT_DIR}/lib/detect.sh" # Show help message show_help() { - cat </dev/null 2>&1; then - log_error "kcov not installed" - log_info "" - log_info "Install kcov with:" - if [[ "$OSTYPE" == "darwin"* ]]; then - log_info " brew install kcov" - else - log_info " sudo apt-get install kcov" - fi - return 1 - fi + if ! command -v kcov >/dev/null 2>&1; then + log_error "kcov not installed" + log_info "" + log_info "Install kcov with:" + if [[ "$OSTYPE" == "darwin"* ]]; then + log_info " brew install kcov" + else + log_info " sudo apt-get install kcov" + fi + return 1 + fi } # Generate coverage for all tests generate_coverage() { - log_header "Generating Code Coverage" - - # Check for kcov - if ! check_kcov; then - return 1 - fi - - # Clean coverage directory - log_info "Cleaning coverage directory..." - rm -rf "$COVERAGE_DIR" - mkdir -p "$COVERAGE_DIR" - - log_info "Running tests with coverage..." - - # Find all bats test files in unit tests - local test_count=0 - while IFS= read -r test_file; do - ((test_count++)) - log_info "[$test_count] Running: $(basename "$test_file")" - - # Run kcov with test file - kcov \ - --exclude-pattern=/usr/include,/tmp,/opt/homebrew \ - "$COVERAGE_DIR/$(basename "$test_file" .bats)" \ - "$test_file" || log_warning "Test failed: $test_file" - done < <(find "${REPO_ROOT}/tests/unit" -name "*.bats" -type f) - - log_info "" - log_success "Coverage generation complete!" - log_info "Coverage report: $COVERAGE_DIR/index.html" - - # Open report in browser on macOS - if [[ "$OSTYPE" == "darwin"* ]]; then - log_info "Opening coverage report in browser..." - open "$COVERAGE_DIR/index.html" 2>/dev/null || true - fi + log_header "Generating Code Coverage" + + # Check for kcov + if ! check_kcov; then + return 1 + fi + + # Clean coverage directory + log_info "Cleaning coverage directory..." + rm -rf "$COVERAGE_DIR" + mkdir -p "$COVERAGE_DIR" + + log_info "Running tests with coverage..." + + # Find all bats test files in unit tests + local test_count=0 + while IFS= read -r test_file; do + ((test_count++)) + log_info "[$test_count] Running: $(basename "$test_file")" + + # Run kcov with test file + kcov \ + --exclude-pattern=/usr/include,/tmp,/opt/homebrew \ + "$COVERAGE_DIR/$(basename "$test_file" .bats)" \ + "$test_file" || log_warning "Test failed: $test_file" + done < <(find "${REPO_ROOT}/tests/unit" -name "*.bats" -type f) + + log_info "" + log_success "Coverage generation complete!" + log_info "Coverage report: $COVERAGE_DIR/index.html" + + # Open report in browser on macOS + if [[ "$OSTYPE" == "darwin"* ]]; then + log_info "Opening coverage report in browser..." + open "$COVERAGE_DIR/index.html" 2>/dev/null || true + fi } # Show help show_help() { - cat <&2 + echo -e "${COLOR_YELLOW}[WARNING]${COLOR_RESET} $*" >&2 } log_error() { - echo -e "${COLOR_RED}[ERROR]${COLOR_RESET} $*" >&2 + echo -e "${COLOR_RED}[ERROR]${COLOR_RESET} $*" >&2 } log_header() { - local header="$*" - local separator="═══════════════════════════════════════" - echo - echo -e "${COLOR_BLUE}${separator}${COLOR_RESET}" - echo -e "${COLOR_BLUE} $header${COLOR_RESET}" - echo -e "${COLOR_BLUE}${separator}${COLOR_RESET}" - echo + local header="$*" + local separator="═══════════════════════════════════════" + echo + echo -e "${COLOR_BLUE}${separator}${COLOR_RESET}" + echo -e "${COLOR_BLUE} $header${COLOR_RESET}" + echo -e "${COLOR_BLUE}${separator}${COLOR_RESET}" + echo } # Error handling die() { - log_error "$@" - exit 1 + log_error "$@" + exit 1 } # Command checking require_command() { - local cmd="$1" - local install_msg="${2:-Install $cmd and try again}" + local cmd="$1" + local install_msg="${2:-Install $cmd and try again}" - if ! command -v "$cmd" >/dev/null 2>&1; then - die "$cmd is required. $install_msg" - fi + if ! command -v "$cmd" >/dev/null 2>&1; then + die "$cmd is required. $install_msg" + fi } # Confirmation prompts confirm() { - local prompt="${1:-Are you sure?}" - local default="${2:-n}" + local prompt="${1:-Are you sure?}" + local default="${2:-n}" - local yn - while true; do - read -rp "$prompt [y/N] " yn - yn="${yn:-$default}" - case "$yn" in - [Yy]*) return 0 ;; - [Nn]*) return 1 ;; - *) echo "Please answer yes or no." ;; - esac - done + local yn + while true; do + read -rp "$prompt [y/N] " yn + yn="${yn:-$default}" + case "$yn" in + [Yy]*) return 0 ;; + [Nn]*) return 1 ;; + *) echo "Please answer yes or no." ;; + esac + done } # File operations backup_file() { - local file="$1" - if [[ -f "$file" ]]; then - local backup - backup="${file}.backup.$(date +%Y%m%d-%H%M%S)" - cp "$file" "$backup" - log_info "Backed up: $file -> $backup" - fi + local file="$1" + if [[ -f "$file" ]]; then + local backup + backup="${file}.backup.$(date +%Y%m%d-%H%M%S)" + cp "$file" "$backup" + log_info "Backed up: $file -> $backup" + fi } # Network utilities check_internet() { - if ! ping -c 1 -W 2 8.8.8.8 >/dev/null 2>&1; then - return 1 - fi - return 0 + if ! ping -c 1 -W 2 8.8.8.8 >/dev/null 2>&1; then + return 1 + fi + return 0 } # Temporary directory management create_temp_dir() { - mktemp -d -t dotfiles.XXXXXX + mktemp -d -t dotfiles.XXXXXX } diff --git a/scripts/lib/detect.sh b/scripts/lib/detect.sh index 718a6d1..c250262 100644 --- a/scripts/lib/detect.sh +++ b/scripts/lib/detect.sh @@ -4,7 +4,7 @@ # Prevent double-sourcing if [[ -n "${_DETECT_SH_LOADED:-}" ]]; then - return 0 + return 0 fi readonly _DETECT_SH_LOADED=1 @@ -15,85 +15,85 @@ source "${SCRIPT_DIR}/common.sh" # OS detection is_macos() { - [[ "$OSTYPE" == "darwin"* ]] + [[ "$OSTYPE" == "darwin"* ]] } is_linux() { - [[ "$OSTYPE" == "linux-gnu"* ]] + [[ "$OSTYPE" == "linux-gnu"* ]] } is_wsl() { - [[ -f /proc/version ]] && grep -qi microsoft /proc/version + [[ -f /proc/version ]] && grep -qi microsoft /proc/version } get_os_type() { - if is_macos; then - echo "macos" - elif is_wsl; then - echo "wsl" - elif is_linux; then - echo "linux" - else - echo "unknown" - fi + if is_macos; then + echo "macos" + elif is_wsl; then + echo "wsl" + elif is_linux; then + echo "linux" + else + echo "unknown" + fi } # Architecture detection get_arch() { - uname -m + uname -m } is_arm64() { - [[ "$(get_arch)" == "arm64" ]] || [[ "$(get_arch)" == "aarch64" ]] + [[ "$(get_arch)" == "arm64" ]] || [[ "$(get_arch)" == "aarch64" ]] } is_x86_64() { - [[ "$(get_arch)" == "x86_64" ]] + [[ "$(get_arch)" == "x86_64" ]] } # macOS version detection get_macos_version() { - if is_macos; then - sw_vers -productVersion - else - echo "N/A" - fi + if is_macos; then + sw_vers -productVersion + else + echo "N/A" + fi } # Shell detection get_current_shell() { - basename "$SHELL" + basename "$SHELL" } is_zsh() { - [[ "$(get_current_shell)" == "zsh" ]] + [[ "$(get_current_shell)" == "zsh" ]] } is_bash() { - [[ "$(get_current_shell)" == "bash" ]] + [[ "$(get_current_shell)" == "bash" ]] } # CI detection is_ci() { - [[ -n "${CI:-}" ]] || [[ -n "${GITHUB_ACTIONS:-}" ]] + [[ -n "${CI:-}" ]] || [[ -n "${GITHUB_ACTIONS:-}" ]] } is_github_actions() { - [[ -n "${GITHUB_ACTIONS:-}" ]] + [[ -n "${GITHUB_ACTIONS:-}" ]] } # User detection is_root() { - [[ "$EUID" -eq 0 ]] + [[ "$EUID" -eq 0 ]] } # Display info show_system_info() { - log_info "System Information:" - log_info " OS: $(get_os_type)" - log_info " Architecture: $(get_arch)" - log_info " Shell: $(get_current_shell)" - if is_macos; then - log_info " macOS Version: $(get_macos_version)" - fi + log_info "System Information:" + log_info " OS: $(get_os_type)" + log_info " Architecture: $(get_arch)" + log_info " Shell: $(get_current_shell)" + if is_macos; then + log_info " macOS Version: $(get_macos_version)" + fi } diff --git a/scripts/lib/validation.sh b/scripts/lib/validation.sh index 66d3781..0ba9ceb 100644 --- a/scripts/lib/validation.sh +++ b/scripts/lib/validation.sh @@ -4,7 +4,7 @@ # Prevent double-sourcing if [[ -n "${_VALIDATION_SH_LOADED:-}" ]]; then - return 0 + return 0 fi readonly _VALIDATION_SH_LOADED=1 @@ -15,157 +15,157 @@ source "${SCRIPT_DIR}/common.sh" # Mise validation validate_mise_config() { - local config_file="$1" + local config_file="$1" - if [[ ! -f "$config_file" ]]; then - log_error "Mise config not found: $config_file" - return 1 - fi + if [[ ! -f "$config_file" ]]; then + log_error "Mise config not found: $config_file" + return 1 + fi - # Check for [tools] section - if ! grep -q '^\[tools\]' "$config_file"; then - log_error "Mise config missing [tools] section" - return 1 - fi + # Check for [tools] section + if ! grep -q '^\[tools\]' "$config_file"; then + log_error "Mise config missing [tools] section" + return 1 + fi - log_success "Mise config validation passed" + log_success "Mise config validation passed" } validate_tool_versions() { - local tool_versions_file="$1" - - if [[ ! -f "$tool_versions_file" ]]; then - log_error ".tool-versions not found: $tool_versions_file" - return 1 - fi - - # Check if file is not empty - if [[ ! -s "$tool_versions_file" ]]; then - log_error ".tool-versions is empty" - return 1 - fi - - # Validate format: tool version - local line_num=0 - while IFS= read -r line; do - ((line_num++)) - # Skip empty lines and comments - [[ -z "$line" ]] || [[ "$line" =~ ^# ]] && continue - - if ! [[ "$line" =~ ^[a-zA-Z0-9_-]+[[:space:]]+[a-zA-Z0-9._-]+$ ]]; then - log_error "Invalid format at line $line_num: $line" - return 1 - fi - done <"$tool_versions_file" - - log_success ".tool-versions validation passed" + local tool_versions_file="$1" + + if [[ ! -f "$tool_versions_file" ]]; then + log_error ".tool-versions not found: $tool_versions_file" + return 1 + fi + + # Check if file is not empty + if [[ ! -s "$tool_versions_file" ]]; then + log_error ".tool-versions is empty" + return 1 + fi + + # Validate format: tool version + local line_num=0 + while IFS= read -r line; do + ((line_num++)) + # Skip empty lines and comments + [[ -z "$line" ]] || [[ "$line" =~ ^# ]] && continue + + if ! [[ "$line" =~ ^[a-zA-Z0-9_-]+[[:space:]]+[a-zA-Z0-9._-]+$ ]]; then + log_error "Invalid format at line $line_num: $line" + return 1 + fi + done <"$tool_versions_file" + + log_success ".tool-versions validation passed" } # Git config validation validate_git_config() { - local git_config="$1" - - if [[ ! -f "$git_config" ]]; then - log_error "Git config not found: $git_config" - return 1 - fi - - # Check for required sections - local required_sections=("user" "core") - for section in "${required_sections[@]}"; do - if ! grep -q "^\[$section\]" "$git_config"; then - log_error "Git config missing [$section] section" - return 1 - fi - done - - log_success "Git config validation passed" + local git_config="$1" + + if [[ ! -f "$git_config" ]]; then + log_error "Git config not found: $git_config" + return 1 + fi + + # Check for required sections + local required_sections=("user" "core") + for section in "${required_sections[@]}"; do + if ! grep -q "^\[$section\]" "$git_config"; then + log_error "Git config missing [$section] section" + return 1 + fi + done + + log_success "Git config validation passed" } # Shell config validation validate_shell_config() { - local shell_config="$1" - - if [[ ! -f "$shell_config" ]]; then - log_error "Shell config not found: $shell_config" - return 1 - fi - - # Check for syntax errors (basic) - if bash -n "$shell_config" 2>/dev/null; then - log_success "Shell config syntax valid" - else - log_error "Shell config has syntax errors" - return 1 - fi + local shell_config="$1" + + if [[ ! -f "$shell_config" ]]; then + log_error "Shell config not found: $shell_config" + return 1 + fi + + # Check for syntax errors (basic) + if bash -n "$shell_config" 2>/dev/null; then + log_success "Shell config syntax valid" + else + log_error "Shell config has syntax errors" + return 1 + fi } # Brewfile validation validate_brewfile() { - local brewfile="$1" + local brewfile="$1" - if [[ ! -f "$brewfile" ]]; then - log_error "Brewfile not found: $brewfile" - return 1 - fi + if [[ ! -f "$brewfile" ]]; then + log_error "Brewfile not found: $brewfile" + return 1 + fi - # Check for valid Homebrew commands - if ! grep -qE "^(tap|brew|cask|mas)" "$brewfile"; then - log_warning "Brewfile may be empty or malformed" - fi + # Check for valid Homebrew commands + if ! grep -qE "^(tap|brew|cask|mas)" "$brewfile"; then + log_warning "Brewfile may be empty or malformed" + fi - log_success "Brewfile validation passed" + log_success "Brewfile validation passed" } # Chezmoi validation validate_chezmoi_dir() { - local chezmoi_dir="${1:-$HOME/.local/share/chezmoi}" + local chezmoi_dir="${1:-$HOME/.local/share/chezmoi}" - if [[ ! -d "$chezmoi_dir" ]]; then - log_error "Chezmoi directory not found: $chezmoi_dir" - return 1 - fi + if [[ ! -d "$chezmoi_dir" ]]; then + log_error "Chezmoi directory not found: $chezmoi_dir" + return 1 + fi - if [[ ! -d "$chezmoi_dir/.git" ]]; then - log_error "Chezmoi directory is not a git repository" - return 1 - fi + if [[ ! -d "$chezmoi_dir/.git" ]]; then + log_error "Chezmoi directory is not a git repository" + return 1 + fi - log_success "Chezmoi directory validation passed" + log_success "Chezmoi directory validation passed" } # Comprehensive validation validate_all_configs() { - local chezmoi_dir="${1:-$HOME/.local/share/chezmoi}" - local errors=0 - - log_header "Validating All Configurations" - - # Mise config - if [[ -f "$chezmoi_dir/dot_config/mise/config.toml" ]]; then - validate_mise_config "$chezmoi_dir/dot_config/mise/config.toml" || ((errors++)) - fi - - # Tool versions - if [[ -f "$chezmoi_dir/dot_tool-versions" ]]; then - validate_tool_versions "$chezmoi_dir/dot_tool-versions" || ((errors++)) - fi - - # Git config - if [[ -f "$chezmoi_dir/dot_config/git/config" ]]; then - validate_git_config "$chezmoi_dir/dot_config/git/config" || ((errors++)) - fi - - # Brewfile - if [[ -f "$chezmoi_dir/Brewfile" ]]; then - validate_brewfile "$chezmoi_dir/Brewfile" || ((errors++)) - fi - - if [[ $errors -eq 0 ]]; then - log_success "All configuration validations passed" - return 0 - else - log_error "Configuration validation failed with $errors error(s)" - return 1 - fi + local chezmoi_dir="${1:-$HOME/.local/share/chezmoi}" + local errors=0 + + log_header "Validating All Configurations" + + # Mise config + if [[ -f "$chezmoi_dir/dot_config/mise/config.toml" ]]; then + validate_mise_config "$chezmoi_dir/dot_config/mise/config.toml" || ((errors++)) + fi + + # Tool versions + if [[ -f "$chezmoi_dir/dot_tool-versions" ]]; then + validate_tool_versions "$chezmoi_dir/dot_tool-versions" || ((errors++)) + fi + + # Git config + if [[ -f "$chezmoi_dir/dot_config/git/config" ]]; then + validate_git_config "$chezmoi_dir/dot_config/git/config" || ((errors++)) + fi + + # Brewfile + if [[ -f "$chezmoi_dir/Brewfile" ]]; then + validate_brewfile "$chezmoi_dir/Brewfile" || ((errors++)) + fi + + if [[ $errors -eq 0 ]]; then + log_success "All configuration validations passed" + return 0 + else + log_error "Configuration validation failed with $errors error(s)" + return 1 + fi }