From 9a941d1752b3b50ccab648891b4da9711915af39 Mon Sep 17 00:00:00 2001 From: Peter Wood Date: Mon, 12 May 2025 13:59:15 -0400 Subject: [PATCH] Enhance setup and testing scripts for improved package management and logging - Updated setup.sh to check for Nala installation and provide alternative installation methods based on Ubuntu version. - Added error handling for package installation, allowing fallback to apt if Nala fails. - Introduced startup.sh to perform container startup checks, including system info and permissions for logs directory. - Created test-setup.sh to validate bootstrap and setup scripts, including detailed logging of package availability and installation results. - Implemented checks for missing packages and provided recommendations for manual installation. - Enhanced logging for better traceability of actions and errors during setup and testing processes. --- .github/copilot-instructions.md | 161 +++++++ Dockerfile | 80 ++++ README.md | 55 +++ debian-patches.sh | 225 ++++++++++ docs/testing-fixes.md | 83 ++++ docs/testing.md | 132 ++++++ dotfiles/README.md | 35 ++ run-docker-tests.sh | 237 ++++++++++ setup/setup.sh | 70 ++- startup.sh | 75 ++++ test-setup.sh | 764 ++++++++++++++++++++++++++++++++ 11 files changed, 1901 insertions(+), 16 deletions(-) create mode 100644 .github/copilot-instructions.md create mode 100644 Dockerfile create mode 100755 debian-patches.sh create mode 100644 docs/testing-fixes.md create mode 100644 docs/testing.md create mode 100755 run-docker-tests.sh create mode 100755 startup.sh create mode 100755 test-setup.sh diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md new file mode 100644 index 0000000..cffcaab --- /dev/null +++ b/.github/copilot-instructions.md @@ -0,0 +1,161 @@ +# GitHub Copilot Instructions for Shell Repository + +This document provides context and guidance for GitHub Copilot when working with this shell script and dotfiles repository. + +## Repository Overview + +This repository contains: + +1. **Shell scripts** for system administration tasks +2. **Dotfiles** for system configuration +3. **Setup scripts** for automated environment configuration +4. **Docker-based testing framework** for validating setup across environments + +## Repository Structure + +- **Root directory**: Contains various utility shell scripts +- **docs/**: Documentation for individual scripts and components +- **dotfiles/**: System configuration files that get symlinked to the user's home directory +- **powershell/**: PowerShell scripts for Windows environments +- **setup/**: Setup scripts and package lists for automated environment configuration +- **.github/**: GitHub-related configuration files + +## Key Files Overview + +### Shell Scripts + +- **bootstrap.sh**: Entry point script for automated setup +- **test-setup.sh**: Testing script for validating environment setup +- **run-docker-tests.sh**: Runner for Docker-based testing +- **update.sh**: System update scripts +- **plex.sh**: Plex Media Server management + +### Configuration Files + +- **setup/packages.list**: List of packages to install during setup +- **dotfiles/my-aliases.zsh**: Custom ZSH aliases +- **dotfiles/tailscale-acl.json**: Tailscale ACL configuration + +### Documentation + +- **README.md**: Main repository documentation +- **docs/testing.md**: Detailed documentation for the testing framework +- **dotfiles/README.md**: Documentation for dotfiles setup and usage + +## Style Guidelines + +When suggesting code or modifications: + +1. **Shell Scripts**: + - Use `#!/bin/bash` for most scripts + - Include proper error handling with `set -e` where appropriate + - For test scripts, avoid `set -e` to allow testing all components + - Add descriptive comments for script sections + - Use colors for terminal output when appropriate (GREEN, RED, YELLOW, etc.) + - Use capitalized variable names (e.g., `USER_HOME=/home/user`) + +2. **Docker Files**: + - Follow Docker best practices for layer optimization + - Use specific tags for base images rather than 'latest' + - Include proper LABEL directives for metadata + +3. **Documentation**: + - Use proper Markdown formatting + - Include code examples using appropriate syntax highlighting + - Document script parameters and usage + +## Testing Framework + +When modifying the testing framework: + +1. Make sure to test across both Ubuntu and Debian environments +2. Ensure tests continue even when individual components fail +3. Track and summarize all errors at the end of tests +4. Maintain proper error reporting and logging + +### Docker Testing Enhancements + +The Docker-based testing framework includes these key features: + +1. **Continuous Testing**: Tests continue running even when individual package installations fail + - Achieved by removing `set -e` from test scripts + - Uses a counter to track errors rather than exiting immediately + +2. **Package Testing**: + - Dynamically reads packages from `setup/packages.list` + - Tests each package individually + - Maintains an array of missing packages for final reporting + +3. **Summary Reporting**: + - Provides a comprehensive summary of all failed tests + - Suggests commands to fix missing packages + - Saves detailed logs to a timestamped file in /tmp + +4. **Cross-Platform Testing**: + - Tests both Ubuntu and Debian environments + - Handles platform-specific package names (e.g., `bat` vs `batcat`) + +## Key Concepts + +- **Shell Environment Setup**: Focuses on ZSH with Oh My Zsh and plugins +- **Docker Testing**: Validates environment setup in isolated containers +- **Dotfiles Management**: Uses symbolic links to user's home directory +- **Package Installation**: Uses apt/nala on Debian-based systems + +## Main Use Cases + +1. **Setting up a new development environment**: Using bootstrap.sh +2. **Managing media services**: Using plex.sh and related scripts +3. **System maintenance**: Using update.sh and backup scripts +4. **Testing configuration changes**: Using the Docker testing framework + +## Code Organization Principles + +1. **Modularity**: Keep scripts focused on one task +2. **Documentation**: Document all scripts and configurations +3. **Testing**: Ensure all changes are testable +4. **Cross-platform**: Support both Ubuntu and Debian where possible + +## Security Practices + +When suggesting security-related code: + +1. **Permissions**: + - Avoid running scripts as root unless necessary + - Use `sudo` for specific commands rather than entire scripts + - Set appropriate file permissions (e.g., `chmod 600` for sensitive files) + +2. **Secret Management**: + - Never include hardcoded credentials in scripts + - Use environment variables or external secret management + - Add sensitive files to .gitignore + +3. **Input Validation**: + - Validate and sanitize all user inputs + - Use quotes around variables to prevent word splitting and globbing + - Implement proper error handling for invalid inputs + +4. **Network Security**: + - Verify URLs before downloading (`curl`/`wget`) + - Use HTTPS instead of HTTP when possible + - Validate checksums for downloaded packages + +## Contribution Guidelines + +For contributors and Copilot suggestions: + +1. **Script Modifications**: + - Test all changes using the Docker testing framework + - Update documentation when adding new functionality + - Maintain backward compatibility when possible + +2. **New Scripts**: + - Follow the established naming conventions + - Include a header with description, usage, and author + - Add appropriate documentation to the docs/ directory + - Add any new dependencies to setup/packages.list + +3. **Review Process**: + - Run tests before submitting changes + - Document what was changed and why + - Consider both Ubuntu and Debian compatibility diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..575d09a --- /dev/null +++ b/Dockerfile @@ -0,0 +1,80 @@ +# Dockerfile to test bootstrap.sh and setup.sh scripts in different environments +# This allows testing the setup process in isolated containers + +# Ubuntu test environment +FROM ubuntu:24.04 as ubuntu-test +LABEL description="Ubuntu test environment for shell setup scripts" + +# Install minimal dependencies needed to run the test +ENV TZ=America/New_York +ENV DEBIAN_FRONTEND=noninteractive +ARG APT_KEY_DONT_WARN_ON_DANGEROUS_USAGE=1 + +RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone \ + && apt-get update && apt-get install -o Dpkg::Options::="--force-confdef" -o Dpkg::Options::="--force-confnew" -y curl git sudo wget + +# Pre-install cowsay and lolcat packages for testing +RUN apt-get update && apt-get install -o Dpkg::Options::="--force-confdef" -o Dpkg::Options::="--force-confnew" -y cowsay lolcat + +# Create logs directory with proper permissions +RUN mkdir -p /logs && chmod -R 777 /logs + +# Create a test user with sudo permissions +RUN useradd -ms /bin/bash testuser && \ + echo "testuser ALL=(ALL) NOPASSWD:ALL" > /etc/sudoers.d/testuser + +USER testuser +WORKDIR /home/testuser + +# Create directory structure for shell setup +RUN mkdir -p /home/testuser/shell/setup + +# Copy test script, startup script, and packages.list +COPY --chown=testuser:testuser test-setup.sh /home/testuser/ +COPY --chown=testuser:testuser startup.sh /home/testuser/ +COPY --chown=testuser:testuser setup/packages.list /home/testuser/shell/setup/ + +# Make scripts executable +RUN chmod +x /home/testuser/test-setup.sh +RUN chmod +x /home/testuser/startup.sh + +CMD ["/bin/bash", "-c", "./startup.sh"] + +# Debian test environment +FROM debian:12 as debian-test +LABEL description="Debian test environment for shell setup scripts" + +# Install minimal dependencies needed to run the test +ENV TZ=America/New_York +ENV DEBIAN_FRONTEND=noninteractive +ARG APT_KEY_DONT_WARN_ON_DANGEROUS_USAGE=1 + +RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone \ + && apt-get update && apt-get install -o Dpkg::Options::="--force-confdef" -o Dpkg::Options::="--force-confnew" -y curl git sudo wget + +# Pre-install cowsay and lolcat packages for testing +RUN apt-get update && apt-get install -o Dpkg::Options::="--force-confdef" -o Dpkg::Options::="--force-confnew" -y cowsay lolcat + +# Create logs directory with proper permissions +RUN mkdir -p /logs && chmod -R 777 /logs + +# Create a test user with sudo permissions +RUN useradd -ms /bin/bash testuser && \ + echo "testuser ALL=(ALL) NOPASSWD:ALL" > /etc/sudoers.d/testuser + +USER testuser +WORKDIR /home/testuser + +# Create directory structure for shell setup +RUN mkdir -p /home/testuser/shell/setup + +# Copy test script, startup script, and packages.list +COPY --chown=testuser:testuser test-setup.sh /home/testuser/ +COPY --chown=testuser:testuser startup.sh /home/testuser/ +COPY --chown=testuser:testuser setup/packages.list /home/testuser/shell/setup/ + +# Make scripts executable +RUN chmod +x /home/testuser/test-setup.sh +RUN chmod +x /home/testuser/startup.sh + +CMD ["/bin/bash", "-c", "./startup.sh"] diff --git a/README.md b/README.md index 72971bd..b7d147f 100644 --- a/README.md +++ b/README.md @@ -14,6 +14,7 @@ This repository contains various shell scripts for managing media-related tasks - [Plex Backup Script Documentation](./docs/plex-backup.md): Detailed documentation for the `backup-plex.sh` script. - [Plex Management Script Documentation](./docs/plex-management.md): Detailed documentation for the `plex.sh` script. - [Folder Metrics Script Documentation](./docs/folder-metrics.md): Detailed documentation for the `folder-metrics.sh` script. +- [Testing Framework Documentation](./docs/testing.md): Detailed documentation for the Docker-based testing system. ## Dotfiles @@ -25,6 +26,60 @@ curl -fsSL https://raw.githubusercontent.com/acedanger/shell/main/bootstrap.sh | For more information about the dotfiles, see [Dotfiles README](./dotfiles/README.md). +## Testing + +This repository includes Docker-based testing to validate the setup process across different environments: + +- **test-setup.sh**: Script that validates the bootstrap and setup process +- **run-docker-tests.sh**: Runner script that executes tests in Docker containers +- **Dockerfile**: Defines test environments (Ubuntu, Debian) + +### Running Tests + +Test your setup in isolated Docker containers with: + +```bash +# Test in Ubuntu container +./run-docker-tests.sh ubuntu + +# Test in Debian container +./run-docker-tests.sh debian + +# Run full bootstrap test in Ubuntu +./run-docker-tests.sh full-ubuntu + +# Run full bootstrap test in Debian +./run-docker-tests.sh full-debian + +# Test in both Ubuntu and Debian +./run-docker-tests.sh all +``` + +#### When to Use Each Testing Option + +- **Use `./run-docker-tests.sh ubuntu` or `./run-docker-tests.sh debian`** when you want to: + - Quickly validate if packages in packages.list are available and installed + - Test if your test-setup.sh script is working correctly + - Check for issues with specific components without performing a full setup + +- **Use `./run-docker-tests.sh full-ubuntu` or `./run-docker-tests.sh full-debian`** when you want to: + - Test the complete bootstrap installation process end-to-end + - Validate that all installation steps work correctly on a fresh system + - Simulate what users will experience when running the bootstrap script + +- **Use `./run-docker-tests.sh all`** when you want to: + - Ensure your test-setup.sh works across both Ubuntu and Debian + - Run comprehensive checks before committing changes + +The test environment checks: +- Package availability and installation +- Core components (git, curl, wget, etc.) +- Additional packages from `setup/packages.list` +- Oh My Zsh and plugin installation +- Dotfile symlinks + +Tests will continue even when some packages fail to install, reporting all issues in a comprehensive summary. + # plex.sh This script is used to manage the Plex Media Server service on a systemd-based Linux distribution. It provides the following functionalities: diff --git a/debian-patches.sh b/debian-patches.sh new file mode 100755 index 0000000..f1ceb88 --- /dev/null +++ b/debian-patches.sh @@ -0,0 +1,225 @@ +#!/bin/bash + +# This script applies Debian-specific patches to the setup scripts +# It should be run before bootstrap.sh on Debian systems + +set -e # Exit on error + +# Colors for output +GREEN='\033[0;32m' +YELLOW='\033[0;33m' +RED='\033[0;31m' +BLUE='\033[0;34m' +NC='\033[0m' # No Color + +# Check if running on Debian +if [ -f /etc/os-release ]; then + . /etc/os-release + if [[ "$ID" == "debian" ]]; then + echo -e "${BLUE}Running on Debian $VERSION_ID ($PRETTY_NAME)${NC}" + else + echo -e "${YELLOW}This script is intended for Debian systems but detected $PRETTY_NAME${NC}" + echo -e "${YELLOW}Continue anyway? [y/N]${NC}" + read -r response + if [[ ! "$response" =~ ^([yY][eE][sS]|[yY])$ ]]; then + echo -e "${RED}Exiting...${NC}" + exit 1 + fi + fi +fi + +# Create a packages mapping file +PATCH_DIR="$HOME/.shell-debian-patches" +mkdir -p "$PATCH_DIR" + +echo -e "${YELLOW}Creating Debian package name mappings...${NC}" +cat > "$PATCH_DIR/debian-packages.map" << 'EOF' +# Debian package name mappings +# format: ubuntu-name|debian-name + +# bat is called batcat on Debian +bat|batcat + +# Keep original names for packages that are the same +git|git +python3|python3 +wget|wget +curl|curl +cowsay|cowsay +lolcat|lolcat +fzf|fzf +zsh|zsh + +# Different package names for Nala source +software-properties-common|software-properties-common +EOF + +# Create a patching script that will be called by setup.sh +echo -e "${YELLOW}Creating Debian patching script...${NC}" +cat > "$PATCH_DIR/apply-debian-patches.sh" << 'EOF' +#!/bin/bash + +# Script to apply Debian-specific patches to the setup process + +# Colors for output +GREEN='\033[0;32m' +YELLOW='\033[0;33m' +RED='\033[0;31m' +BLUE='\033[0;34m' +NC='\033[0m' # No Color + +PATCH_DIR="$HOME/.shell-debian-patches" + +# Check if running on Debian +if [ -f /etc/os-release ]; then + . /etc/os-release + if [[ "$ID" != "debian" ]]; then + echo -e "${YELLOW}Not running on Debian, skipping patches${NC}" + exit 0 + fi +fi + +echo -e "${BLUE}Applying Debian-specific patches...${NC}" + +# Function to map package names from Ubuntu to Debian +map_package() { + local ubuntu_pkg="$1" + local debian_pkg + + # Look for the package in the mapping file + if [ -f "$PATCH_DIR/debian-packages.map" ]; then + debian_pkg=$(grep -v "^#" "$PATCH_DIR/debian-packages.map" | grep "^$ubuntu_pkg|" | cut -d'|' -f2) + fi + + # If not found or empty, use the original name + if [ -z "$debian_pkg" ]; then + debian_pkg="$ubuntu_pkg" + fi + + echo "$debian_pkg" +} + +# Patch the packages.list file if it exists +if [ -f "$HOME/shell/setup/packages.list" ]; then + echo -e "${YELLOW}Patching packages.list for Debian compatibility...${NC}" + + # Create a temporary patched file + temp_file=$(mktemp) + + # Process each line + while IFS= read -r line; do + # Skip comments and empty lines + if [[ "$line" =~ ^//.*$ ]] || [[ -z "$line" ]]; then + echo "$line" >> "$temp_file" + continue + fi + + # Map the package name + debian_pkg=$(map_package "$line") + echo "$debian_pkg" >> "$temp_file" + + done < "$HOME/shell/setup/packages.list" + + # Backup original and replace with patched version + cp "$HOME/shell/setup/packages.list" "$HOME/shell/setup/packages.list.orig" + mv "$temp_file" "$HOME/shell/setup/packages.list" + + echo -e "${GREEN}Patched packages.list for Debian compatibility${NC}" +fi + +# Other Debian-specific patches can be added here... + +# Add contrib and non-free repositories if they're not already enabled +echo -e "${YELLOW}Checking Debian repositories...${NC}" +if ! grep -q "contrib" /etc/apt/sources.list; then + echo -e "${YELLOW}Adding contrib and non-free repositories...${NC}" + # Create a backup of the original sources.list + sudo cp /etc/apt/sources.list /etc/apt/sources.list.backup + + # Add contrib and non-free to each deb line + sudo sed -i 's/main$/main contrib non-free non-free-firmware/g' /etc/apt/sources.list + + echo -e "${GREEN}Added contrib and non-free repositories${NC}" +fi + +echo -e "${GREEN}Debian patches applied successfully${NC}" +EOF + +chmod +x "$PATCH_DIR/apply-debian-patches.sh" + +# Create a wrapper script for bootstrap.sh +echo -e "${YELLOW}Creating Debian bootstrap wrapper...${NC}" +cat > "$HOME/debian-bootstrap.sh" << 'EOF' +#!/bin/bash + +# Debian wrapper for bootstrap.sh + +set -e # Exit on error + +# Colors for output +GREEN='\033[0;32m' +YELLOW='\033[0;33m' +RED='\033[0;31m' +BLUE='\033[0;34m' +NC='\033[0m' # No Color + +PATCH_DIR="$HOME/.shell-debian-patches" + +# Apply Debian patches first +if [ -x "$PATCH_DIR/apply-debian-patches.sh" ]; then + echo -e "${BLUE}Applying Debian patches before bootstrap...${NC}" + "$PATCH_DIR/apply-debian-patches.sh" +fi + +# Download and run the bootstrap script +echo -e "${BLUE}Running bootstrap script...${NC}" +curl -s https://raw.githubusercontent.com/acedanger/shell/main/bootstrap.sh | bash + +# Apply patches again after bootstrap (in case packages.list was just downloaded) +if [ -x "$PATCH_DIR/apply-debian-patches.sh" ]; then + echo -e "${BLUE}Applying Debian patches after bootstrap...${NC}" + "$PATCH_DIR/apply-debian-patches.sh" +fi + +echo -e "${GREEN}Debian bootstrap completed!${NC}" +EOF + +chmod +x "$HOME/debian-bootstrap.sh" + +# Add a hook to setup.sh to call the Debian patch script +echo -e "${YELLOW}Creating setup.sh hook for Debian...${NC}" +cat > "$PATCH_DIR/setup-hook.sh" << 'EOF' +#!/bin/bash + +# Hook to modify setup.sh to call Debian patches + +if [ -f /etc/os-release ]; then + . /etc/os-release + if [[ "$ID" == "debian" ]]; then + # Modify setup.sh to call Debian patches + if [ -f "$HOME/shell/setup/setup.sh" ]; then + # Check if the patch hasn't been applied yet + if ! grep -q "apply-debian-patches" "$HOME/shell/setup/setup.sh"; then + # Add call to Debian patch script right after the shebang + sed -i '2i\ +# Apply Debian patches if available\ +if [ -x "$HOME/.shell-debian-patches/apply-debian-patches.sh" ]; then\ + echo -e "${YELLOW}Applying Debian-specific patches...${NC}"\ + "$HOME/.shell-debian-patches/apply-debian-patches.sh"\ +fi' "$HOME/shell/setup/setup.sh" + fi + fi + fi +fi +EOF + +chmod +x "$PATCH_DIR/setup-hook.sh" + +echo -e "${GREEN}Debian compatibility patch has been set up!${NC}" +echo -e "${YELLOW}To bootstrap your Debian system, run:${NC}" +echo -e " ${BLUE}bash ~/debian-bootstrap.sh${NC}" +echo "" +echo -e "${YELLOW}This has created:${NC}" +echo -e "1. Package name mappings for Debian" +echo -e "2. A patch script for Debian-specific adjustments" +echo -e "3. A Debian-specific bootstrap wrapper" diff --git a/docs/testing-fixes.md b/docs/testing-fixes.md new file mode 100644 index 0000000..1742b26 --- /dev/null +++ b/docs/testing-fixes.md @@ -0,0 +1,83 @@ +# Docker-based Testing Framework Improvements + +This document outlines the improvements made to the Docker-based testing framework for validating shell scripts and dotfiles across different environments. + +## Issues Fixed + +### 1. `local` Keyword Usage Outside Function + +Fixed a syntax error where the `local` keyword was used outside of a function context: + +```bash +# Before (incorrect): +for pkg in $packages; do + local actual_pkg=$(get_package_name "$pkg") + # ... +done + +# After (correct): +for pkg in $packages; do + actual_pkg=$(get_package_name "$pkg") + # ... +done +``` + +### 2. Log Directory Handling + +Enhanced log directory handling to ensure proper permissions and fallback mechanisms: + +- Added better error handling for log directory creation and permissions +- Added validation to verify write permissions before proceeding +- Implemented fallback to /tmp if host volume mounting fails +- Added debugging information when log operations fail + +### 3. Package Verification + +Improved package detection, especially for packages like `cowsay` and `lolcat` that are typically installed in `/usr/games/`: + +- Enhanced `test_package()` function to check in common alternate locations +- Added specific handling for packages that may be installed with different paths +- Added detailed debugging output for problematic packages + +### 4. Docker Container Configuration + +Improved the Docker container configuration for more reliable testing: + +- Added proper volume mounting with explicit read/write permissions +- Added timestamp consistency between host and container +- Added container type labels to log files for better tracking +- Enhanced error detection for volume mounting issues + +## Implementation Details + +### 1. Enhanced Logging System + +- Timestamps are now synchronized between host and container +- Log file names include container type (ubuntu/debian) for clarity +- Added validation to confirm logs are properly saved to host + +### 2. Container Environment Setup + +- Improved `startup.sh` with better diagnostics before running tests +- Added permissions verification for mounted volumes +- Added write tests to confirm permissions are correctly set + +### 3. Test Framework Improvements + +- Improved error handling for better diagnostics +- Enhanced reporting for package detection issues +- Better isolation between test iterations + +## Running Tests + +To run tests with the improved framework: + +```bash +# Test in Ubuntu container +./run-docker-tests.sh ubuntu + +# Test in Debian container +./run-docker-tests.sh debian +``` + +The logs will be saved in the `./logs` directory with filenames that include the timestamp and container type. diff --git a/docs/testing.md b/docs/testing.md new file mode 100644 index 0000000..845a08a --- /dev/null +++ b/docs/testing.md @@ -0,0 +1,132 @@ +# Shell Setup Testing Framework + +This document describes the testing framework for validating the shell setup across different environments. + +## Overview + +The testing framework consists of three main components: + +1. **test-setup.sh**: The main test script that validates the bootstrap and setup process +2. **run-docker-tests.sh**: A runner script that executes tests in Docker containers +3. **Dockerfile**: Definition of test environments (Ubuntu and Debian) + +## Testing Features + +- **Cross-platform testing**: Test on both Ubuntu and Debian environments +- **Isolated environments**: All tests run in fresh Docker containers +- **Comprehensive validation**: Tests both the bootstrap and setup processes +- **Continuous testing**: Tests all packages regardless of individual failures +- **Detailed reporting**: Summary of all successful and failed components + +## How Testing Works + +### The Docker Test Environment + +The `Dockerfile` defines two testing environments: +- **ubuntu-test**: Based on Ubuntu 24.04 +- **debian-test**: Based on Debian 12 + +Each environment: +1. Installs minimal dependencies (curl, git, sudo, wget) +2. Creates a test user with sudo permissions +3. Sets up the directory structure for testing +4. Copies the test script and packages list +5. Runs the test script when the container starts + +### The Test Script (test-setup.sh) + +The test script validates: + +1. **Script Syntax**: Checks if bootstrap.sh and setup.sh have valid syntax +2. **Core Tools**: Verifies git, curl, wget are available +3. **Package Availability**: Checks if packages in packages.list are available in repositories +4. **Package Installation**: Tests if each package is installed +5. **Shell Setup**: Validates Oh My Zsh and plugin installation +6. **Dotfiles**: Checks if dotfiles are properly symlinked + +The script tracks all missing or misconfigured components and provides a summary at the end, including suggestions for fixing issues. + +### Test Runner (run-docker-tests.sh) + +The runner script provides several test modes: + +- **ubuntu**: Run test on Ubuntu container +- **debian**: Run test on Debian container +- **full-ubuntu**: Run full bootstrap test on Ubuntu +- **full-debian**: Run full bootstrap test on Debian +- **all**: Run tests on both Ubuntu and Debian + +## Testing Without Stopping on Failures + +A key feature is the ability to test all packages in `packages.list` without stopping at the first failure. This ensures: + +1. Complete coverage of all requirements +2. Comprehensive reporting of all issues +3. Better debugging experience when multiple components need attention + +## Running Tests + +```bash +# Test on Ubuntu +./run-docker-tests.sh ubuntu + +# Test on Debian +./run-docker-tests.sh debian + +# Full bootstrap test on Ubuntu +./run-docker-tests.sh full-ubuntu + +# Full bootstrap test on Debian +./run-docker-tests.sh full-debian + +# Test on both Ubuntu and Debian +./run-docker-tests.sh all +``` + +### Choosing the Right Test Option + +The testing framework offers different options for different testing needs: + +| If you want to... | Use this command | Why | +|-------------------|------------------|-----| +| Quickly check if specific packages are available | `./run-docker-tests.sh ubuntu` or `debian` | Fast validation of packages without full installation | +| Test changes to the test-setup.sh script | `./run-docker-tests.sh ubuntu` or `debian` | Executes only the test script in a clean environment | +| Validate a fix for a package installation issue | `./run-docker-tests.sh ubuntu` or `debian` | Tests package availability and installation | +| Test the complete user experience | `./run-docker-tests.sh full-ubuntu` or `full-debian` | Executes the actual bootstrap script like a real user would | +| Ensure bootstrap.sh works correctly | `./run-docker-tests.sh full-ubuntu` or `full-debian` | Tests the entire installation process from scratch | +| Verify cross-platform compatibility | `./run-docker-tests.sh all` | Tests on both supported platforms | +| Before pushing changes to main | `./run-docker-tests.sh all` and both full tests | Complete validation across environments | + +### Key Differences + +**Standard Tests** (`ubuntu`, `debian`): +- Use the Docker targets defined in the main Dockerfile +- Run the `test-setup.sh` script to check components +- Faster execution, focused on component validation +- Don't perform the actual bootstrap installation + +**Full Tests** (`full-ubuntu`, `full-debian`): +- Create a temporary Dockerfile for comprehensive testing +- Execute the bootstrap script directly from GitHub +- Complete end-to-end testing of the actual installation process +- Simulate the real user experience + +## Test Output + +The test provides: + +1. A color-coded console output showing success/failure of each component +2. A list of missing packages at the end +3. A detailed log file with all test results (saved to /tmp) +4. Suggestions for fixing detected issues + +## Adding New Tests + +To add new package tests: +1. Add the package name to `setup/packages.list` +2. The test framework will automatically validate its availability and installation + +For more complex components: +1. Add a new test function in `test-setup.sh` +2. Call the function in the main testing sequence +3. Increment the error counter if the test fails diff --git a/dotfiles/README.md b/dotfiles/README.md index 8327684..ebbbe6a 100644 --- a/dotfiles/README.md +++ b/dotfiles/README.md @@ -93,6 +93,41 @@ curl -fsSL https://raw.githubusercontent.com/acedanger/shell/main/bootstrap.sh | - Configure zoxide for better navigation - Install and configure Git +## Testing + +The repository includes a comprehensive testing framework to validate the shell setup process across different environments. + +### Docker-Based Testing + +Tests are run in isolated Docker containers to ensure consistent, repeatable validation: +- **Ubuntu 24.04 environment**: Tests compatibility with the latest Ubuntu LTS +- **Debian 12 environment**: Tests compatibility with Debian Stable + +### Test Coverage + +The tests validate: +- Package availability in repositories +- Successful installation of all packages in `packages.list` +- Oh My Zsh installation +- Zsh plugin installation +- Dotfile symlinking +- NVM and Node.js setup + +### Running Tests + +```bash +# Test your setup on Ubuntu +./run-docker-tests.sh ubuntu + +# Test your setup on Debian +./run-docker-tests.sh debian + +# Run a full bootstrap test (including installation) +./run-docker-tests.sh full-ubuntu +``` + +For more details on testing, see [Testing Documentation](../docs/testing.md). + ## Manual Steps If you need to manually set up aliases: diff --git a/run-docker-tests.sh b/run-docker-tests.sh new file mode 100755 index 0000000..3230a83 --- /dev/null +++ b/run-docker-tests.sh @@ -0,0 +1,237 @@ +#!/bin/bash + +# Script to run setup tests in Docker containers +# This allows testing the setup process in isolated environments + +set -e # Exit on error + +# Colors for output +GREEN='\033[0;32m' +YELLOW='\033[0;33m' +RED='\033[0;31m' +BLUE='\033[0;34m' +NC='\033[0m' # No Color + +# Check if Docker is installed and working +if ! command -v docker &> /dev/null; then + echo -e "${RED}Error: Docker is not installed. Please install Docker to run tests.${NC}" + exit 1 +fi + +# Create logs directory at the top level to ensure it exists +LOGS_DIR="$(pwd)/logs" +if [ ! -d "$LOGS_DIR" ]; then + echo -e "${YELLOW}Creating logs directory at: $LOGS_DIR${NC}" + mkdir -p "$LOGS_DIR" || { + echo -e "${RED}Failed to create logs directory! Check permissions.${NC}" + exit 1 + } +else + echo -e "${GREEN}Logs directory already exists at: $LOGS_DIR${NC}" +fi + +# Ensure the logs directory is writable +if [ ! -w "$LOGS_DIR" ]; then + echo -e "${YELLOW}Setting permissions on logs directory...${NC}" + chmod -R 777 "$LOGS_DIR" || { + echo -e "${RED}Failed to set write permissions on logs directory!${NC}" + exit 1 + } +fi + +# Create a test file to verify we can write to it +if touch "$LOGS_DIR/test_file" && rm "$LOGS_DIR/test_file"; then + echo -e "${GREEN}Log directory is writable and ready for use${NC}" +else + echo -e "${RED}Cannot write to logs directory even after setting permissions!${NC}" + exit 1 +fi + +# Check if Docker is running +if ! docker info &>/dev/null; then + echo -e "${YELLOW}Warning: Docker appears to be installed but not running or not properly configured.${NC}" + echo -e "${YELLOW}If using WSL2, ensure Docker Desktop is running with WSL integration enabled.${NC}" + echo -e "${YELLOW}Would you like to run the local test instead? [Y/n]${NC}" + read -r response + if [[ "$response" =~ ^([nN][oO]|[nN])$ ]]; then + echo -e "${RED}Exiting...${NC}" + exit 1 + else + echo -e "${BLUE}Running local test instead...${NC}" + ./test-setup.sh + exit $? + fi +fi + +# Build and run Ubuntu test container +run_ubuntu_test() { + echo -e "\n${BLUE}=== Running test in Ubuntu container ===${NC}" + # Create the logs directory if it doesn't exist + local log_dir="$(pwd)/logs" + mkdir -p "$log_dir" || true + + # Use sudo for chmod only if necessary + if [ ! -w "$log_dir" ]; then + echo -e "${YELLOW}Attempting to fix permissions with sudo...${NC}" + sudo chmod -R 777 "$log_dir" 2>/dev/null || { + echo -e "${YELLOW}Could not change permissions with sudo, continuing anyway...${NC}" + } + fi + + echo -e "${YELLOW}Logs will be saved to: $log_dir${NC}" + echo -e "${YELLOW}Building Ubuntu test container...${NC}" + docker build --target ubuntu-test -t shell-test:ubuntu . + + echo -e "${GREEN}Running tests with package installation...${NC}" + + # Create a timestamp for this test run + TEST_TIMESTAMP=$(date +"%Y%m%d-%H%M%S") + echo -e "${YELLOW}Test run timestamp: $TEST_TIMESTAMP${NC}" + + # Run container with proper volume mount and add environment variable for timestamp + docker run --rm -it \ + -e TEST_TIMESTAMP="$TEST_TIMESTAMP" \ + -e CONTAINER_TYPE="ubuntu" \ + -v "$log_dir:/logs:z" \ + shell-test:ubuntu + + # Check if logs were created + if ls "$log_dir"/setup-test-*"$TEST_TIMESTAMP"* &>/dev/null 2>&1; then + echo -e "${GREEN}Test logs successfully created in host directory${NC}" + else + echo -e "${YELLOW}Warning: No log files found matching timestamp $TEST_TIMESTAMP${NC}" + echo -e "${YELLOW}This may indicate issues with volume mounting or permissions${NC}" + echo -e "${YELLOW}Contents of log directory:${NC}" + ls -la "$log_dir" || echo "Cannot list directory contents" + fi + + echo -e "${BLUE}Test completed. Check logs in $log_dir directory${NC}" +} + +# Build and run Debian test container +run_debian_test() { + echo -e "\n${BLUE}=== Running test in Debian container ===${NC}" + # Create the logs directory if it doesn't exist + local log_dir="$(pwd)/logs" + mkdir -p "$log_dir" || true + + # Use sudo for chmod only if necessary + if [ ! -w "$log_dir" ]; then + echo -e "${YELLOW}Attempting to fix permissions with sudo...${NC}" + sudo chmod -R 777 "$log_dir" 2>/dev/null || { + echo -e "${YELLOW}Could not change permissions with sudo, continuing anyway...${NC}" + } + fi + + echo -e "${YELLOW}Logs will be saved to: $log_dir${NC}" + echo -e "${YELLOW}Building Debian test container...${NC}" + docker build --target debian-test -t shell-test:debian . + + echo -e "${GREEN}Running tests with package installation...${NC}" + + # Create a timestamp for this test run + TEST_TIMESTAMP=$(date +"%Y%m%d-%H%M%S") + echo -e "${YELLOW}Test run timestamp: $TEST_TIMESTAMP${NC}" + + # Run container with proper volume mount and add environment variable for timestamp + docker run --rm -it \ + -e TEST_TIMESTAMP="$TEST_TIMESTAMP" \ + -e CONTAINER_TYPE="debian" \ + -v "$log_dir:/logs:z" \ + shell-test:debian + + # Check if logs were created + if ls "$log_dir"/setup-test-*"$TEST_TIMESTAMP"* &>/dev/null 2>&1; then + echo -e "${GREEN}Test logs successfully created in host directory${NC}" + else + echo -e "${YELLOW}Warning: No log files found matching timestamp $TEST_TIMESTAMP${NC}" + echo -e "${YELLOW}This may indicate issues with volume mounting or permissions${NC}" + echo -e "${YELLOW}Contents of log directory:${NC}" + ls -la "$log_dir" || echo "Cannot list directory contents" + fi + + echo -e "${BLUE}Test completed. Check logs in $log_dir directory${NC}" +} + +# Full test with bootstrap script +run_full_test() { + local distro=$1 + local tag_name=$(echo $distro | sed 's/:/-/g') # Replace colon with hyphen for tag + echo -e "\n${BLUE}=== Running full bootstrap test in $distro container ===${NC}" + + # Create a Dockerfile for full test + cat > Dockerfile.fulltest < /etc/timezone \ + && apt-get update && apt-get install -y curl git sudo wget + +# Create a test user with sudo permissions +RUN useradd -ms /bin/bash testuser && \\ + echo "testuser ALL=(ALL) NOPASSWD:ALL" > /etc/sudoers.d/testuser + +# Create directory structure for setup files +RUN mkdir -p /home/testuser/shell + +# Copy test script for post-bootstrap validation +COPY --chown=testuser:testuser test-setup.sh /home/testuser/ + +# Copy entire repo structure to ensure we have all needed files +COPY --chown=testuser:testuser . /home/testuser/shell/ + +USER testuser +WORKDIR /home/testuser + +# Make the script executable +RUN chmod +x /home/testuser/test-setup.sh + +# Run tests before and after bootstrap to verify package installation +CMD ["/bin/bash", "-c", "echo -e '\\n\\nRunning pre-bootstrap tests...' && ./test-setup.sh && echo -e '\\n\\nRunning bootstrap...' && /home/testuser/shell/bootstrap.sh && echo -e '\\n\\nRunning post-bootstrap tests...' && ./test-setup.sh"] +EOF + + # Build and run the container + # Create the logs directory if it doesn't exist + mkdir -p "$(pwd)/logs" + docker build -f Dockerfile.fulltest -t shell-full-test:$tag_name . + docker run --rm -it -v "$(pwd)/logs:/logs" shell-full-test:$tag_name + + # Clean up + rm Dockerfile.fulltest +} + +# Parse command line arguments +case "$1" in + ubuntu) + run_ubuntu_test + ;; + debian) + run_debian_test + ;; + full-ubuntu) + run_full_test "ubuntu:24.04" + ;; + full-debian) + run_full_test "debian:12" + ;; + all) + run_ubuntu_test + run_debian_test + ;; + *) + echo -e "${BLUE}Shell Setup Test Runner${NC}" + echo -e "Usage: $0 [option]" + echo -e "\nOptions:" + echo " ubuntu Run test on Ubuntu container (tests packages and components)" + echo " debian Run test on Debian container (tests packages and components)" + echo " full-ubuntu Run full bootstrap test on Ubuntu container (performs complete installation)" + echo " full-debian Run full bootstrap test on Debian container (performs complete installation)" + echo " all Run tests on both Ubuntu and Debian containers (component tests only)" + echo -e "\nExamples:" + echo -e " $0 ubuntu # Quick test for package availability" + echo -e " $0 full-debian # Test complete bootstrap installation" + ;; +esac diff --git a/setup/setup.sh b/setup/setup.sh index 4953981..64227bf 100755 --- a/setup/setup.sh +++ b/setup/setup.sh @@ -25,11 +25,21 @@ echo -e "${YELLOW}Setting up Nala repository...${NC}" if apt-cache show nala &>/dev/null; then echo -e "${GREEN}Nala is available in standard repositories${NC}" else - # Try Ubuntu PPA as an alternative - echo -e "${YELLOW}Trying Nala from Ubuntu PPA...${NC}" - sudo apt-get update - sudo apt-get install -y software-properties-common - sudo add-apt-repository -y ppa:volitank/ppa + echo -e "${YELLOW}Nala not found in standard repositories. Trying alternative installation methods...${NC}" + + # Check Ubuntu version + if grep -q "noble\|lunar\|mantic\|jammy" /etc/os-release; then + echo -e "${GREEN}Ubuntu $(grep VERSION_CODENAME /etc/os-release | cut -d= -f2) detected. Ensuring universe repository is enabled...${NC}" + # Make sure universe repository is enabled + sudo apt-get update + sudo apt-get install -y software-properties-common + sudo add-apt-repository -y universe + else + # For older Ubuntu versions try to install directly + echo -e "${YELLOW}Older Ubuntu version detected. Trying to install Nala directly...${NC}" + sudo apt-get update + sudo apt-get install -y software-properties-common + fi fi # Setup VS Code repository @@ -47,21 +57,49 @@ out=$(mktemp) && wget -nv -O$out https://cli.github.com/packages/githubcli-archi && rm "$out" \ && echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/githubcli-archive-keyring.gpg] https://cli.github.com/packages stable main" | sudo tee /etc/apt/sources.list.d/github-cli.list > /dev/null -# Update package lists -sudo apt update +# Initialize USING_NALA variable +USING_NALA=false + +# Update package lists (ignoring previous errors) +echo -e "${YELLOW}Updating package lists...${NC}" +sudo apt update || echo -e "${YELLOW}Warning: apt update had some errors, but continuing...${NC}" # Install Nala first echo -e "${YELLOW}Installing Nala package manager...${NC}" -sudo apt-get install -y nala +if sudo apt-get install -y nala; then + USING_NALA=true + echo -e "${GREEN}Nala installed successfully!${NC}" +else + echo -e "${YELLOW}Failed to install Nala. Continuing with standard apt...${NC}" +fi -# Configure Nala mirrors -echo -e "${YELLOW}Configuring Nala mirrors...${NC}" -sudo nala fetch --auto --fetches 3 - -# Install remaining packages using Nala -echo -e "${YELLOW}Installing packages from packages.list...${NC}" -mapfile -t pkgs < <(grep -v '^//' "$SCRIPT_DIR/packages.list" | grep -v -e '^$' -e '^nala$') -sudo nala install -y "${pkgs[@]}" +# Configure Nala mirrors and install packages +if [ "$USING_NALA" = true ]; then + echo -e "${YELLOW}Configuring Nala mirrors...${NC}" + + # First, remove any existing nala sources list to avoid issues with invalid mirrors + sudo rm -f /etc/apt/sources.list.d/nala-sources.list 2>/dev/null + + # Try to fetch mirrors with less aggressive settings + if ! sudo nala fetch --auto --fetches 1 --country auto; then + echo -e "${YELLOW}Mirror selection failed, continuing with system default mirrors...${NC}" + # Remove any potentially corrupted Nala sources + sudo rm -f /etc/apt/sources.list.d/nala-sources.list 2>/dev/null + fi + + # Install remaining packages using Nala + echo -e "${YELLOW}Installing packages from packages.list with Nala...${NC}" + mapfile -t pkgs < <(grep -v '^//' "$SCRIPT_DIR/packages.list" | grep -v -e '^$' -e '^nala$') + sudo nala install -y "${pkgs[@]}" || { + echo -e "${YELLOW}Nala install failed, falling back to apt...${NC}" + sudo apt-get install -y "${pkgs[@]}" + } +else + # Fall back to apt if Nala installation failed + echo -e "${YELLOW}Installing packages from packages.list with apt...${NC}" + mapfile -t pkgs < <(grep -v '^//' "$SCRIPT_DIR/packages.list" | grep -v -e '^$' -e '^nala$') + sudo apt-get install -y "${pkgs[@]}" +fi # Install Zsh if not already installed echo -e "${YELLOW}Installing Zsh...${NC}" diff --git a/startup.sh b/startup.sh new file mode 100755 index 0000000..fb5ee2c --- /dev/null +++ b/startup.sh @@ -0,0 +1,75 @@ +#!/bin/bash + +# Startup script for Docker containers to ensure permissions and run tests + +# Define colors for output +GREEN='\033[0;32m' +YELLOW='\033[0;33m' +RED='\033[0;31m' +BLUE='\033[0;34m' +NC='\033[0m' # No Color + +echo -e "${BLUE}=== Container Startup Checks ===${NC}" + +# Display system information +echo -e "${BLUE}System information:${NC}" +if [ -f /etc/os-release ]; then + . /etc/os-release + echo -e "- OS: ${GREEN}$PRETTY_NAME${NC}" +else + echo -e "- OS: ${YELLOW}Unknown${NC}" +fi + +# Ensure packages.list is available +echo -e "${BLUE}Checking for packages.list:${NC}" +if [ -f "$HOME/shell/setup/packages.list" ]; then + echo -e "- packages.list: ${GREEN}Found${NC}" + # Count packages in list (excluding comments and empty lines) + pkg_count=$(grep -v '^//' "$HOME/shell/setup/packages.list" | grep -v -e '^$' | wc -l) + echo -e "- Package count: ${GREEN}$pkg_count packages${NC}" +else + echo -e "- packages.list: ${RED}Not found${NC}" + echo "Error: Could not find packages.list file. Tests will fail." +fi + +# Ensure logs directory has correct permissions +echo -e "${BLUE}Setting up logs directory:${NC}" +if [ -d "/logs" ]; then + echo -e "- Logs directory: ${GREEN}Found${NC}" + + # Check ownership and permissions + logs_owner=$(stat -c '%U:%G' /logs) + echo -e "- Current ownership: $logs_owner" + + echo "- Setting permissions on /logs directory..." + sudo chown -R $(whoami):$(whoami) /logs 2>/dev/null || echo -e "${YELLOW}Failed to set ownership${NC}" + sudo chmod -R 777 /logs 2>/dev/null || echo -e "${YELLOW}Failed to set permissions${NC}" + + # Verify permissions are correct + if [ -w "/logs" ]; then + echo -e "- Write permission: ${GREEN}OK${NC}" + + # Create a test file to really verify we can write + if touch "/logs/test_file" 2>/dev/null; then + echo -e "- Test write: ${GREEN}Succeeded${NC}" + rm -f "/logs/test_file" + else + echo -e "- Test write: ${RED}Failed${NC}" + fi + else + echo -e "- Write permission: ${RED}Failed${NC}" + fi +else + echo -e "- Logs directory: ${YELLOW}Not found${NC}" + echo "- Creating /logs directory..." + if sudo mkdir -p /logs && sudo chown -R $(whoami):$(whoami) /logs && sudo chmod -R 777 /logs; then + echo -e "- Created logs directory with proper permissions: ${GREEN}Success${NC}" + else + echo -e "- Creating logs directory: ${RED}Failed${NC}" + echo "Warning: Logs will be saved inside container only" + fi +fi + +echo -e "${BLUE}=== Starting Test Script ===${NC}" +# Run the test script +./test-setup.sh diff --git a/test-setup.sh b/test-setup.sh new file mode 100755 index 0000000..999c287 --- /dev/null +++ b/test-setup.sh @@ -0,0 +1,764 @@ +#!/bin/bash + +# Script to test bootstrap.sh and setup.sh scripts in different environments +# This script can be run on different systems to validate the setup process + +# NOTE: We don't use 'set -e' to allow testing all packages even if some fail + +# Colors for output +GREEN='\033[0;32m' +YELLOW='\033[0;33m' +RED='\033[0;31m' +BLUE='\033[0;34m' +NC='\033[0m' # No Color + +# Maximum number of installation attempts +MAX_INSTALL_ATTEMPTS=3 +CURRENT_ATTEMPT=1 + +# Log file +# Create logs directory if it doesn't exist and is writable +# First, try creating the logs directory in case it doesn't exist +if [ -d "/logs" ] || mkdir -p /logs 2>/dev/null; then + if [ -w "/logs" ]; then + LOGS_DIR="/logs" + echo -e "${GREEN}Logs will be saved to host system at ./logs/${NC}" + else + echo -e "${YELLOW}Warning: /logs directory exists but is not writable${NC}" + sudo chmod -R 777 /logs 2>/dev/null || true + if [ -w "/logs" ]; then + LOGS_DIR="/logs" + echo -e "${GREEN}Logs will be saved to host system at ./logs/ (permissions fixed)${NC}" + else + LOGS_DIR="/tmp" + echo -e "${RED}Warning: Could not fix /logs permissions. Logs will be saved to $LOGS_DIR (container only)${NC}" + fi + fi +else + LOGS_DIR="/tmp" + echo -e "${YELLOW}Warning: /logs directory not available or could not be created. Logs will be saved to $LOGS_DIR (container only)${NC}" +fi +# Use the timestamp from environment variable if set, otherwise generate a new one +TIMESTAMP="${TEST_TIMESTAMP:-$(date +"%Y%m%d-%H%M%S")}" +CONTAINER_INFO="${CONTAINER_TYPE:+$CONTAINER_TYPE-}" + +LOG_FILE="$LOGS_DIR/setup-test-${CONTAINER_INFO}${TIMESTAMP}.log" +echo "Starting setup test at $(date)" > "$LOG_FILE" || { + echo -e "${RED}Error: Failed to write to log file at $LOG_FILE. Check permissions.${NC}" + ls -la "$LOGS_DIR" 2>/dev/null || echo "Cannot list logs directory contents" + LOGS_DIR="/tmp" + LOG_FILE="$LOGS_DIR/setup-test-${CONTAINER_INFO}${TIMESTAMP}.log" + echo -e "${YELLOW}Attempting to use fallback log location: $LOG_FILE${NC}" + echo "Starting setup test at $(date)" > "$LOG_FILE" +} + +# Identify the system +echo -e "${BLUE}=== System Information ===${NC}" +if [ -f /etc/os-release ]; then + . /etc/os-release + echo -e "Detected: ${GREEN}$PRETTY_NAME${NC}" + echo "Testing on: $PRETTY_NAME" >> "$LOG_FILE" +else + echo -e "${RED}Could not determine OS${NC}" + echo "Could not determine OS" >> "$LOG_FILE" + exit 1 +fi + +# Determine if this is a pre-bootstrap or post-bootstrap test +TEST_STAGE="Unknown" +if [ -d "$HOME/shell" ] && [ -f "$HOME/shell/bootstrap.sh" ] && [ -d "$HOME/shell/setup" ] && [ -f "$HOME/shell/setup/setup.sh" ]; then + # Check if there are any signs that bootstrap has been completed + if [ -d "$HOME/.oh-my-zsh" ] || [ -f "$HOME/.zshrc" ] || [ -d "$HOME/.nvm" ]; then + TEST_STAGE="Post-Bootstrap" + else + TEST_STAGE="Mid-Bootstrap" + fi +else + TEST_STAGE="Pre-Bootstrap" +fi +echo -e "Test Stage: ${GREEN}$TEST_STAGE${NC}" +echo "Test Stage: $TEST_STAGE" >> "$LOG_FILE" + +# Check if this is running in a test environment (container or VM) +ENV_TYPE="Unknown" +if [ -f /.dockerenv ]; then + ENV_TYPE="Docker container" +elif grep -q "^flags.*hypervisor" /proc/cpuinfo 2>/dev/null; then + ENV_TYPE="Virtual machine" +else + ENV_TYPE="Physical machine" +fi +echo -e "Environment: ${GREEN}$ENV_TYPE${NC}" +echo "Environment: $ENV_TYPE" >> "$LOG_FILE" + +# Initialize arrays for tracking +missing_packages=() +installed_this_round=() +failed_packages=() + +# Helper functions +check_command() { + command -v "$1" &> /dev/null +} + +test_package() { + local pkg=$1 + local cmd=${2:-$1} + local alt_cmd=$3 # Optional alternative command name + + echo -en "Testing if $pkg is installed... " + + # Special case for cowsay and lolcat which might be in different paths + if [ "$pkg" = "cowsay" ]; then + if check_command "$cmd"; then + echo -e "${GREEN}✓${NC}" + echo "$pkg: Installed (found in PATH as $cmd)" >> "$LOG_FILE" + return 0 + elif [ -x "/usr/games/cowsay" ]; then + echo -e "${GREEN}✓${NC} (in /usr/games)" + echo "$pkg: Installed (found in /usr/games/)" >> "$LOG_FILE" + # Create a symlink to make it available in PATH for other scripts + if [ ! -e "$HOME/.local/bin" ]; then + mkdir -p "$HOME/.local/bin" + fi + if [ ! -e "$HOME/.local/bin/cowsay" ] && [ -w "$HOME/.local/bin" ]; then + ln -sf /usr/games/cowsay "$HOME/.local/bin/cowsay" + echo "Created symlink for cowsay in $HOME/.local/bin" >> "$LOG_FILE" + fi + return 0 + fi + elif [ "$pkg" = "lolcat" ]; then + if check_command "$cmd"; then + echo -e "${GREEN}✓${NC}" + echo "$pkg: Installed (found in PATH as $cmd)" >> "$LOG_FILE" + return 0 + elif [ -x "/usr/games/lolcat" ]; then + echo -e "${GREEN}✓${NC} (in /usr/games)" + echo "$pkg: Installed (found in /usr/games/)" >> "$LOG_FILE" + # Create a symlink to make it available in PATH for other scripts + if [ ! -e "$HOME/.local/bin" ]; then + mkdir -p "$HOME/.local/bin" + fi + if [ ! -e "$HOME/.local/bin/lolcat" ] && [ -w "$HOME/.local/bin" ]; then + ln -sf /usr/games/lolcat "$HOME/.local/bin/lolcat" + echo "Created symlink for lolcat in $HOME/.local/bin" >> "$LOG_FILE" + fi + return 0 + fi + elif check_command "$cmd"; then + echo -e "${GREEN}✓${NC}" + echo "$pkg: Installed (as $cmd)" >> "$LOG_FILE" + return 0 + elif [ -n "$alt_cmd" ] && check_command "$alt_cmd"; then + echo -e "${GREEN}✓${NC} (as $alt_cmd)" + echo "$pkg: Installed (as $alt_cmd)" >> "$LOG_FILE" + return 0 + else + echo -e "${RED}✗${NC}" + echo "$pkg: Missing" >> "$LOG_FILE" + return 1 + fi + # Note: The calling code will handle the failure and continue testing +} + +test_file_exists() { + local file=$1 + local description=${2:-"File exists"} + echo -en "Testing if $file exists... " + if [ -e "$file" ]; then + echo -e "${GREEN}✓${NC}" + echo "$description: Yes" >> "$LOG_FILE" + return 0 + else + echo -e "${RED}✗${NC}" + echo "$description: No" >> "$LOG_FILE" + return 1 + fi +} + +# Get actual package name based on distribution +get_package_name() { + local pkg=$1 + + # Handle Debian-specific package name differences + if [[ "$ID" == "debian" ]]; then + case "$pkg" in + "bat") + echo "batcat" + ;; + *) + echo "$pkg" + ;; + esac + else + # Default to the original package name for Ubuntu and others + echo "$pkg" + fi +} + +# Function to install missing packages +install_missing_packages() { + local packages=("$@") + local install_cmd_name + local install_cmd=() + + if [ ${#packages[@]} -eq 0 ]; then + echo -e "${GREEN}No packages to install${NC}" + echo "No packages to install" >> "$LOG_FILE" + return 0 + fi + + echo -e "\n${BLUE}=== Installing missing packages (Attempt $CURRENT_ATTEMPT of $MAX_INSTALL_ATTEMPTS) ===${NC}" + echo "=== Installing missing packages (Attempt $CURRENT_ATTEMPT of $MAX_INSTALL_ATTEMPTS) ===" >> "$LOG_FILE" + + # Determine the best installation command + if check_command nala; then + install_cmd=(sudo DEBIAN_FRONTEND=noninteractive nala install -y) + install_cmd_name="nala" + echo -e "${GREEN}Using nala for package installation${NC}" + echo "Using nala for package installation" >> "$LOG_FILE" + else + install_cmd=(sudo DEBIAN_FRONTEND=noninteractive apt-get -o Dpkg::Options::="--force-confdef" -o Dpkg::Options::="--force-confnew" install -y) + install_cmd_name="apt-get" + echo -e "${YELLOW}Using apt-get for package installation${NC}" + echo "Using apt-get for package installation" >> "$LOG_FILE" + fi + + # Convert package list to distribution-specific names + local install_list=() + for pkg in "${packages[@]}"; do + local actual_pkg=$(get_package_name "$pkg") + install_list+=("$actual_pkg") + done + + # Update package lists + echo -e "${YELLOW}Updating package lists...${NC}" + echo "Updating package lists" >> "$LOG_FILE" + if check_command nala; then + echo -e "${GREEN}Running: sudo DEBIAN_FRONTEND=noninteractive nala update${NC}" + sudo DEBIAN_FRONTEND=noninteractive nala update | tee -a "$LOG_FILE" + else + echo -e "${GREEN}Running: sudo DEBIAN_FRONTEND=noninteractive apt-get update${NC}" + sudo DEBIAN_FRONTEND=noninteractive apt-get update | tee -a "$LOG_FILE" + fi + + # Install packages + echo -e "${YELLOW}Installing packages: ${install_list[*]}${NC}" + echo "Installing packages: ${install_list[*]}" >> "$LOG_FILE" + + # Show the exact command being run for debugging + echo -e "${BLUE}Running: ${install_cmd[*]} ${install_list[*]}${NC}" + + # Execute the install command with the package list + if ! "${install_cmd[@]}" "${install_list[@]}" 2>&1 | tee -a "$LOG_FILE"; then + echo -e "${RED}Failed to install some packages. Check the log for details.${NC}" + echo "Failed to install some packages" >> "$LOG_FILE" + + # Try to install packages one by one to identify problematic ones + echo -e "${YELLOW}Trying to install packages individually...${NC}" + echo "Trying to install packages individually" >> "$LOG_FILE" + + installed_this_round=() + local failed_this_round=() + + for i in "${!packages[@]}"; do + local pkg="${packages[$i]}" + local actual_pkg="${install_list[$i]}" + + echo -en "Installing $pkg as $actual_pkg... " + if "${install_cmd[@]}" "$actual_pkg" >> "$LOG_FILE" 2>&1; then + echo -e "${GREEN}✓${NC}" + echo "$pkg: Installed successfully" >> "$LOG_FILE" + installed_this_round+=("$pkg") + else + echo -e "${RED}✗${NC}" + echo "$pkg: Installation failed" >> "$LOG_FILE" + failed_this_round+=("$pkg") + fi + done + + failed_packages=("${failed_this_round[@]}") + return 1 + else + echo -e "${GREEN}Successfully installed all packages!${NC}" + echo "Successfully installed all packages" >> "$LOG_FILE" + installed_this_round=("${packages[@]}") + return 0 + fi +} + +# Create a temporary backup of .zshrc if it exists +if [ -f "$HOME/.zshrc" ]; then + cp "$HOME/.zshrc" "$HOME/.zshrc.test_backup" + echo "Backed up existing .zshrc" >> "$LOG_FILE" +fi + +# Main testing sequence +echo -e "\n${BLUE}=== Testing bootstrap.sh ===${NC}" +echo "=== Testing bootstrap.sh ===" >> "$LOG_FILE" + +# Get the bootstrap script if it doesn't exist locally +if [ ! -f "$HOME/shell/bootstrap.sh" ]; then + echo -e "${YELLOW}Downloading bootstrap.sh...${NC}" + echo "Downloading bootstrap.sh" >> "$LOG_FILE" + curl -s -o /tmp/bootstrap.sh https://raw.githubusercontent.com/acedanger/shell/main/bootstrap.sh || { + echo -e "${RED}Failed to download bootstrap.sh${NC}" + echo "Failed to download bootstrap.sh" >> "$LOG_FILE" + exit 1 + } + chmod +x /tmp/bootstrap.sh +else + echo -e "${YELLOW}Using local bootstrap.sh${NC}" + echo "Using local bootstrap.sh" >> "$LOG_FILE" + cp "$HOME/shell/bootstrap.sh" /tmp/bootstrap.sh +fi + +# Run bootstrap in test mode (no actual execution) +echo -e "${YELLOW}Analyzing bootstrap script...${NC}" +bash -n /tmp/bootstrap.sh +if [ $? -eq 0 ]; then + echo -e "${GREEN}Bootstrap script syntax is valid${NC}" + echo "Bootstrap script syntax: Valid" >> "$LOG_FILE" +else + echo -e "${RED}Bootstrap script has syntax errors${NC}" + echo "Bootstrap script syntax: Invalid" >> "$LOG_FILE" + exit 1 +fi + +echo -e "\n${BLUE}=== Testing setup.sh ===${NC}" +echo "=== Testing setup.sh ===" >> "$LOG_FILE" + +# Check setup.sh script if available +if [ -f "$HOME/shell/setup/setup.sh" ]; then + echo -e "${YELLOW}Analyzing setup script...${NC}" + bash -n "$HOME/shell/setup/setup.sh" + if [ $? -eq 0 ]; then + echo -e "${GREEN}Setup script syntax is valid${NC}" + echo "Setup script syntax: Valid" >> "$LOG_FILE" + else + echo -e "${RED}Setup script has syntax errors${NC}" + echo "Setup script syntax: Invalid" >> "$LOG_FILE" + exit 1 + fi +else + echo -e "${YELLOW}Setup script not available locally. Will be downloaded by bootstrap.${NC}" + echo "Setup script not available locally" >> "$LOG_FILE" +fi + +echo -e "\n${BLUE}=== Testing Debian compatibility ===${NC}" +echo "=== Testing Debian compatibility ===" >> "$LOG_FILE" + +# Check for Debian-specific commands used in scripts +echo -en "Checking if apt is available... " +if check_command apt; then + echo -e "${GREEN}✓${NC}" + echo "apt: Available" >> "$LOG_FILE" +else + echo -e "${RED}✗${NC} (Scripts may not work here)" + echo "apt: Not available" >> "$LOG_FILE" +fi + +# Check if the package list is valid for this system +if [ -f "$HOME/shell/setup/packages.list" ]; then + echo -e "${YELLOW}Testing package availability in repositories:${NC}" + echo "Testing package availability:" >> "$LOG_FILE" + + # Exclude commented lines and empty lines + packages=$(grep -v '^//' "$HOME/shell/setup/packages.list" | grep -v -e '^$') + + for pkg in $packages; do + echo -en "Checking if $pkg is available in repos... " + actual_pkg=$(get_package_name "$pkg") + if apt-cache show "$actual_pkg" &>/dev/null; then + echo -e "${GREEN}✓${NC}" + echo "$pkg: Available in repos as $actual_pkg" >> "$LOG_FILE" + else + echo -e "${RED}✗${NC}" + echo "$pkg: Not available in repos" >> "$LOG_FILE" + fi + done +fi + +# Main testing and installation loop +while [ $CURRENT_ATTEMPT -le $MAX_INSTALL_ATTEMPTS ]; do + echo -e "\n${BLUE}=== Testing installed components (Attempt $CURRENT_ATTEMPT of $MAX_INSTALL_ATTEMPTS) ===${NC}" + echo "=== Testing installed components (Attempt $CURRENT_ATTEMPT of $MAX_INSTALL_ATTEMPTS) ===" >> "$LOG_FILE" + + errors=0 + missing_packages=() + + # Test core required tools + core_tools=("git" "curl" "wget") + for tool in "${core_tools[@]}"; do + if ! test_package "$tool"; then + ((errors++)) + missing_packages+=("$tool") + fi + done + + # Test if setup has already been run + if [ -d "$HOME/.oh-my-zsh" ]; then + echo -e "Oh My Zsh: ${GREEN}Installed${NC}" + echo "Oh My Zsh: Installed" >> "$LOG_FILE" + else + echo -e "Oh My Zsh: ${YELLOW}Not installed${NC}" + echo "Oh My Zsh: Not installed" >> "$LOG_FILE" + fi + + # Check custom packages from packages.list + echo -e "${YELLOW}Testing packages listed in packages.list:${NC}" + echo "Testing packages listed in packages.list:" >> "$LOG_FILE" + + if [ -f "$HOME/shell/setup/packages.list" ]; then + # Exclude commented lines and empty lines + packages=$(grep -v '^//' "$HOME/shell/setup/packages.list" | grep -v -e '^$') + + for pkg in $packages; do + case "$pkg" in + "bat") + if ! test_package "bat" "bat" "batcat"; then + ((errors++)) + missing_packages+=("$pkg") + fi + ;; + "cowsay") + # Extra debugging for cowsay + echo -e "\n${BLUE}Debugging cowsay package:${NC}" | tee -a "$LOG_FILE" + if [ -f "/usr/games/cowsay" ]; then + echo -e "- Cowsay found at /usr/games/cowsay" | tee -a "$LOG_FILE" + ls -la /usr/games/cowsay | tee -a "$LOG_FILE" + else + echo -e "- Cowsay not found at /usr/games/cowsay" | tee -a "$LOG_FILE" + fi + if check_command cowsay; then + echo -e "- Cowsay found in PATH" | tee -a "$LOG_FILE" + command -v cowsay | tee -a "$LOG_FILE" + else + echo -e "- Cowsay not found in PATH" | tee -a "$LOG_FILE" + fi + + if ! test_package "cowsay" "cowsay"; then + ((errors++)) + missing_packages+=("$pkg") + fi + ;; + "lolcat") + # Extra debugging for lolcat + echo -e "\n${BLUE}Debugging lolcat package:${NC}" | tee -a "$LOG_FILE" + if [ -f "/usr/games/lolcat" ]; then + echo -e "- Lolcat found at /usr/games/lolcat" | tee -a "$LOG_FILE" + ls -la /usr/games/lolcat | tee -a "$LOG_FILE" + else + echo -e "- Lolcat not found at /usr/games/lolcat" | tee -a "$LOG_FILE" + fi + if check_command lolcat; then + echo -e "- Lolcat found in PATH" | tee -a "$LOG_FILE" + command -v lolcat | tee -a "$LOG_FILE" + else + echo -e "- Lolcat not found in PATH" | tee -a "$LOG_FILE" + fi + + if ! test_package "lolcat" "lolcat"; then + ((errors++)) + missing_packages+=("$pkg") + fi + ;; + *) + if ! test_package "$pkg" "$pkg"; then + ((errors++)) + missing_packages+=("$pkg") + fi + ;; + esac + done + + # Report missing packages + if [ ${#missing_packages[@]} -gt 0 ]; then + echo -e "\n${YELLOW}Missing packages:${NC}" + for pkg in "${missing_packages[@]}"; do + echo -e "- ${RED}$pkg${NC}" + done + + # Count installed vs. total packages + total_pkgs=$(grep -v '^//' "$HOME/shell/setup/packages.list" | grep -v -e '^$' | wc -l) + installed_pkgs=$((total_pkgs - ${#missing_packages[@]})) + echo -e "${GREEN}$installed_pkgs of $total_pkgs packages installed${NC} (${YELLOW}${#missing_packages[@]} missing${NC})" + echo "$installed_pkgs of $total_pkgs packages installed (${#missing_packages[@]} missing)" >> "$LOG_FILE" + + # Install missing packages if we haven't reached the maximum attempts + if [ $CURRENT_ATTEMPT -lt $MAX_INSTALL_ATTEMPTS ]; then + install_missing_packages "${missing_packages[@]}" + + echo -e "\n${BLUE}=== Installation Results ===${NC}" + if [ ${#installed_this_round[@]} -gt 0 ]; then + echo -e "${GREEN}Successfully installed:${NC}" + for pkg in "${installed_this_round[@]}"; do + echo -e "- ${GREEN}$pkg${NC}" + done + echo "Successfully installed: ${installed_this_round[*]}" >> "$LOG_FILE" + fi + + if [ ${#failed_packages[@]} -gt 0 ]; then + echo -e "${RED}Failed to install:${NC}" + for pkg in "${failed_packages[@]}"; do + echo -e "- ${RED}$pkg${NC}" + done + echo "Failed to install: ${failed_packages[*]}" >> "$LOG_FILE" + fi + + CURRENT_ATTEMPT=$((CURRENT_ATTEMPT + 1)) + echo -e "\n${YELLOW}Continuing to next test iteration...${NC}" + echo "Continuing to next test iteration" >> "$LOG_FILE" + continue + fi + else + total_pkgs=$(grep -v '^//' "$HOME/shell/setup/packages.list" | grep -v -e '^$' | wc -l) + echo -e "\n${GREEN}All $total_pkgs packages are installed!${NC}" + echo "All $total_pkgs packages are installed" >> "$LOG_FILE" + break + fi + else + echo -e "${RED}packages.list file not found at $HOME/shell/setup/packages.list${NC}" + echo "packages.list file not found" >> "$LOG_FILE" + fi + + # If no missing packages or we've reached max attempts, break out of the loop + if [ ${#missing_packages[@]} -eq 0 ] || [ $CURRENT_ATTEMPT -ge $MAX_INSTALL_ATTEMPTS ]; then + break + fi + + CURRENT_ATTEMPT=$((CURRENT_ATTEMPT + 1)) +done + +# Test nvm installation +if [ -d "$HOME/.nvm" ]; then + echo -e "NVM: ${GREEN}Installed${NC}" + echo "NVM: Installed" >> "$LOG_FILE" + + # Source NVM + export NVM_DIR="$HOME/.nvm" + [ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh" + + # Test Node.js installed by NVM + if check_command node; then + node_version=$(node -v) + echo -e "Node.js: ${GREEN}$node_version${NC}" + echo "Node.js: $node_version" >> "$LOG_FILE" + else + echo -e "Node.js: ${RED}Not installed${NC}" + echo "Node.js: Not installed" >> "$LOG_FILE" + ((errors++)) + fi +else + echo -e "NVM: ${YELLOW}Not installed${NC}" + echo "NVM: Not installed" >> "$LOG_FILE" +fi + +# Test zoxide +if test_package "zoxide"; then + echo -e "Zoxide integration: ${GREEN}Available${NC}" + echo "Zoxide integration: Available" >> "$LOG_FILE" +fi + +# Test ZSH plugins +zsh_plugins=( + "$HOME/.oh-my-zsh/custom/plugins/zsh-autosuggestions" + "$HOME/.oh-my-zsh/custom/plugins/zsh-syntax-highlighting" + "$HOME/.oh-my-zsh/custom/plugins/zsh-you-should-use" +) + +for plugin_dir in "${zsh_plugins[@]}"; do + plugin_name=$(basename "$plugin_dir") + if [ -d "$plugin_dir" ]; then + echo -e "ZSH plugin $plugin_name: ${GREEN}Installed${NC}" + echo "ZSH plugin $plugin_name: Installed" >> "$LOG_FILE" + else + echo -e "ZSH plugin $plugin_name: ${YELLOW}Not installed${NC}" + echo "ZSH plugin $plugin_name: Not installed" >> "$LOG_FILE" + fi +done + +# Test dotfile linking +dotfiles=( + "$HOME/.zshrc" + "$HOME/.nanorc" + "$HOME/.profile" + "$HOME/.gitconfig" +) + +for dotfile in "${dotfiles[@]}"; do + if [ -L "$dotfile" ]; then + echo -e "Dotfile $dotfile: ${GREEN}Symlinked${NC}" + echo "Dotfile $dotfile: Symlinked" >> "$LOG_FILE" + + # Check if symlink is valid + if [ -e "$dotfile" ]; then + target=$(readlink -f "$dotfile") + echo -e " → Links to: ${GREEN}$target${NC}" + echo " → Links to: $target" >> "$LOG_FILE" + else + echo -e " → ${RED}Broken symlink${NC}" + echo " → Broken symlink" >> "$LOG_FILE" + ((errors++)) + fi + elif [ -f "$dotfile" ]; then + echo -e "Dotfile $dotfile: ${YELLOW}Exists but not symlinked${NC}" + echo "Dotfile $dotfile: Exists but not symlinked" >> "$LOG_FILE" + else + echo -e "Dotfile $dotfile: ${YELLOW}Not present${NC}" + echo "Dotfile $dotfile: Not present" >> "$LOG_FILE" + fi +done + +echo -e "\n${BLUE}=== Summary ===${NC}" +echo "=== Summary ===" >> "$LOG_FILE" + +# Test summary +if [ $errors -eq 0 ]; then + echo -e "${GREEN}All critical components and packages passed! Your setup appears to be working correctly.${NC}" + echo "Result: All critical components and packages passed" >> "$LOG_FILE" +else + echo -e "${RED}Found $errors potential issues with your setup.${NC}" + echo "Result: Found $errors potential issues" >> "$LOG_FILE" + + # Display missing packages if any + if [ ${#missing_packages[@]} -gt 0 ]; then + echo -e "\n${YELLOW}The following packages were not found:${NC}" + echo "Missing packages:" >> "$LOG_FILE" + for pkg in "${missing_packages[@]}"; do + echo -e " - ${RED}$pkg${NC}" + echo " - $pkg" >> "$LOG_FILE" + done + + if [ $CURRENT_ATTEMPT -gt $MAX_INSTALL_ATTEMPTS ]; then + echo -e "\n${RED}Reached maximum installation attempts ($MAX_INSTALL_ATTEMPTS).${NC}" + echo -e "${YELLOW}Some packages could not be installed automatically.${NC}" + echo "Reached maximum installation attempts" >> "$LOG_FILE" + fi + + echo -e "\n${BLUE}You can manually install these packages with:${NC}" + echo -e " sudo apt-get install ${missing_packages[*]}" + fi +fi + +# Provide more detailed information about log files +if [[ "$LOG_FILE" == "/logs/"* ]]; then + HOST_LOG_FILE="./logs/$(basename "$LOG_FILE")" + echo -e "\n${YELLOW}Complete test log saved to:${NC} $LOG_FILE" + echo -e "${GREEN}This log file is accessible on the host system at:${NC} $HOST_LOG_FILE" +else + echo -e "\n${YELLOW}Complete test log saved to:${NC} $LOG_FILE (in container only)" + echo -e "${YELLOW}Note: This log file will be lost when the container exits." + echo -e "To preserve logs, run with the logs volume mounted using run-docker-tests.sh${NC}" +fi + +# Check for Debian-specific issues +if [[ "$ID" == "debian" ]]; then + echo -e "\n${BLUE}=== Debian-Specific Recommendations ===${NC}" + echo "=== Debian-Specific Recommendations ===" >> "$LOG_FILE" + echo "1. Ensure Debian's 'universe' equivalent repositories are enabled (contrib, non-free)" + echo "2. Some packages like 'bat' may have different names in Debian (batcat)" + echo "3. Consider adding Debian-specific adjustments to setup.sh" + + # Add specific Debian package name mappings + echo -e "\nOn Debian, you may need these package name adjustments:" + echo " - bat → batcat" + echo " - (add more as needed)" + + echo "Debian package name mappings may be required" >> "$LOG_FILE" +fi + +# Enhanced testing for bootstrapped environment +check_bootstrapped_environment() { + echo -e "\n${BLUE}=== Checking for bootstrapped environment ===${NC}" + echo "=== Checking for bootstrapped environment ===" >> "$LOG_FILE" + + if [ -d "$HOME/shell" ] && [ -f "$HOME/shell/bootstrap.sh" ]; then + echo -e "${GREEN}Environment appears to be bootstrapped:${NC}" + echo "Environment is bootstrapped" >> "$LOG_FILE" + + # Check the shell repository structure + if [ -d "$HOME/shell/setup" ] && [ -f "$HOME/shell/setup/setup.sh" ]; then + echo -e " - ${GREEN}Setup directory and script present${NC}" + echo "Setup directory and script: Present" >> "$LOG_FILE" + + # Check if setup.sh is executable + if [ -x "$HOME/shell/setup/setup.sh" ]; then + echo -e " - ${GREEN}Setup script is executable${NC}" + echo "Setup script is executable: Yes" >> "$LOG_FILE" + else + echo -e " - ${RED}Setup script is not executable${NC}" + echo "Setup script is executable: No" >> "$LOG_FILE" + fi + else + echo -e " - ${RED}Setup directory or setup.sh missing${NC}" + echo "Setup directory or setup.sh: Missing" >> "$LOG_FILE" + fi + + # Check packages.list + if [ -f "$HOME/shell/setup/packages.list" ]; then + echo -e " - ${GREEN}Packages list present${NC}" + echo "Packages list: Present" >> "$LOG_FILE" + + # Count packages in list + pkg_count=$(grep -v '^//' "$HOME/shell/setup/packages.list" | grep -v -e '^$' | wc -l) + echo -e " - ${GREEN}Package list contains $pkg_count packages${NC}" + echo "Package list count: $pkg_count" >> "$LOG_FILE" + else + echo -e " - ${RED}Packages list missing${NC}" + echo "Packages list: Missing" >> "$LOG_FILE" + fi + + # Check dotfiles directory + if [ -d "$HOME/shell/dotfiles" ]; then + echo -e " - ${GREEN}Dotfiles directory present${NC}" + echo "Dotfiles directory: Present" >> "$LOG_FILE" + + # Check if dotfiles are properly symlinked + dotfiles_linked=true + for dotfile in "$HOME/.zshrc" "$HOME/.nanorc" "$HOME/.profile" "$HOME/.gitconfig"; do + if [ -L "$dotfile" ]; then + target=$(readlink -f "$dotfile" 2>/dev/null) + if [[ "$target" == *"shell/dotfiles"* ]]; then + continue + else + dotfiles_linked=false + break + fi + else + dotfiles_linked=false + break + fi + done + + if $dotfiles_linked; then + echo -e " - ${GREEN}Dotfiles are properly symlinked${NC}" + echo "Dotfiles symlinked: Yes" >> "$LOG_FILE" + else + echo -e " - ${YELLOW}Some dotfiles are not properly symlinked${NC}" + echo "Dotfiles symlinked: No" >> "$LOG_FILE" + fi + else + echo -e " - ${RED}Dotfiles directory missing${NC}" + echo "Dotfiles directory: Missing" >> "$LOG_FILE" + fi + else + echo -e "${YELLOW}No bootstrapped environment detected${NC}" + echo "No bootstrapped environment detected" >> "$LOG_FILE" + fi +} + +# Call the check if we're not in a simple test environment +if [ -d "$HOME/shell" ] && [ -f "$HOME/shell/bootstrap.sh" ]; then + check_bootstrapped_environment +fi + +# Restore .zshrc if we backed it up +if [ -f "$HOME/.zshrc.test_backup" ]; then + echo -e "\n${YELLOW}Restoring original .zshrc${NC}" + mv "$HOME/.zshrc.test_backup" "$HOME/.zshrc" + echo "Restored original .zshrc" >> "$LOG_FILE" +fi + +echo -e "\n${GREEN}Test completed!${NC}" +echo "Test completed at $(date)" >> "$LOG_FILE"