420 lines
9.7 KiB
Markdown
420 lines
9.7 KiB
Markdown
# 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
|
|
|