Task 11: Implement Deployment and Configuration
- Add production.py with security hardening and performance optimizations - Required environment variables for security (JWT, passwords, database) - Database connection pooling for PostgreSQL/MySQL - Security configurations and allowed hosts - Production logging and rotation settings - API rate limiting and performance tuning - Add development.py with relaxed settings for local development - Defaults for development (SQLite, debug logging, auto-reload) - Higher rate limits and longer session timeouts - Dev credentials for easy local setup - Development database defaults - Add environment configuration loader (__init__.py) - Automatic environment detection - Factory functions for lazy loading settings - Proper environment validation - Add startup scripts (start.sh) - Bash script for starting application in any environment - Conda environment validation - Automatic directory creation - Environment file generation - Database initialization - Development vs production startup modes - Add setup script (setup.py) - Python setup automation for environment initialization - Dependency installation - Environment file generation - Database initialization - Comprehensive validation and error handling - Update requirements.txt with psutil dependency All configurations follow project coding standards and include comprehensive documentation, type hints, and error handling.
This commit is contained in:
421
scripts/setup.py
Normal file
421
scripts/setup.py
Normal file
@@ -0,0 +1,421 @@
|
||||
"""
|
||||
Aniworld Application Setup Script
|
||||
|
||||
This script handles initial setup, dependency installation, database
|
||||
initialization, and configuration for the Aniworld application.
|
||||
|
||||
Usage:
|
||||
python setup.py [--environment {development|production}] [--no-deps]
|
||||
python setup.py --help
|
||||
"""
|
||||
|
||||
import argparse
|
||||
import asyncio
|
||||
import os
|
||||
import subprocess
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
class SetupManager:
|
||||
"""Manages application setup and initialization."""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
environment: str = "development",
|
||||
skip_deps: bool = False
|
||||
):
|
||||
"""
|
||||
Initialize setup manager.
|
||||
|
||||
Args:
|
||||
environment: Environment mode (development or production)
|
||||
skip_deps: Skip dependency installation
|
||||
"""
|
||||
self.environment = environment
|
||||
self.skip_deps = skip_deps
|
||||
self.project_root = Path(__file__).parent.parent
|
||||
self.conda_env = "AniWorld"
|
||||
|
||||
# ============================================================================
|
||||
# Logging
|
||||
# ============================================================================
|
||||
|
||||
@staticmethod
|
||||
def log_info(message: str) -> None:
|
||||
"""Log info message."""
|
||||
print(f"\033[34m[INFO]\033[0m {message}")
|
||||
|
||||
@staticmethod
|
||||
def log_success(message: str) -> None:
|
||||
"""Log success message."""
|
||||
print(f"\033[32m[SUCCESS]\033[0m {message}")
|
||||
|
||||
@staticmethod
|
||||
def log_warning(message: str) -> None:
|
||||
"""Log warning message."""
|
||||
print(f"\033[33m[WARNING]\033[0m {message}")
|
||||
|
||||
@staticmethod
|
||||
def log_error(message: str) -> None:
|
||||
"""Log error message."""
|
||||
print(f"\033[31m[ERROR]\033[0m {message}")
|
||||
|
||||
# ============================================================================
|
||||
# Validation
|
||||
# ============================================================================
|
||||
|
||||
def validate_environment(self) -> bool:
|
||||
"""
|
||||
Validate environment parameter.
|
||||
|
||||
Returns:
|
||||
True if valid, False otherwise
|
||||
"""
|
||||
valid_envs = {"development", "production", "testing"}
|
||||
if self.environment not in valid_envs:
|
||||
self.log_error(
|
||||
f"Invalid environment: {self.environment}. "
|
||||
f"Must be one of: {valid_envs}"
|
||||
)
|
||||
return False
|
||||
self.log_success(f"Environment: {self.environment}")
|
||||
return True
|
||||
|
||||
def check_conda_env(self) -> bool:
|
||||
"""
|
||||
Check if conda environment exists.
|
||||
|
||||
Returns:
|
||||
True if exists, False otherwise
|
||||
"""
|
||||
result = subprocess.run(
|
||||
["conda", "env", "list"],
|
||||
capture_output=True,
|
||||
text=True
|
||||
)
|
||||
if self.conda_env in result.stdout:
|
||||
self.log_success(f"Conda environment '{self.conda_env}' found")
|
||||
return True
|
||||
self.log_error(
|
||||
f"Conda environment '{self.conda_env}' not found. "
|
||||
f"Create with: conda create -n {self.conda_env} python=3.11"
|
||||
)
|
||||
return False
|
||||
|
||||
def check_python_version(self) -> bool:
|
||||
"""
|
||||
Check Python version.
|
||||
|
||||
Returns:
|
||||
True if version >= 3.9, False otherwise
|
||||
"""
|
||||
if sys.version_info < (3, 9):
|
||||
self.log_error(
|
||||
f"Python 3.9+ required. Current: {sys.version_info.major}."
|
||||
f"{sys.version_info.minor}"
|
||||
)
|
||||
return False
|
||||
self.log_success(
|
||||
f"Python version: {sys.version_info.major}."
|
||||
f"{sys.version_info.minor}"
|
||||
)
|
||||
return True
|
||||
|
||||
# ============================================================================
|
||||
# Directory Setup
|
||||
# ============================================================================
|
||||
|
||||
def create_directories(self) -> bool:
|
||||
"""
|
||||
Create necessary directories.
|
||||
|
||||
Returns:
|
||||
True if successful, False otherwise
|
||||
"""
|
||||
try:
|
||||
directories = [
|
||||
"logs",
|
||||
"data",
|
||||
"data/config_backups",
|
||||
"Temp",
|
||||
"tests",
|
||||
"scripts",
|
||||
]
|
||||
self.log_info("Creating directories...")
|
||||
for directory in directories:
|
||||
dir_path = self.project_root / directory
|
||||
dir_path.mkdir(parents=True, exist_ok=True)
|
||||
self.log_success("Directories created")
|
||||
return True
|
||||
except Exception as e:
|
||||
self.log_error(f"Failed to create directories: {e}")
|
||||
return False
|
||||
|
||||
# ============================================================================
|
||||
# Dependency Installation
|
||||
# ============================================================================
|
||||
|
||||
def install_dependencies(self) -> bool:
|
||||
"""
|
||||
Install Python dependencies.
|
||||
|
||||
Returns:
|
||||
True if successful, False otherwise
|
||||
"""
|
||||
if self.skip_deps:
|
||||
self.log_warning("Skipping dependency installation")
|
||||
return True
|
||||
|
||||
try:
|
||||
requirements_file = self.project_root / "requirements.txt"
|
||||
if not requirements_file.exists():
|
||||
self.log_error(
|
||||
f"requirements.txt not found at {requirements_file}"
|
||||
)
|
||||
return False
|
||||
|
||||
self.log_info("Installing dependencies...")
|
||||
subprocess.run(
|
||||
["conda", "run", "-n", self.conda_env,
|
||||
"pip", "install", "-q", "-r", str(requirements_file)],
|
||||
check=True
|
||||
)
|
||||
self.log_success("Dependencies installed")
|
||||
return True
|
||||
except subprocess.CalledProcessError as e:
|
||||
self.log_error(f"Failed to install dependencies: {e}")
|
||||
return False
|
||||
|
||||
# ============================================================================
|
||||
# Environment Configuration
|
||||
# ============================================================================
|
||||
|
||||
def create_env_files(self) -> bool:
|
||||
"""
|
||||
Create environment configuration files.
|
||||
|
||||
Returns:
|
||||
True if successful, False otherwise
|
||||
"""
|
||||
try:
|
||||
self.log_info("Creating environment configuration files...")
|
||||
|
||||
env_file = self.project_root / f".env.{self.environment}"
|
||||
if env_file.exists():
|
||||
self.log_warning(f"{env_file.name} already exists")
|
||||
return True
|
||||
|
||||
# Create environment file with defaults
|
||||
env_content = self._get_env_template()
|
||||
env_file.write_text(env_content)
|
||||
self.log_success(f"Created {env_file.name}")
|
||||
return True
|
||||
except Exception as e:
|
||||
self.log_error(f"Failed to create env files: {e}")
|
||||
return False
|
||||
|
||||
def _get_env_template(self) -> str:
|
||||
"""
|
||||
Get environment file template.
|
||||
|
||||
Returns:
|
||||
Environment file content
|
||||
"""
|
||||
if self.environment == "production":
|
||||
return """# Aniworld Production Configuration
|
||||
# IMPORTANT: Set these values before running in production
|
||||
|
||||
# Security (REQUIRED - generate new values)
|
||||
JWT_SECRET_KEY=change-this-to-a-secure-random-key
|
||||
PASSWORD_SALT=change-this-to-a-secure-random-salt
|
||||
MASTER_PASSWORD_HASH=change-this-to-hashed-password
|
||||
|
||||
# Database (REQUIRED - use PostgreSQL or MySQL in production)
|
||||
DATABASE_URL=postgresql://user:password@localhost/aniworld
|
||||
DATABASE_POOL_SIZE=20
|
||||
DATABASE_MAX_OVERFLOW=10
|
||||
|
||||
# Application
|
||||
ENVIRONMENT=production
|
||||
ANIME_DIRECTORY=/var/lib/aniworld
|
||||
TEMP_DIRECTORY=/tmp/aniworld
|
||||
|
||||
# Server
|
||||
HOST=0.0.0.0
|
||||
PORT=8000
|
||||
WORKERS=4
|
||||
|
||||
# Security
|
||||
CORS_ORIGINS=https://yourdomain.com
|
||||
ALLOWED_HOSTS=yourdomain.com
|
||||
|
||||
# Logging
|
||||
LOG_LEVEL=WARNING
|
||||
LOG_FILE=logs/production.log
|
||||
LOG_ROTATION_SIZE=10485760
|
||||
LOG_RETENTION_DAYS=30
|
||||
|
||||
# Performance
|
||||
API_RATE_LIMIT=60
|
||||
SESSION_TIMEOUT_HOURS=24
|
||||
MAX_CONCURRENT_DOWNLOADS=3
|
||||
"""
|
||||
else: # development
|
||||
return """# Aniworld Development Configuration
|
||||
|
||||
# Security (Development defaults - NOT for production)
|
||||
JWT_SECRET_KEY=dev-secret-key-change-in-production
|
||||
PASSWORD_SALT=dev-salt-change-in-production
|
||||
MASTER_PASSWORD_HASH=$2b$12$wP0KBVbJKVAb8CdSSXw0NeGTKCkbw4fSAFXIqR2/wDqPSEBn9w7lS
|
||||
MASTER_PASSWORD=password
|
||||
|
||||
# Database
|
||||
DATABASE_URL=sqlite:///./data/aniworld_dev.db
|
||||
|
||||
# Application
|
||||
ENVIRONMENT=development
|
||||
ANIME_DIRECTORY=/tmp/aniworld_dev
|
||||
TEMP_DIRECTORY=/tmp/aniworld_dev/temp
|
||||
|
||||
# Server
|
||||
HOST=127.0.0.1
|
||||
PORT=8000
|
||||
WORKERS=1
|
||||
|
||||
# Security
|
||||
CORS_ORIGINS=*
|
||||
|
||||
# Logging
|
||||
LOG_LEVEL=DEBUG
|
||||
LOG_FILE=logs/development.log
|
||||
|
||||
# Performance
|
||||
API_RATE_LIMIT=1000
|
||||
SESSION_TIMEOUT_HOURS=168
|
||||
MAX_CONCURRENT_DOWNLOADS=1
|
||||
"""
|
||||
|
||||
# ============================================================================
|
||||
# Database Initialization
|
||||
# ============================================================================
|
||||
|
||||
async def init_database(self) -> bool:
|
||||
"""
|
||||
Initialize database.
|
||||
|
||||
Returns:
|
||||
True if successful, False otherwise
|
||||
"""
|
||||
try:
|
||||
self.log_info("Initializing database...")
|
||||
# Import and run database initialization
|
||||
os.chdir(self.project_root)
|
||||
from src.server.database import init_db
|
||||
await init_db()
|
||||
self.log_success("Database initialized")
|
||||
return True
|
||||
except Exception as e:
|
||||
self.log_error(f"Failed to initialize database: {e}")
|
||||
return False
|
||||
|
||||
# ============================================================================
|
||||
# Summary
|
||||
# ============================================================================
|
||||
|
||||
def print_summary(self) -> None:
|
||||
"""Print setup summary."""
|
||||
self.log_info("=" * 50)
|
||||
self.log_info("Setup Summary")
|
||||
self.log_info("=" * 50)
|
||||
self.log_info(f"Environment: {self.environment}")
|
||||
self.log_info(f"Conda Environment: {self.conda_env}")
|
||||
self.log_info(f"Project Root: {self.project_root}")
|
||||
self.log_info("")
|
||||
self.log_success("Setup complete!")
|
||||
self.log_info("")
|
||||
self.log_info("Next steps:")
|
||||
self.log_info("1. Configure .env files with your settings")
|
||||
if self.environment == "production":
|
||||
self.log_info("2. Set up database (PostgreSQL/MySQL)")
|
||||
self.log_info("3. Configure security settings")
|
||||
self.log_info("4. Run: ./scripts/start.sh production")
|
||||
else:
|
||||
self.log_info("2. Run: ./scripts/start.sh development")
|
||||
self.log_info("")
|
||||
|
||||
# ============================================================================
|
||||
# Main Setup
|
||||
# ============================================================================
|
||||
|
||||
async def run(self) -> int:
|
||||
"""
|
||||
Run setup process.
|
||||
|
||||
Returns:
|
||||
0 if successful, 1 otherwise
|
||||
"""
|
||||
print("\033[34m" + "=" * 50 + "\033[0m")
|
||||
print("\033[34mAniworld Application Setup\033[0m")
|
||||
print("\033[34m" + "=" * 50 + "\033[0m")
|
||||
print()
|
||||
|
||||
# Validation
|
||||
if not self.validate_environment():
|
||||
return 1
|
||||
if not self.check_python_version():
|
||||
return 1
|
||||
if not self.check_conda_env():
|
||||
return 1
|
||||
|
||||
# Setup
|
||||
if not self.create_directories():
|
||||
return 1
|
||||
if not self.create_env_files():
|
||||
return 1
|
||||
if not self.install_dependencies():
|
||||
return 1
|
||||
|
||||
# Initialize database
|
||||
if not await self.init_database():
|
||||
return 1
|
||||
|
||||
# Summary
|
||||
self.print_summary()
|
||||
return 0
|
||||
|
||||
|
||||
async def main() -> int:
|
||||
"""
|
||||
Main entry point.
|
||||
|
||||
Returns:
|
||||
Exit code
|
||||
"""
|
||||
parser = argparse.ArgumentParser(
|
||||
description="Aniworld Application Setup"
|
||||
)
|
||||
parser.add_argument(
|
||||
"--environment",
|
||||
choices=["development", "production", "testing"],
|
||||
default="development",
|
||||
help="Environment to set up (default: development)"
|
||||
)
|
||||
parser.add_argument(
|
||||
"--no-deps",
|
||||
action="store_true",
|
||||
help="Skip dependency installation"
|
||||
)
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
setup = SetupManager(
|
||||
environment=args.environment,
|
||||
skip_deps=args.no_deps
|
||||
)
|
||||
return await setup.run()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
exit_code = asyncio.run(main())
|
||||
sys.exit(exit_code)
|
||||
245
scripts/start.sh
Normal file
245
scripts/start.sh
Normal file
@@ -0,0 +1,245 @@
|
||||
#!/bin/bash
|
||||
|
||||
################################################################################
|
||||
# Aniworld Application Startup Script
|
||||
#
|
||||
# This script initializes the development or production environment,
|
||||
# installs dependencies, sets up the database, and starts the application.
|
||||
#
|
||||
# Usage:
|
||||
# ./start.sh [development|production] [--no-install] [--no-migrate]
|
||||
#
|
||||
# Environment Variables:
|
||||
# ENVIRONMENT: 'development' or 'production' (default: development)
|
||||
# CONDA_ENV: Conda environment name (default: AniWorld)
|
||||
# PORT: Server port (default: 8000)
|
||||
# HOST: Server host (default: 127.0.0.1)
|
||||
#
|
||||
################################################################################
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
# ============================================================================
|
||||
# Configuration
|
||||
# ============================================================================
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
PROJECT_ROOT="$(dirname "$SCRIPT_DIR")"
|
||||
CONDA_ENV="${CONDA_ENV:-AniWorld}"
|
||||
ENVIRONMENT="${1:-development}"
|
||||
INSTALL_DEPS="${INSTALL_DEPS:-true}"
|
||||
RUN_MIGRATIONS="${RUN_MIGRATIONS:-true}"
|
||||
PORT="${PORT:-8000}"
|
||||
HOST="${HOST:-127.0.0.1}"
|
||||
|
||||
# ============================================================================
|
||||
# Color Output
|
||||
# ============================================================================
|
||||
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
BLUE='\033[0;34m'
|
||||
NC='\033[0m' # No Color
|
||||
|
||||
# ============================================================================
|
||||
# Functions
|
||||
# ============================================================================
|
||||
|
||||
log_info() {
|
||||
echo -e "${BLUE}[INFO]${NC} $1"
|
||||
}
|
||||
|
||||
log_success() {
|
||||
echo -e "${GREEN}[SUCCESS]${NC} $1"
|
||||
}
|
||||
|
||||
log_warning() {
|
||||
echo -e "${YELLOW}[WARNING]${NC} $1"
|
||||
}
|
||||
|
||||
log_error() {
|
||||
echo -e "${RED}[ERROR]${NC} $1"
|
||||
}
|
||||
|
||||
# Check if conda environment exists
|
||||
check_conda_env() {
|
||||
if ! conda env list | grep -q "^$CONDA_ENV "; then
|
||||
log_error "Conda environment '$CONDA_ENV' not found."
|
||||
log_info "Create it with: conda create -n $CONDA_ENV python=3.11"
|
||||
exit 1
|
||||
fi
|
||||
log_success "Conda environment '$CONDA_ENV' found."
|
||||
}
|
||||
|
||||
# Validate environment parameter
|
||||
validate_environment() {
|
||||
if [[ ! "$ENVIRONMENT" =~ ^(development|production|testing)$ ]]; then
|
||||
log_error "Invalid environment: $ENVIRONMENT"
|
||||
log_info "Valid options: development, production, testing"
|
||||
exit 1
|
||||
fi
|
||||
log_success "Environment set to: $ENVIRONMENT"
|
||||
}
|
||||
|
||||
# Create necessary directories
|
||||
create_directories() {
|
||||
log_info "Creating necessary directories..."
|
||||
mkdir -p "$PROJECT_ROOT/logs"
|
||||
mkdir -p "$PROJECT_ROOT/data"
|
||||
mkdir -p "$PROJECT_ROOT/data/config_backups"
|
||||
mkdir -p "$PROJECT_ROOT/Temp"
|
||||
log_success "Directories created."
|
||||
}
|
||||
|
||||
# Install dependencies
|
||||
install_dependencies() {
|
||||
if [[ "$INSTALL_DEPS" != "true" ]]; then
|
||||
log_warning "Skipping dependency installation."
|
||||
return
|
||||
fi
|
||||
|
||||
log_info "Installing dependencies..."
|
||||
conda run -n "$CONDA_ENV" pip install -q -r "$PROJECT_ROOT/requirements.txt"
|
||||
log_success "Dependencies installed."
|
||||
}
|
||||
|
||||
# Run database migrations
|
||||
run_migrations() {
|
||||
if [[ "$RUN_MIGRATIONS" != "true" ]]; then
|
||||
log_warning "Skipping database migrations."
|
||||
return
|
||||
fi
|
||||
|
||||
log_info "Running database migrations..."
|
||||
cd "$PROJECT_ROOT"
|
||||
conda run -n "$CONDA_ENV" \
|
||||
python -m alembic upgrade head 2>/dev/null || log_warning "No migrations to run."
|
||||
log_success "Database migrations completed."
|
||||
}
|
||||
|
||||
# Initialize database
|
||||
init_database() {
|
||||
log_info "Initializing database..."
|
||||
cd "$PROJECT_ROOT"
|
||||
conda run -n "$CONDA_ENV" \
|
||||
python -c "from src.server.database import init_db; import asyncio; asyncio.run(init_db())"
|
||||
log_success "Database initialized."
|
||||
}
|
||||
|
||||
# Create environment file if it doesn't exist
|
||||
create_env_file() {
|
||||
ENV_FILE="$PROJECT_ROOT/.env.$ENVIRONMENT"
|
||||
if [[ ! -f "$ENV_FILE" ]]; then
|
||||
log_warning "Creating $ENV_FILE with defaults..."
|
||||
cat > "$ENV_FILE" << EOF
|
||||
# Aniworld Configuration for $ENVIRONMENT
|
||||
|
||||
# Security Settings
|
||||
JWT_SECRET_KEY=your-secret-key-here
|
||||
PASSWORD_SALT=your-salt-here
|
||||
MASTER_PASSWORD_HASH=\$2b\$12\$wP0KBVbJKVAb8CdSSXw0NeGTKCkbw4fSAFXIqR2/wDqPSEBn9w7lS
|
||||
|
||||
# Database
|
||||
DATABASE_URL=sqlite:///./data/aniworld_${ENVIRONMENT}.db
|
||||
|
||||
# Application
|
||||
ENVIRONMENT=${ENVIRONMENT}
|
||||
ANIME_DIRECTORY=/path/to/anime
|
||||
|
||||
# Server
|
||||
PORT=${PORT}
|
||||
HOST=${HOST}
|
||||
|
||||
# Logging
|
||||
LOG_LEVEL=$([ "$ENVIRONMENT" = "production" ] && echo "WARNING" || echo "DEBUG")
|
||||
|
||||
# Features (development only)
|
||||
$([ "$ENVIRONMENT" = "development" ] && echo "DEBUG=true" || echo "DEBUG=false")
|
||||
EOF
|
||||
log_success "Created $ENV_FILE - please configure with your settings"
|
||||
fi
|
||||
}
|
||||
|
||||
# Start the application
|
||||
start_application() {
|
||||
log_info "Starting Aniworld application..."
|
||||
log_info "Environment: $ENVIRONMENT"
|
||||
log_info "Conda Environment: $CONDA_ENV"
|
||||
log_info "Server: http://$HOST:$PORT"
|
||||
|
||||
cd "$PROJECT_ROOT"
|
||||
|
||||
case "$ENVIRONMENT" in
|
||||
development)
|
||||
log_info "Starting in development mode with auto-reload..."
|
||||
conda run -n "$CONDA_ENV" \
|
||||
python -m uvicorn \
|
||||
src.server.fastapi_app:app \
|
||||
--host "$HOST" \
|
||||
--port "$PORT" \
|
||||
--reload
|
||||
;;
|
||||
production)
|
||||
WORKERS="${WORKERS:-4}"
|
||||
log_info "Starting in production mode with $WORKERS workers..."
|
||||
conda run -n "$CONDA_ENV" \
|
||||
python -m uvicorn \
|
||||
src.server.fastapi_app:app \
|
||||
--host "$HOST" \
|
||||
--port "$PORT" \
|
||||
--workers "$WORKERS" \
|
||||
--worker-class "uvicorn.workers.UvicornWorker"
|
||||
;;
|
||||
testing)
|
||||
log_warning "Starting in testing mode..."
|
||||
# Testing mode typically runs tests instead of starting server
|
||||
conda run -n "$CONDA_ENV" \
|
||||
python -m pytest tests/ -v --tb=short
|
||||
;;
|
||||
*)
|
||||
log_error "Unknown environment: $ENVIRONMENT"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
# ============================================================================
|
||||
# Main Script
|
||||
# ============================================================================
|
||||
|
||||
main() {
|
||||
log_info "=========================================="
|
||||
log_info "Aniworld Application Startup"
|
||||
log_info "=========================================="
|
||||
|
||||
# Parse command-line options
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case "$1" in
|
||||
--no-install)
|
||||
INSTALL_DEPS="false"
|
||||
shift
|
||||
;;
|
||||
--no-migrate)
|
||||
RUN_MIGRATIONS="false"
|
||||
shift
|
||||
;;
|
||||
*)
|
||||
ENVIRONMENT="$1"
|
||||
shift
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
validate_environment
|
||||
check_conda_env
|
||||
create_directories
|
||||
create_env_file
|
||||
install_dependencies
|
||||
init_database
|
||||
run_migrations
|
||||
start_application
|
||||
}
|
||||
|
||||
# Run main function
|
||||
main "$@"
|
||||
Reference in New Issue
Block a user