diff --git a/projects/02-command-line-mastery/play.sh b/projects/02-command-line-mastery/play.sh index 7167a80..5e1ca2a 100644 --- a/projects/02-command-line-mastery/play.sh +++ b/projects/02-command-line-mastery/play.sh @@ -8,44 +8,183 @@ CHALLENGE_DIR=~/challenge GREEN='\033[0;32m' YELLOW='\033[1;33m' BLUE='\033[0;34m' +RED='\033[0;31m' NC='\033[0m' +# Error handling +set -o errexit # Exit on error +set -o nounset # Exit on undefined variable +set -o pipefail # Exit on pipe failure + +# Trap errors and provide error message +trap 'echo -e "${RED}Error: Command failed at line $LINENO${NC}" >&2' ERR + +# Function to check if command exists +check_command() { + if ! command -v "$1" &>/dev/null; then + echo -e "${RED}Error: Required command '$1' not found${NC}" + return 1 + fi +} + +# Function to check required commands +check_requirements() { + local required_commands=("find" "grep" "awk" "sed" "mkdir" "chmod" "touch") + local missing_commands=() + + for cmd in "${required_commands[@]}"; do + if ! check_command "$cmd"; then + missing_commands+=("$cmd") + fi + done + + if [[ ${#missing_commands[@]} -gt 0 ]]; then + echo -e "${RED}Error: Missing required commands: ${missing_commands[*]}${NC}" + return 1 + fi +} + +# Function to clean up previous environment +cleanup_environment() { + echo -e "${BLUE}Cleaning up previous environment...${NC}" + if [[ -d "$CHALLENGE_DIR" ]]; then + if ! rm -rf "$CHALLENGE_DIR" 2>/dev/null; then + echo -e "${RED}Error: Failed to remove directory: $CHALLENGE_DIR${NC}" + echo -e "${YELLOW}Tip: Check if you have proper permissions${NC}" + return 1 + fi + echo -e "${GREEN}Previous environment cleaned up${NC}" + fi +} + # Function to setup the environment setup_environment() { echo -e "${BLUE}Setting up challenge environment...${NC}" - # Create directory structure - mkdir -p "$CHALLENGE_DIR"/{files,backup,temp} - mkdir -p "$CHALLENGE_DIR/.tracking/data" + # Check requirements first + if ! check_requirements; then + echo -e "${RED}Error: Setup failed - missing requirements${NC}" + return 1 + } - # Copy all setup scripts to tracking directory - cp "$PROJECT_DIR"/setup/*.sh "$CHALLENGE_DIR/.tracking/" - chmod +x "$CHALLENGE_DIR/.tracking/"*.sh + # Clean up first + cleanup_environment - # Initialize tracking files + # Create directory structure with error checking + local dirs=( + "$CHALLENGE_DIR"/{files,backup,temp} + "$CHALLENGE_DIR/.tracking/data" + ) + + for dir in "${dirs[@]}"; do + if ! mkdir -p "$dir"; then + echo -e "${RED}Error: Failed to create directory: $dir${NC}" + return 1 + fi + done + + # Create project directories with error checking + for i in {1..5}; do + local project_dirs=( + "$CHALLENGE_DIR/files/project$i"/{src,config,logs,data} + ) + for dir in "${project_dirs[@]}"; do + if ! mkdir -p "$dir"; then + echo -e "${RED}Error: Failed to create project directory: $dir${NC}" + return 1 + fi + done + done + + # Copy setup scripts with error checking + if ! cp "$PROJECT_DIR"/setup/*.sh "$CHALLENGE_DIR/.tracking/" 2>/dev/null; then + echo -e "${RED}Error: Failed to copy setup scripts${NC}" + echo -e "${YELLOW}Tip: Check if setup scripts exist in: $PROJECT_DIR/setup/${NC}" + return 1 + fi + + # Set permissions with error checking + if ! chmod +x "$CHALLENGE_DIR/.tracking/"*.sh 2>/dev/null; then + echo -e "${RED}Error: Failed to set script permissions${NC}" + return 1 + fi + + # Initialize tracking files with error checking local data_dir="$CHALLENGE_DIR/.tracking/data" - touch "$data_dir"/{progress,score,challenge,commands,time,hints_used,achievements,attempts}.csv + local tracking_files=(progress score challenge commands time hints_used achievements attempts) + + for file in "${tracking_files[@]}"; do + if ! touch "$data_dir/$file.csv" 2>/dev/null; then + echo -e "${RED}Error: Failed to create tracking file: $file.csv${NC}" + return 1 + fi + done echo -e "${GREEN}Environment setup complete!${NC}" } +# Function to validate challenge ID +validate_challenge_id() { + local id=$1 + if [[ ! $id =~ ^[1-9]\.[1-5]$ ]]; then + echo -e "${RED}Error: Invalid challenge ID format${NC}" + echo -e "${YELLOW}Tip: Challenge ID should be in format 'level.number' (e.g., 1.1)${NC}" + return 1 + fi +} + # Function to start a challenge start_challenge() { local challenge_id=$1 - # Source required scripts - source "$CHALLENGE_DIR/.tracking/common.sh" - source "$CHALLENGE_DIR/.tracking/challenges.sh" + # Validate challenge ID + if ! validate_challenge_id "$challenge_id"; then + return 1 + fi + + # Source required scripts with error checking + local required_scripts=(common.sh challenges.sh) + for script in "${required_scripts[@]}"; do + if ! source "$CHALLENGE_DIR/.tracking/$script" 2>/dev/null; then + echo -e "${RED}Error: Required script not found: $script${NC}" + echo -e "${YELLOW}Running setup first...${NC}" + setup_environment + if ! source "$CHALLENGE_DIR/.tracking/$script" 2>/dev/null; then + echo -e "${RED}Error: Failed to source $script even after setup${NC}" + return 1 + fi + fi + done clear echo -e "${GREEN}🎮 Command Line Mastery Challenge${NC}" echo "==============================" - get_challenge_details "$challenge_id" - bash "$CHALLENGE_DIR/.tracking/level1-setup.sh" + # Setup challenge environment with error checking + if ! get_challenge_details "$challenge_id" 2>/dev/null; then + echo -e "${RED}Error: Challenge $challenge_id not found${NC}" + echo -e "${YELLOW}Available challenges:${NC}" + list_challenges + return 1 + fi + if ! bash "$CHALLENGE_DIR/.tracking/level1-setup.sh" 2>/dev/null; then + echo -e "${RED}Error: Failed to setup challenge environment${NC}" + echo -e "${YELLOW}Try running: $0 clean && $0 setup${NC}" + return 1 + fi + + # Record challenge start with error checking local start_time=$(date +%s) - echo "$challenge_id,$start_time" > "$CHALLENGE_DIR/.tracking/current_challenge" + if ! echo "$challenge_id,$start_time" > "$CHALLENGE_DIR/.tracking/current_challenge" 2>/dev/null; then + echo -e "${RED}Error: Failed to record challenge start${NC}" + return 1 + fi + + # Create command aliases + alias verify="$CHALLENGE_DIR/.tracking/verify-challenge.sh" + alias hint="$CHALLENGE_DIR/.tracking/show-hint.sh" + alias status="$CHALLENGE_DIR/.tracking/track-progress.sh" echo -e "\n${GREEN}Challenge ready!${NC}" echo -e "Commands available:" @@ -63,46 +202,57 @@ Commands: start Start a specific challenge list List available challenges setup Setup/reset the environment + clean Remove all challenge files help Show this help message Examples: $0 setup # First time setup + $0 clean # Remove all challenge files $0 list # Show available challenges $0 start 1.1 # Start challenge 1.1 EOF } -# Main execution -case "$1" in - "setup") - setup_environment - ;; - "start") - if [[ ! -d "$CHALLENGE_DIR/.tracking" ]]; then - echo -e "${YELLOW}Environment not set up. Running setup first...${NC}" +# Main execution with error handling +main() { + case "$1" in + "setup") setup_environment - fi - if [[ -z "$2" ]]; then - echo "Error: Please specify a challenge ID" - echo "Example: $0 start 1.1" - exit 1 - fi - start_challenge "$2" - ;; - "list") - if [[ -f "$CHALLENGE_DIR/.tracking/challenges.sh" ]]; then - source "$CHALLENGE_DIR/.tracking/challenges.sh" + ;; + "clean") + cleanup_environment + ;; + "start") + if [[ -z "${2:-}" ]]; then + echo -e "${RED}Error: Please specify a challenge ID${NC}" + echo "Example: $0 start 1.1" + return 1 + fi + start_challenge "$2" + ;; + "list") + if [[ ! -f "$CHALLENGE_DIR/.tracking/challenges.sh" ]]; then + setup_environment + fi + if ! source "$CHALLENGE_DIR/.tracking/challenges.sh" 2>/dev/null; then + echo -e "${RED}Error: Failed to load challenges${NC}" + return 1 + fi list_challenges - else - echo "Please run setup first: $0 setup" - fi - ;; - "help"|"--help"|"-h"|"") - show_help - ;; - *) - echo "Unknown command: $1" - show_help - exit 1 - ;; -esac \ No newline at end of file + ;; + "help"|"--help"|"-h"|"") + show_help + ;; + *) + echo -e "${RED}Unknown command: $1${NC}" + show_help + return 1 + ;; + esac +} + +# Run main with error handling +if ! main "${@:-}"; then + echo -e "${RED}Command failed. See errors above.${NC}" + exit 1 +fi \ No newline at end of file