# Script Standards - Dubai Metaverse ## Overview This document defines standards and best practices for all scripts in the Dubai Metaverse project. All scripts should follow these guidelines for consistency, maintainability, and reliability. ## Script Template ### Bash Script Template ```bash #!/bin/bash # # Script Name: script_name.sh # Description: Brief description of what the script does # Usage: ./script_name.sh [options] [arguments] # Author: [Author Name] # Date: 2024-11-21 # Version: 1.0 # # Exit codes: # 0: Success # 1: General error # 2: Invalid arguments # 3: Missing dependencies # 4: Permission denied # 5: File/directory not found set -euo pipefail # Exit on error, undefined vars, pipe failures # Configuration SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" PROJECT_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)" LOG_FILE="${LOG_FILE:-$PROJECT_ROOT/logs/script_name.log}" # Colors for output RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' NC='\033[0m' # No Color # Logging function log() { local level="${1:-INFO}" shift local message="$*" local timestamp=$(date +'%Y-%m-%d %H:%M:%S') echo "[$timestamp] [$level] $message" | tee -a "$LOG_FILE" } # Error handling error_exit() { log "ERROR" "$*" exit 1 } # Warning function warning() { log "WARNING" "$*" } # Success function success() { log "SUCCESS" "$*" } # Validate prerequisites validate_prerequisites() { local missing_deps=() # Check required commands local required_commands=("git" "python3") for cmd in "${required_commands[@]}"; do if ! command -v "$cmd" &> /dev/null; then missing_deps+=("$cmd") fi done if [ ${#missing_deps[@]} -gt 0 ]; then error_exit "Missing required commands: ${missing_deps[*]}" fi # Check required files local required_files=("$PROJECT_ROOT/README.md") for file in "${required_files[@]}"; do if [ ! -f "$file" ]; then error_exit "Required file not found: $file" fi done # Check environment if [ -z "${REQUIRED_ENV_VAR:-}" ]; then warning "Environment variable REQUIRED_ENV_VAR not set" fi } # Print usage information usage() { cat << EOF Usage: $0 [OPTIONS] [ARGUMENTS] Description: Brief description of what the script does Options: -h, --help Show this help message -v, --verbose Enable verbose output -q, --quiet Suppress non-error output -d, --dry-run Perform a dry run without making changes Arguments: arg1 Description of first argument arg2 Description of second argument Examples: $0 --verbose arg1 arg2 $0 --dry-run Exit codes: 0 Success 1 General error 2 Invalid arguments 3 Missing dependencies EOF } # Parse command line arguments parse_args() { while [[ $# -gt 0 ]]; do case $1 in -h|--help) usage exit 0 ;; -v|--verbose) VERBOSE=true shift ;; -q|--quiet) QUIET=true shift ;; -d|--dry-run) DRY_RUN=true shift ;; *) error_exit "Unknown option: $1. Use --help for usage." ;; esac done } # Main function main() { log "INFO" "Starting script_name.sh" # Parse arguments parse_args "$@" # Validate prerequisites validate_prerequisites # Create log directory if it doesn't exist mkdir -p "$(dirname "$LOG_FILE")" # Script logic here log "INFO" "Performing main operations..." # Example: Check if running as root (if needed) # if [ "$EUID" -eq 0 ]; then # error_exit "This script should not be run as root" # fi # Example: Check if file exists # if [ ! -f "$file" ]; then # error_exit "File not found: $file" # fi # Example: Execute command with error handling # if ! command_to_run; then # error_exit "Command failed: command_to_run" # fi success "Script completed successfully" } # Run main if script is executed directly if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then main "$@" fi ``` ### Python Script Template ```python #!/usr/bin/env python3 """ Script Name: script_name.py Description: Brief description of what the script does Usage: python3 script_name.py [options] [arguments] Author: [Author Name] Date: 2024-11-21 Version: 1.0 """ import sys import os import argparse import logging from pathlib import Path from typing import List, Optional # Configuration SCRIPT_DIR = Path(__file__).parent.resolve() PROJECT_ROOT = SCRIPT_DIR.parent.resolve() LOG_FILE = PROJECT_ROOT / "logs" / "script_name.log" # Setup logging logging.basicConfig( level=logging.INFO, format='[%(asctime)s] [%(levelname)s] %(message)s', handlers=[ logging.FileHandler(LOG_FILE), logging.StreamHandler(sys.stdout) ] ) logger = logging.getLogger(__name__) def validate_prerequisites() -> None: """Validate that all prerequisites are met.""" missing_deps = [] # Check required commands required_commands = ["git", "python3"] for cmd in required_commands: if not shutil.which(cmd): missing_deps.append(cmd) if missing_deps: logger.error(f"Missing required commands: {', '.join(missing_deps)}") sys.exit(3) # Check required files required_files = [PROJECT_ROOT / "README.md"] for file in required_files: if not file.exists(): logger.error(f"Required file not found: {file}") sys.exit(5) def parse_args() -> argparse.Namespace: """Parse command line arguments.""" parser = argparse.ArgumentParser( description="Brief description of what the script does", formatter_class=argparse.RawDescriptionHelpFormatter ) parser.add_argument( "-v", "--verbose", action="store_true", help="Enable verbose output" ) parser.add_argument( "-q", "--quiet", action="store_true", help="Suppress non-error output" ) parser.add_argument( "-d", "--dry-run", action="store_true", help="Perform a dry run without making changes" ) parser.add_argument( "arg1", help="Description of first argument" ) return parser.parse_args() def main() -> int: """Main function.""" logger.info("Starting script_name.py") try: # Parse arguments args = parse_args() # Validate prerequisites validate_prerequisites() # Create log directory if it doesn't exist LOG_FILE.parent.mkdir(parents=True, exist_ok=True) # Script logic here logger.info("Performing main operations...") # Example: Check if file exists # file_path = Path("example.txt") # if not file_path.exists(): # logger.error(f"File not found: {file_path}") # return 1 logger.info("Script completed successfully") return 0 except KeyboardInterrupt: logger.warning("Script interrupted by user") return 130 except Exception as e: logger.error(f"Unexpected error: {e}", exc_info=True) return 1 if __name__ == "__main__": sys.exit(main()) ``` ## Standards ### Error Handling 1. **Always use `set -euo pipefail`** in bash scripts 2. **Handle errors explicitly** - don't ignore failures 3. **Provide meaningful error messages** 4. **Use appropriate exit codes** 5. **Log all errors** to log file ### Logging 1. **Log all important operations** 2. **Use consistent log format**: `[timestamp] [level] message` 3. **Log levels**: INFO, WARNING, ERROR, SUCCESS 4. **Log to both file and console** (tee) 5. **Create log directory** if it doesn't exist ### Input Validation 1. **Validate all user input** 2. **Check prerequisites** before execution 3. **Verify file/directory existence** 4. **Check command availability** 5. **Validate environment variables** ### Documentation 1. **Include header with description** 2. **Document all functions** 3. **Provide usage information** 4. **Include examples** 5. **Document exit codes** ### Code Quality 1. **Use meaningful variable names** 2. **Add comments for complex logic** 3. **Keep functions focused and small** 4. **Avoid hardcoded paths** (use variables) 5. **Follow consistent formatting** ## Exit Codes Standard exit codes for all scripts: - `0`: Success - `1`: General error - `2`: Invalid arguments - `3`: Missing dependencies - `4`: Permission denied - `5`: File/directory not found - `130`: Interrupted by user (Ctrl+C) ## Testing ### Before Committing 1. **Test with valid inputs** 2. **Test with invalid inputs** 3. **Test error conditions** 4. **Test edge cases** 5. **Verify exit codes** ### Test Checklist - [ ] Script runs without errors - [ ] Error handling works correctly - [ ] Logging functions properly - [ ] Help/usage information displays - [ ] Exit codes are correct - [ ] No hardcoded paths - [ ] Documentation is complete ## Best Practices 1. **Fail fast** - Check prerequisites early 2. **Be idempotent** - Scripts should be safe to run multiple times 3. **Provide feedback** - Inform user of progress 4. **Clean up** - Remove temporary files 5. **Be portable** - Avoid platform-specific code when possible ## Examples See existing scripts for examples: - `scripts/setup/setup_project.sh` - Setup script example - `scripts/validation/validate_project.sh` - Validation script example - `scripts/data/import_osm_data.py` - Python script example --- **Last Updated**: 2024 **Version**: 1.0