Features: 1) Introduce CLI utility lessy.py to streamline project management tasks (e.g., install, run, restart, test); 2) Add Unix and Windows script commands for make-messages and compile-messages to improve translation workflow; 3) Include shared utility libraries (utils.sh, utils.ps1) for reusable functions across scripts.
Fixes: 1) Remove obsolete `reboot` scripts for Unix and Windows to prevent redundancy; 2) Update Windows `test.ps1` to handle omitted coverage patterns and improve error feedback. Extra: 1) Refactor Windows scripts (`make-messages.ps1`, `compile-messages.ps1`, `backup.ps1`) to use shared utilities for better consistency and output formatting; 2) Add spinner-based progress indicators to enhance user experience in interactive environments.
This commit is contained in:
parent
4c7b40b899
commit
42b40627de
23 changed files with 1218 additions and 237 deletions
129
lessy.py
Executable file
129
lessy.py
Executable file
|
|
@ -0,0 +1,129 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
import platform
|
||||||
|
import subprocess
|
||||||
|
import sys
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
import click
|
||||||
|
|
||||||
|
OS = platform.system().lower()
|
||||||
|
SCRIPT_EXT = ".ps1" if OS == "windows" else ".sh"
|
||||||
|
SCRIPT_DIR = "Windows" if OS == "windows" else "Unix"
|
||||||
|
PROJECT_ROOT = Path(__file__).parent.absolute()
|
||||||
|
SCRIPTS_PATH = PROJECT_ROOT / "scripts" / SCRIPT_DIR
|
||||||
|
|
||||||
|
|
||||||
|
def get_script_path(command: str) -> Path:
|
||||||
|
return SCRIPTS_PATH / f"{command}{SCRIPT_EXT}"
|
||||||
|
|
||||||
|
|
||||||
|
def run_script(script_name: str, *args) -> int:
|
||||||
|
script_path = get_script_path(script_name)
|
||||||
|
|
||||||
|
if not script_path.exists():
|
||||||
|
click.secho(f"Error: Script '{script_name}' not found at {script_path}", fg="red", err=True)
|
||||||
|
return 1
|
||||||
|
|
||||||
|
if OS == "windows":
|
||||||
|
cmd = ["pwsh", "-File", str(script_path)]
|
||||||
|
else:
|
||||||
|
cmd = ["bash", str(script_path)]
|
||||||
|
|
||||||
|
cmd.extend(args)
|
||||||
|
|
||||||
|
try:
|
||||||
|
result = subprocess.run(cmd, cwd=PROJECT_ROOT)
|
||||||
|
return result.returncode
|
||||||
|
except FileNotFoundError:
|
||||||
|
shell_name = "PowerShell" if OS == "windows" else "bash"
|
||||||
|
click.secho(f"Error: {shell_name} not found. Please ensure it's installed.", fg="red", err=True)
|
||||||
|
return 127
|
||||||
|
except KeyboardInterrupt:
|
||||||
|
click.secho("\nOperation cancelled by user.", fg="yellow")
|
||||||
|
return 130
|
||||||
|
|
||||||
|
|
||||||
|
@click.group(
|
||||||
|
context_settings={"help_option_names": ["-h", "--help"]},
|
||||||
|
invoke_without_command=True,
|
||||||
|
)
|
||||||
|
@click.pass_context
|
||||||
|
def cli(ctx):
|
||||||
|
if ctx.invoked_subcommand is None:
|
||||||
|
click.echo(ctx.get_help())
|
||||||
|
|
||||||
|
|
||||||
|
@cli.command()
|
||||||
|
def install():
|
||||||
|
return sys.exit(run_script("install"))
|
||||||
|
|
||||||
|
|
||||||
|
@cli.command()
|
||||||
|
def run():
|
||||||
|
return sys.exit(run_script("run"))
|
||||||
|
|
||||||
|
|
||||||
|
@cli.command()
|
||||||
|
def restart():
|
||||||
|
return sys.exit(run_script("restart"))
|
||||||
|
|
||||||
|
|
||||||
|
@cli.command()
|
||||||
|
@click.option("-r", "--report", type=click.Choice(["xml", "html"]), help="Generate coverage report (xml or html)")
|
||||||
|
def test(report):
|
||||||
|
args = []
|
||||||
|
if report:
|
||||||
|
args.extend(["-r", report])
|
||||||
|
return sys.exit(run_script("test", *args))
|
||||||
|
|
||||||
|
|
||||||
|
@cli.command()
|
||||||
|
def uninstall():
|
||||||
|
if click.confirm("This will remove all Docker containers, volumes, and generated files. Continue?"):
|
||||||
|
return sys.exit(run_script("uninstall"))
|
||||||
|
else:
|
||||||
|
click.secho("Uninstall cancelled.", fg="yellow")
|
||||||
|
return 0
|
||||||
|
|
||||||
|
|
||||||
|
@cli.command()
|
||||||
|
def backup():
|
||||||
|
return sys.exit(run_script("backup"))
|
||||||
|
|
||||||
|
|
||||||
|
@cli.command(name="generate-env")
|
||||||
|
def generate_env():
|
||||||
|
return sys.exit(run_script("generate-environment-file"))
|
||||||
|
|
||||||
|
|
||||||
|
@cli.command(name="export-env")
|
||||||
|
def export_env():
|
||||||
|
return sys.exit(run_script("export-environment-file"))
|
||||||
|
|
||||||
|
|
||||||
|
@cli.command(name="make-messages")
|
||||||
|
def make_messages():
|
||||||
|
return sys.exit(run_script("make-messages"))
|
||||||
|
|
||||||
|
|
||||||
|
@cli.command(name="compile-messages")
|
||||||
|
def compile_messages():
|
||||||
|
return sys.exit(run_script("compile-messages"))
|
||||||
|
|
||||||
|
|
||||||
|
@cli.command()
|
||||||
|
def info():
|
||||||
|
click.echo(f"{'='*60}")
|
||||||
|
click.secho("lessy - eVibes Project CLI", fg="cyan", bold=True)
|
||||||
|
click.echo(f"{'='*60}")
|
||||||
|
click.echo(f"Operating System: {platform.system()} ({platform.release()})")
|
||||||
|
click.echo(f"Python Version: {platform.python_version()}")
|
||||||
|
click.echo(f"Architecture: {platform.machine()}")
|
||||||
|
click.echo(f"Project Root: {PROJECT_ROOT}")
|
||||||
|
click.echo(f"Scripts Directory: {SCRIPTS_PATH}")
|
||||||
|
click.echo(f"Script Extension: {SCRIPT_EXT}")
|
||||||
|
click.echo(f"{'='*60}")
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
cli()
|
||||||
|
|
@ -3,10 +3,21 @@ set -euo pipefail
|
||||||
|
|
||||||
source ./scripts/Unix/starter.sh
|
source ./scripts/Unix/starter.sh
|
||||||
|
|
||||||
echo "Starting database backup process..."
|
# Database backup
|
||||||
docker compose exec app uv run manage.py dbbackup
|
log_step "Starting database backup process..."
|
||||||
echo "Database backup created under ./dbbackup"
|
if ! docker compose exec app uv run manage.py dbbackup; then
|
||||||
|
log_error "Database backup failed"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
log_success "Database backup created under ./dbbackup"
|
||||||
|
|
||||||
echo "Starting media backup process..."
|
# Media backup
|
||||||
docker compose exec app uv run manage.py mediabackup
|
log_step "Starting media backup process..."
|
||||||
echo "Media backup created under ./dbbackup"
|
if ! docker compose exec app uv run manage.py mediabackup; then
|
||||||
|
log_error "Media backup failed"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
log_success "Media backup created under ./dbbackup"
|
||||||
|
|
||||||
|
echo
|
||||||
|
log_result "Backup completed successfully!"
|
||||||
|
|
|
||||||
28
scripts/Unix/compile-messages.sh
Executable file
28
scripts/Unix/compile-messages.sh
Executable file
|
|
@ -0,0 +1,28 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
source ./scripts/Unix/starter.sh
|
||||||
|
|
||||||
|
if [ ! -f .env ]; then
|
||||||
|
log_warning ".env file not found. Exiting without running Docker steps."
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Check placeholders
|
||||||
|
log_step "Checking placeholders in PO files..."
|
||||||
|
if ! docker compose exec app uv run manage.py check_translated -l ALL -a ALL; then
|
||||||
|
log_error "PO files have placeholder issues"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
log_success "PO files have no placeholder issues!"
|
||||||
|
|
||||||
|
# Compile messages
|
||||||
|
log_step "Compiling PO files into MO files..."
|
||||||
|
if ! docker compose exec app uv run manage.py compilemessages -l ar_AR -l cs_CZ -l da_DK -l de_DE -l en_GB -l en_US -l es_ES -l fa_IR -l fr_FR -l he_IL -l hi_IN -l hr_HR -l id_ID -l it_IT -l ja_JP -l kk_KZ -l ko_KR -l nl_NL -l no_NO -l pl_PL -l pt_BR -l ro_RO -l ru_RU -l sv_SE -l th_TH -l tr_TR -l vi_VN -l zh_Hans; then
|
||||||
|
log_error "Failed to compile messages"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
log_success "Compiled successfully!"
|
||||||
|
|
||||||
|
echo
|
||||||
|
log_result "Translation compilation complete!"
|
||||||
|
|
@ -4,41 +4,30 @@ set -euo pipefail
|
||||||
source ./scripts/Unix/starter.sh
|
source ./scripts/Unix/starter.sh
|
||||||
|
|
||||||
if [ ! -f .env ]; then
|
if [ ! -f .env ]; then
|
||||||
echo ".env file not found. Exiting without running Docker steps." >&2
|
log_warning ".env file not found. Exiting without running Docker steps."
|
||||||
exit 0
|
exit 0
|
||||||
fi
|
fi
|
||||||
|
|
||||||
cpu_count=$(getconf _NPROCESSORS_ONLN)
|
# Check system requirements
|
||||||
if [ "$cpu_count" -lt 4 ]; then
|
if ! check_system_requirements 4 6 20; then
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [ -f /proc/meminfo ]; then
|
# Pull Docker images
|
||||||
mem_kb=$(grep MemTotal /proc/meminfo | awk '{print $2}')
|
log_step "Pulling images..."
|
||||||
total_mem_gb=$(awk "BEGIN {printf \"%.2f\", $mem_kb/1024/1024}")
|
if ! docker compose pull; then
|
||||||
else
|
log_error "Failed to pull Docker images"
|
||||||
total_mem_bytes=$(sysctl -n hw.memsize)
|
|
||||||
total_mem_gb=$(awk "BEGIN {printf \"%.2f\", $total_mem_bytes/1024/1024/1024}")
|
|
||||||
fi
|
|
||||||
if ! awk "BEGIN {exit !($total_mem_gb >= 6)}"; then
|
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
log_success "Images pulled successfully"
|
||||||
|
|
||||||
avail_kb=$(df -k . | tail -1 | awk '{print $4}')
|
# Build Docker images
|
||||||
free_gb=$(awk "BEGIN {printf \"%.2f\", $avail_kb/1024/1024}")
|
log_step "Building images..."
|
||||||
if ! awk "BEGIN {exit !($free_gb >= 20)}"; then
|
if ! docker compose build; then
|
||||||
|
log_error "Failed to build Docker images"
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
log_success "Images built successfully"
|
||||||
echo "System requirements met: CPU cores=$cpu_count, RAM=${total_mem_gb}GB, FreeDisk=${free_gb}GB"
|
|
||||||
|
|
||||||
echo "Pulling images"
|
|
||||||
docker compose pull
|
|
||||||
echo "Images pulled successfully"
|
|
||||||
|
|
||||||
echo "Building images"
|
|
||||||
docker compose build
|
|
||||||
echo "Images built successfully"
|
|
||||||
|
|
||||||
echo
|
echo
|
||||||
echo "You can now use run.sh script."
|
log_result "You can now use run.sh script or run: ./lessy.py run"
|
||||||
|
|
|
||||||
44
scripts/Unix/make-messages.sh
Executable file
44
scripts/Unix/make-messages.sh
Executable file
|
|
@ -0,0 +1,44 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
source ./scripts/Unix/starter.sh
|
||||||
|
|
||||||
|
if [ ! -f .env ]; then
|
||||||
|
log_warning ".env file not found. Exiting without running Docker steps."
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Remove old fuzzy entries
|
||||||
|
log_step "Remove old fuzzy entries..."
|
||||||
|
if ! docker compose exec app uv run manage.py fix_fuzzy; then
|
||||||
|
log_error "Failed to remove old fuzzy entries"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
log_success "Old fuzzy entries removed successfully!"
|
||||||
|
|
||||||
|
# Update PO files
|
||||||
|
log_step "Updating PO files..."
|
||||||
|
if ! docker compose exec app uv run manage.py makemessages -l ar_AR -l cs_CZ -l da_DK -l de_DE -l en_GB -l en_US -l es_ES -l fa_IR -l fr_FR -l he_IL -l hi_IN -l hr_HR -l id_ID -l it_IT -l ja_JP -l kk_KZ -l ko_KR -l nl_NL -l no_NO -l pl_PL -l pt_BR -l ro_RO -l ru_RU -l sv_SE -l th_TH -l tr_TR -l vi_VN -l zh_Hans; then
|
||||||
|
log_error "Failed to update PO files"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
log_success "PO files updated successfully!"
|
||||||
|
|
||||||
|
# Fix new fuzzy entries
|
||||||
|
log_step "Fixing new fuzzy entries..."
|
||||||
|
if ! docker compose exec app uv run manage.py fix_fuzzy; then
|
||||||
|
log_error "Failed to fix new fuzzy entries"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
log_success "New fuzzy entries fixed successfully!"
|
||||||
|
|
||||||
|
# Translate with DeepL
|
||||||
|
log_step "Translating with DeepL..."
|
||||||
|
if ! docker compose exec app uv run manage.py deepl_translate -l ALL -a ALL; then
|
||||||
|
log_error "Translation failed"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
log_success "Translated successfully!"
|
||||||
|
|
||||||
|
echo
|
||||||
|
log_result "You can now use compile-messages.sh script or run: ./lessy.py compile-messages"
|
||||||
|
|
@ -1,26 +0,0 @@
|
||||||
#!/usr/bin/env bash
|
|
||||||
set -euo pipefail
|
|
||||||
|
|
||||||
source ./scripts/Unix/starter.sh
|
|
||||||
|
|
||||||
echo "Shutting down..."
|
|
||||||
docker compose down
|
|
||||||
echo "Services were shut down successfully!"
|
|
||||||
|
|
||||||
echo "Spinning services up..."
|
|
||||||
docker compose up -d --build --wait
|
|
||||||
echo "Services are up and healthy!"
|
|
||||||
|
|
||||||
echo "Completing pre-run tasks..."
|
|
||||||
docker compose exec app uv run manage.py migrate --no-input --verbosity 0
|
|
||||||
docker compose exec app uv run manage.py initialize
|
|
||||||
docker compose exec app uv run manage.py set_default_caches
|
|
||||||
docker compose exec app uv run manage.py search_index --rebuild -f
|
|
||||||
docker compose exec app uv run manage.py collectstatic --clear --no-input --verbosity 0
|
|
||||||
echo "Pre-run tasks completed successfully!"
|
|
||||||
|
|
||||||
echo "Cleaning up unused Docker data..."
|
|
||||||
docker system prune -f
|
|
||||||
echo "Unused Docker data cleaned successfully!"
|
|
||||||
|
|
||||||
echo "All done! eVibes is up and running!"
|
|
||||||
65
scripts/Unix/restart.sh
Executable file
65
scripts/Unix/restart.sh
Executable file
|
|
@ -0,0 +1,65 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
source ./scripts/Unix/starter.sh
|
||||||
|
|
||||||
|
# Shutdown services
|
||||||
|
log_step "Shutting down..."
|
||||||
|
if ! docker compose down; then
|
||||||
|
log_error "Failed to shut down services"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
log_success "Services were shut down successfully!"
|
||||||
|
|
||||||
|
# Rebuild and start services
|
||||||
|
log_step "Spinning services up with rebuild..."
|
||||||
|
if ! docker compose up -d --build --wait; then
|
||||||
|
log_error "Failed to start services"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
log_success "Services are up and healthy!"
|
||||||
|
|
||||||
|
# Run pre-run tasks
|
||||||
|
log_step "Completing pre-run tasks..."
|
||||||
|
|
||||||
|
log_info " → Running migrations..."
|
||||||
|
if ! docker compose exec app uv run manage.py migrate --no-input --verbosity 0; then
|
||||||
|
log_error "Migrations failed"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
log_info " → Initializing..."
|
||||||
|
if ! docker compose exec app uv run manage.py initialize; then
|
||||||
|
log_error "Initialization failed"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
log_info " → Setting default caches..."
|
||||||
|
if ! docker compose exec app uv run manage.py set_default_caches; then
|
||||||
|
log_error "Cache setup failed"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
log_info " → Rebuilding search index..."
|
||||||
|
if ! docker compose exec app uv run manage.py search_index --rebuild -f; then
|
||||||
|
log_error "Search index rebuild failed"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
log_info " → Collecting static files..."
|
||||||
|
if ! docker compose exec app uv run manage.py collectstatic --clear --no-input --verbosity 0; then
|
||||||
|
log_error "Static files collection failed"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
log_success "Pre-run tasks completed successfully!"
|
||||||
|
|
||||||
|
# Cleanup
|
||||||
|
log_step "Cleaning up unused Docker data..."
|
||||||
|
if ! docker system prune -f; then
|
||||||
|
log_warning "Docker cleanup had issues, but continuing..."
|
||||||
|
fi
|
||||||
|
log_success "Unused Docker data cleaned successfully!"
|
||||||
|
|
||||||
|
echo
|
||||||
|
log_result "All done! eVibes is up and running!"
|
||||||
|
|
@ -3,36 +3,72 @@ set -euo pipefail
|
||||||
|
|
||||||
source ./scripts/Unix/starter.sh
|
source ./scripts/Unix/starter.sh
|
||||||
|
|
||||||
echo "Verifying all images are present..."
|
# Verify Docker images
|
||||||
|
log_step "Verifying all images are present..."
|
||||||
if command -v jq >/dev/null 2>&1; then
|
if command -v jq >/dev/null 2>&1; then
|
||||||
images=$(docker compose config --format json | jq -r '.services[].image // empty')
|
images=$(docker compose config --format json | jq -r '.services[].image // empty')
|
||||||
if [ -n "$images" ]; then
|
if [ -n "$images" ]; then
|
||||||
for image in $images; do
|
for image in $images; do
|
||||||
if ! docker image inspect "$image" > /dev/null 2>&1; then
|
if ! docker image inspect "$image" > /dev/null 2>&1; then
|
||||||
echo "Required images not found. Please run install.sh first." >&2
|
log_error "Required images not found. Please run install.sh first."
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
echo " - Found image: $image"
|
log_info " • Found image: $image"
|
||||||
done
|
done
|
||||||
fi
|
fi
|
||||||
else
|
else
|
||||||
echo "jq is not installed; skipping image verification step."
|
log_warning "jq is not installed; skipping image verification step."
|
||||||
fi
|
fi
|
||||||
|
|
||||||
echo "Spinning services up..."
|
# Start services
|
||||||
docker compose up --no-build --detach --wait
|
log_step "Spinning services up..."
|
||||||
echo "Services are up and healthy!"
|
if ! docker compose up --no-build --detach --wait; then
|
||||||
|
log_error "Failed to start services"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
log_success "Services are up and healthy!"
|
||||||
|
|
||||||
echo "Completing pre-run tasks..."
|
# Run pre-run tasks
|
||||||
docker compose exec app uv run manage.py migrate --no-input --verbosity 0
|
log_step "Completing pre-run tasks..."
|
||||||
docker compose exec app uv run manage.py initialize
|
|
||||||
docker compose exec app uv run manage.py set_default_caches
|
|
||||||
docker compose exec app uv run manage.py search_index --rebuild -f
|
|
||||||
docker compose exec app uv run manage.py collectstatic --clear --no-input --verbosity 0
|
|
||||||
echo "Pre-run tasks completed successfully!"
|
|
||||||
|
|
||||||
echo "Cleaning unused Docker data..."
|
log_info " → Running migrations..."
|
||||||
docker system prune -f
|
if ! docker compose exec app uv run manage.py migrate --no-input --verbosity 0; then
|
||||||
echo "Unused Docker data cleaned successfully!"
|
log_error "Migrations failed"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
echo "All done! eVibes is up and running!"
|
log_info " → Initializing..."
|
||||||
|
if ! docker compose exec app uv run manage.py initialize; then
|
||||||
|
log_error "Initialization failed"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
log_info " → Setting default caches..."
|
||||||
|
if ! docker compose exec app uv run manage.py set_default_caches; then
|
||||||
|
log_error "Cache setup failed"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
log_info " → Rebuilding search index..."
|
||||||
|
if ! docker compose exec app uv run manage.py search_index --rebuild -f; then
|
||||||
|
log_error "Search index rebuild failed"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
log_info " → Collecting static files..."
|
||||||
|
if ! docker compose exec app uv run manage.py collectstatic --clear --no-input --verbosity 0; then
|
||||||
|
log_error "Static files collection failed"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
log_success "Pre-run tasks completed successfully!"
|
||||||
|
|
||||||
|
# Cleanup
|
||||||
|
log_step "Cleaning unused Docker data..."
|
||||||
|
if ! docker system prune -f; then
|
||||||
|
log_warning "Docker cleanup had issues, but continuing..."
|
||||||
|
fi
|
||||||
|
log_success "Unused Docker data cleaned successfully!"
|
||||||
|
|
||||||
|
echo
|
||||||
|
log_result "All done! eVibes is up and running!"
|
||||||
|
|
|
||||||
|
|
@ -7,18 +7,30 @@ else script_path="$0"
|
||||||
fi
|
fi
|
||||||
script_dir="$(cd "$(dirname "$script_path")" && pwd -P)"
|
script_dir="$(cd "$(dirname "$script_path")" && pwd -P)"
|
||||||
|
|
||||||
|
# Load shared utilities
|
||||||
|
# shellcheck source=../lib/utils.sh
|
||||||
|
source "$script_dir/../lib/utils.sh"
|
||||||
|
|
||||||
if [ ! -d "./evibes" ]; then
|
if [ ! -d "./evibes" ]; then
|
||||||
echo "❌ Please run this script from the project's root (where the 'evibes' directory lives)." >&2
|
log_error "❌ Please run this script from the project's root (where the 'evibes' directory lives)."
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
art_path="$script_dir/../ASCII_ART_EVIBES"
|
art_path="$script_dir/../ASCII_ART_EVIBES"
|
||||||
if [ ! -f "$art_path" ]; then
|
if [ ! -f "$art_path" ]; then
|
||||||
echo "❌ Could not find ASCII art at $art_path" >&2
|
log_error "❌ Could not find ASCII art at $art_path"
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
cat "$art_path"
|
if is_interactive; then
|
||||||
echo
|
# In interactive mode, show colorful banner
|
||||||
echo " by WISELESS TEAM"
|
purple='\033[38;2;121;101;209m'
|
||||||
echo
|
reset='\033[0m'
|
||||||
|
echo -e "${purple}$(cat "$art_path")${reset}"
|
||||||
|
echo
|
||||||
|
echo -e "${COLOR_GRAY} by WISELESS TEAM${COLOR_RESET}"
|
||||||
|
echo
|
||||||
|
else
|
||||||
|
# In non-interactive mode, show simple banner
|
||||||
|
echo "eVibes by WISELESS TEAM"
|
||||||
|
fi
|
||||||
|
|
|
||||||
|
|
@ -4,12 +4,13 @@ set -euo pipefail
|
||||||
source ./scripts/Unix/starter.sh
|
source ./scripts/Unix/starter.sh
|
||||||
|
|
||||||
report=""
|
report=""
|
||||||
|
omit_pattern='storefront/*,monitoring/*,Dockerfiles/*,*/__init__.py,*/tests/*,*/migrations/*,manage.py,evibes/*'
|
||||||
|
|
||||||
while [ "$#" -gt 0 ]; do
|
while [ "$#" -gt 0 ]; do
|
||||||
case "$1" in
|
case "$1" in
|
||||||
--report|-r)
|
--report|-r)
|
||||||
if [ "${2-}" = "" ]; then
|
if [ "${2-}" = "" ]; then
|
||||||
echo "Error: --report/-r requires an argument: xml or html" >&2
|
log_error "Error: --report/-r requires an argument: xml or html"
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
report="$2"
|
report="$2"
|
||||||
|
|
@ -24,7 +25,7 @@ while [ "$#" -gt 0 ]; do
|
||||||
shift
|
shift
|
||||||
;;
|
;;
|
||||||
*)
|
*)
|
||||||
echo "Unknown argument: $1" >&2
|
log_error "Unknown argument: $1"
|
||||||
echo "Usage: $0 [--report|-r xml|html]" >&2
|
echo "Usage: $0 [--report|-r xml|html]" >&2
|
||||||
exit 1
|
exit 1
|
||||||
;;
|
;;
|
||||||
|
|
@ -33,18 +34,43 @@ done
|
||||||
|
|
||||||
case "${report:-}" in
|
case "${report:-}" in
|
||||||
"")
|
"")
|
||||||
docker compose exec app uv run coverage erase
|
log_step "Running tests with coverage..."
|
||||||
docker compose exec app uv run coverage run --source='.' --omit='storefront/*,monitoring/*,Dockerfiles/*,*/__init__.py,*/tests/*,*/migrations/*,manage.py,evibes/*' manage.py test
|
|
||||||
|
log_info " → Erasing previous coverage data..."
|
||||||
|
if ! docker compose exec app uv run coverage erase; then
|
||||||
|
log_error "Failed to erase coverage data"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
log_info " → Running tests..."
|
||||||
|
if ! docker compose exec app uv run coverage run --source='.' --omit="$omit_pattern" manage.py test; then
|
||||||
|
log_error "Tests failed"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
log_info " → Generating coverage report..."
|
||||||
docker compose exec app uv run coverage report -m
|
docker compose exec app uv run coverage report -m
|
||||||
;;
|
;;
|
||||||
xml)
|
xml)
|
||||||
docker compose exec app uv run coverage xml --omit='storefront/*,monitoring/*,Dockerfiles/*,*/__init__.py,*/tests/*,*/migrations/*,manage.py,evibes/*'
|
log_step "Generating XML coverage report..."
|
||||||
|
if docker compose exec app uv run coverage xml --omit="$omit_pattern"; then
|
||||||
|
log_success "XML coverage report generated"
|
||||||
|
else
|
||||||
|
log_error "Failed to generate XML coverage report"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
;;
|
;;
|
||||||
html)
|
html)
|
||||||
docker compose exec app uv run coverage html --omit='storefront/*,monitoring/*,Dockerfiles/*,*/__init__.py,*/tests/*,*/migrations/*,manage.py,evibes/*'
|
log_step "Generating HTML coverage report..."
|
||||||
|
if docker compose exec app uv run coverage html --omit="$omit_pattern"; then
|
||||||
|
log_success "HTML coverage report generated"
|
||||||
|
else
|
||||||
|
log_error "Failed to generate HTML coverage report"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
;;
|
;;
|
||||||
*)
|
*)
|
||||||
echo "Invalid report type: $report (expected xml or html)" >&2
|
log_error "Invalid report type: $report (expected xml or html)"
|
||||||
exit 1
|
exit 1
|
||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
|
|
|
||||||
|
|
@ -3,21 +3,31 @@ set -euo pipefail
|
||||||
|
|
||||||
source ./scripts/Unix/starter.sh
|
source ./scripts/Unix/starter.sh
|
||||||
|
|
||||||
echo "Shutting down..."
|
# Shutdown services
|
||||||
docker compose down
|
log_step "Shutting down..."
|
||||||
echo "Services were shut down successfully!"
|
if ! docker compose down; then
|
||||||
|
log_error "Failed to shut down services"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
log_success "Services were shut down successfully!"
|
||||||
|
|
||||||
echo "Removing volumes..."
|
# Remove volumes
|
||||||
docker volume rm -f evibes_prometheus-data
|
log_step "Removing volumes..."
|
||||||
docker volume rm -f evibes_es-data
|
docker volume rm -f evibes_prometheus-data || log_warning "Failed to remove prometheus-data volume"
|
||||||
echo "Volumes were removed successfully!"
|
docker volume rm -f evibes_es-data || log_warning "Failed to remove es-data volume"
|
||||||
|
log_success "Volumes were removed successfully!"
|
||||||
|
|
||||||
echo "Cleaning up unused Docker data..."
|
# Cleanup Docker
|
||||||
docker system prune -a -f --volumes
|
log_step "Cleaning up unused Docker data..."
|
||||||
echo "Unused Docker data cleaned successfully!"
|
if ! docker system prune -a -f --volumes; then
|
||||||
|
log_warning "Docker cleanup had issues, but continuing..."
|
||||||
|
fi
|
||||||
|
log_success "Unused Docker data cleaned successfully!"
|
||||||
|
|
||||||
echo "Removing related files..."
|
# Remove local files
|
||||||
|
log_step "Removing related files..."
|
||||||
rm -rf ./media ./static
|
rm -rf ./media ./static
|
||||||
echo "Related files removed successfully!"
|
log_success "Related files removed successfully!"
|
||||||
|
|
||||||
echo "Bye-bye, hope you return later!"
|
echo
|
||||||
|
log_result "Bye-bye, hope you return later!"
|
||||||
|
|
|
||||||
|
|
@ -2,21 +2,32 @@
|
||||||
Set-StrictMode -Version Latest
|
Set-StrictMode -Version Latest
|
||||||
$ErrorActionPreference = 'Stop'
|
$ErrorActionPreference = 'Stop'
|
||||||
|
|
||||||
|
# Load shared utilities
|
||||||
|
$utilsPath = Join-Path $PSScriptRoot '..\lib\utils.ps1'
|
||||||
|
. $utilsPath
|
||||||
|
|
||||||
.\scripts\Windows\starter.ps1
|
.\scripts\Windows\starter.ps1
|
||||||
if ($LASTEXITCODE -ne 0) {
|
if ($LASTEXITCODE -ne 0) {
|
||||||
exit $LASTEXITCODE
|
exit $LASTEXITCODE
|
||||||
}
|
}
|
||||||
|
|
||||||
Write-Host "Starting database backup process..." -ForegroundColor Magenta
|
# Database backup
|
||||||
|
Write-Step "Starting database backup process..."
|
||||||
docker compose exec app uv run manage.py dbbackup
|
docker compose exec app uv run manage.py dbbackup
|
||||||
if ($LASTEXITCODE -ne 0) {
|
if ($LASTEXITCODE -ne 0) {
|
||||||
|
Write-Error-Custom "Database backup failed"
|
||||||
exit $LASTEXITCODE
|
exit $LASTEXITCODE
|
||||||
}
|
}
|
||||||
Write-Host "Database backup created under ./dbbackup" -ForegroundColor Green
|
Write-Success "Database backup created under ./dbbackup"
|
||||||
|
|
||||||
Write-Host "Starting media backup process..." -ForegroundColor Magenta
|
# Media backup
|
||||||
|
Write-Step "Starting media backup process..."
|
||||||
docker compose exec app uv run manage.py mediabackup
|
docker compose exec app uv run manage.py mediabackup
|
||||||
if ($LASTEXITCODE -ne 0) {
|
if ($LASTEXITCODE -ne 0) {
|
||||||
|
Write-Error-Custom "Media backup failed"
|
||||||
exit $LASTEXITCODE
|
exit $LASTEXITCODE
|
||||||
}
|
}
|
||||||
Write-Host "Media backup created under ./dbbackup" -ForegroundColor Green
|
Write-Success "Media backup created under ./dbbackup"
|
||||||
|
|
||||||
|
Write-Result ""
|
||||||
|
Write-Result "Backup completed successfully!"
|
||||||
|
|
@ -1,6 +1,10 @@
|
||||||
Set-StrictMode -Version Latest
|
Set-StrictMode -Version Latest
|
||||||
$ErrorActionPreference = 'Stop'
|
$ErrorActionPreference = 'Stop'
|
||||||
|
|
||||||
|
# Load shared utilities
|
||||||
|
$utilsPath = Join-Path $PSScriptRoot '..\lib\utils.ps1'
|
||||||
|
. $utilsPath
|
||||||
|
|
||||||
.\scripts\Windows\starter.ps1
|
.\scripts\Windows\starter.ps1
|
||||||
if ($LASTEXITCODE -ne 0) {
|
if ($LASTEXITCODE -ne 0) {
|
||||||
exit $LASTEXITCODE
|
exit $LASTEXITCODE
|
||||||
|
|
@ -8,20 +12,27 @@ if ($LASTEXITCODE -ne 0) {
|
||||||
|
|
||||||
if (-not (Test-Path '.env'))
|
if (-not (Test-Path '.env'))
|
||||||
{
|
{
|
||||||
Write-Warning ".env file not found. Exiting without running Docker steps."
|
Write-Warning-Custom ".env file not found. Exiting without running Docker steps."
|
||||||
exit 0
|
exit 0
|
||||||
}
|
}
|
||||||
|
|
||||||
Write-Host "Checking placeholders in PO files..." -ForegroundColor Magenta
|
# Check placeholders
|
||||||
|
Write-Step "Checking placeholders in PO files..."
|
||||||
docker compose exec app uv run manage.py check_translated -l ALL -a ALL
|
docker compose exec app uv run manage.py check_translated -l ALL -a ALL
|
||||||
if ($LASTEXITCODE -ne 0) {
|
if ($LASTEXITCODE -ne 0) {
|
||||||
|
Write-Error-Custom "PO files have placeholder issues"
|
||||||
exit $LASTEXITCODE
|
exit $LASTEXITCODE
|
||||||
}
|
}
|
||||||
Write-Host "PO files have no placeholder issues!" -ForegroundColor Green
|
Write-Success "PO files have no placeholder issues!"
|
||||||
|
|
||||||
Write-Host "Compiling PO files into MO files..." -ForegroundColor Magenta
|
# Compile messages
|
||||||
|
Write-Step "Compiling PO files into MO files..."
|
||||||
docker compose exec app uv run manage.py compilemessages -l ar_AR -l cs_CZ -l da_DK -l de_DE -l en_GB -l en_US -l es_ES -l fa_IR -l fr_FR -l he_IL -l hi_IN -l hr_HR -l id_ID -l it_IT -l ja_JP -l kk_KZ -l ko_KR -l nl_NL -l no_NO -l pl_PL -l pt_BR -l ro_RO -l ru_RU -l sv_SE -l th_TH -l tr_TR -l vi_VN -l zh_Hans
|
docker compose exec app uv run manage.py compilemessages -l ar_AR -l cs_CZ -l da_DK -l de_DE -l en_GB -l en_US -l es_ES -l fa_IR -l fr_FR -l he_IL -l hi_IN -l hr_HR -l id_ID -l it_IT -l ja_JP -l kk_KZ -l ko_KR -l nl_NL -l no_NO -l pl_PL -l pt_BR -l ro_RO -l ru_RU -l sv_SE -l th_TH -l tr_TR -l vi_VN -l zh_Hans
|
||||||
if ($LASTEXITCODE -ne 0) {
|
if ($LASTEXITCODE -ne 0) {
|
||||||
|
Write-Error-Custom "Failed to compile messages"
|
||||||
exit $LASTEXITCODE
|
exit $LASTEXITCODE
|
||||||
}
|
}
|
||||||
Write-Host "Compiled successfully!" -ForegroundColor Green
|
Write-Success "Compiled successfully!"
|
||||||
|
|
||||||
|
Write-Result ""
|
||||||
|
Write-Result "Translation compilation complete!"
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,10 @@
|
||||||
Set-StrictMode -Version Latest
|
Set-StrictMode -Version Latest
|
||||||
$ErrorActionPreference = 'Stop'
|
$ErrorActionPreference = 'Stop'
|
||||||
|
|
||||||
|
# Load shared utilities
|
||||||
|
$utilsPath = Join-Path $PSScriptRoot '..\lib\utils.ps1'
|
||||||
|
. $utilsPath
|
||||||
|
|
||||||
.\scripts\Windows\starter.ps1
|
.\scripts\Windows\starter.ps1
|
||||||
if ($LASTEXITCODE -ne 0) {
|
if ($LASTEXITCODE -ne 0) {
|
||||||
exit $LASTEXITCODE
|
exit $LASTEXITCODE
|
||||||
|
|
@ -8,39 +12,33 @@ if ($LASTEXITCODE -ne 0) {
|
||||||
|
|
||||||
if (-not (Test-Path '.env'))
|
if (-not (Test-Path '.env'))
|
||||||
{
|
{
|
||||||
Write-Warning ".env file not found. Exiting without running Docker steps."
|
Write-Warning-Custom ".env file not found. Exiting without running Docker steps."
|
||||||
exit 0
|
exit 0
|
||||||
}
|
}
|
||||||
|
|
||||||
$cpuCount = [Environment]::ProcessorCount
|
# Check system requirements
|
||||||
if ($cpuCount -lt 4)
|
if (-not (Test-SystemRequirements -MinCpu 4 -MinRamGB 6 -MinDiskGB 20))
|
||||||
{
|
{
|
||||||
exit 1
|
exit 1
|
||||||
}
|
}
|
||||||
|
|
||||||
$totalMemBytes = (Get-CimInstance Win32_ComputerSystem).TotalPhysicalMemory
|
# Pull Docker images
|
||||||
$totalMemGB = [Math]::Round($totalMemBytes / 1GB, 2)
|
Write-Step "Pulling images..."
|
||||||
if ($totalMemGB -lt 6)
|
|
||||||
{
|
|
||||||
exit 1
|
|
||||||
}
|
|
||||||
|
|
||||||
$currentDrive = Split-Path -Qualifier $PWD
|
|
||||||
$driveLetter = $currentDrive.Substring(0, 1)
|
|
||||||
$freeGB = [Math]::Round((Get-PSDrive -Name $driveLetter).Free / 1GB, 2)
|
|
||||||
if ($freeGB -lt 20)
|
|
||||||
{
|
|
||||||
exit 1
|
|
||||||
}
|
|
||||||
|
|
||||||
Write-Host "System requirements met: CPU cores=$cpuCount, RAM=${totalMemGB}GB, FreeDisk=${freeGB}GB" -ForegroundColor Green
|
|
||||||
|
|
||||||
Write-Host "Pulling images" -ForegroundColor Magenta
|
|
||||||
docker compose pull
|
docker compose pull
|
||||||
Write-Host "Images pulled successfully" -ForegroundColor Green
|
if ($LASTEXITCODE -ne 0) {
|
||||||
|
Write-Error-Custom "Failed to pull Docker images"
|
||||||
|
exit $LASTEXITCODE
|
||||||
|
}
|
||||||
|
Write-Success "Images pulled successfully"
|
||||||
|
|
||||||
Write-Host "Building images" -ForegroundColor Magenta
|
# Build Docker images
|
||||||
|
Write-Step "Building images..."
|
||||||
docker compose build
|
docker compose build
|
||||||
Write-Host "Images built successfully" -ForegroundColor Green
|
if ($LASTEXITCODE -ne 0) {
|
||||||
Write-Host ""
|
Write-Error-Custom "Failed to build Docker images"
|
||||||
Write-Host "You can now use run.ps1 script." -ForegroundColor Cyan
|
exit $LASTEXITCODE
|
||||||
|
}
|
||||||
|
Write-Success "Images built successfully"
|
||||||
|
|
||||||
|
Write-Result ""
|
||||||
|
Write-Result "You can now use run.ps1 script or run: lessy.py run"
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,10 @@
|
||||||
Set-StrictMode -Version Latest
|
Set-StrictMode -Version Latest
|
||||||
$ErrorActionPreference = 'Stop'
|
$ErrorActionPreference = 'Stop'
|
||||||
|
|
||||||
|
# Load shared utilities
|
||||||
|
$utilsPath = Join-Path $PSScriptRoot '..\lib\utils.ps1'
|
||||||
|
. $utilsPath
|
||||||
|
|
||||||
.\scripts\Windows\starter.ps1
|
.\scripts\Windows\starter.ps1
|
||||||
if ($LASTEXITCODE -ne 0) {
|
if ($LASTEXITCODE -ne 0) {
|
||||||
exit $LASTEXITCODE
|
exit $LASTEXITCODE
|
||||||
|
|
@ -8,37 +12,45 @@ if ($LASTEXITCODE -ne 0) {
|
||||||
|
|
||||||
if (-not (Test-Path '.env'))
|
if (-not (Test-Path '.env'))
|
||||||
{
|
{
|
||||||
Write-Warning ".env file not found. Exiting without running Docker steps."
|
Write-Warning-Custom ".env file not found. Exiting without running Docker steps."
|
||||||
exit 0
|
exit 0
|
||||||
}
|
}
|
||||||
|
|
||||||
Write-Host "Remove old fuzzy entries..." -ForegroundColor Magenta
|
# Remove old fuzzy entries
|
||||||
|
Write-Step "Remove old fuzzy entries..."
|
||||||
docker compose exec app uv run manage.py fix_fuzzy
|
docker compose exec app uv run manage.py fix_fuzzy
|
||||||
if ($LASTEXITCODE -ne 0) {
|
if ($LASTEXITCODE -ne 0) {
|
||||||
|
Write-Error-Custom "Failed to remove old fuzzy entries"
|
||||||
exit $LASTEXITCODE
|
exit $LASTEXITCODE
|
||||||
}
|
}
|
||||||
Write-Host "Old fuzzy entries removed successfully!" -ForegroundColor Green
|
Write-Success "Old fuzzy entries removed successfully!"
|
||||||
|
|
||||||
Write-Host "Updating PO files..." -ForegroundColor Magenta
|
# Update PO files
|
||||||
|
Write-Step "Updating PO files..."
|
||||||
docker compose exec app uv run manage.py makemessages -l ar_AR -l cs_CZ -l da_DK -l de_DE -l en_GB -l en_US -l es_ES -l fa_IR -l fr_FR -l he_IL -l hi_IN -l hr_HR -l id_ID -l it_IT -l ja_JP -l kk_KZ -l ko_KR -l nl_NL -l no_NO -l pl_PL -l pt_BR -l ro_RO -l ru_RU -l sv_SE -l th_TH -l tr_TR -l vi_VN -l zh_Hans
|
docker compose exec app uv run manage.py makemessages -l ar_AR -l cs_CZ -l da_DK -l de_DE -l en_GB -l en_US -l es_ES -l fa_IR -l fr_FR -l he_IL -l hi_IN -l hr_HR -l id_ID -l it_IT -l ja_JP -l kk_KZ -l ko_KR -l nl_NL -l no_NO -l pl_PL -l pt_BR -l ro_RO -l ru_RU -l sv_SE -l th_TH -l tr_TR -l vi_VN -l zh_Hans
|
||||||
if ($LASTEXITCODE -ne 0) {
|
if ($LASTEXITCODE -ne 0) {
|
||||||
|
Write-Error-Custom "Failed to update PO files"
|
||||||
exit $LASTEXITCODE
|
exit $LASTEXITCODE
|
||||||
}
|
}
|
||||||
Write-Host "PO files updated successfully!" -ForegroundColor Green
|
Write-Success "PO files updated successfully!"
|
||||||
|
|
||||||
Write-Host "Fixing new fuzzy entries..." -ForegroundColor Magenta
|
# Fix new fuzzy entries
|
||||||
|
Write-Step "Fixing new fuzzy entries..."
|
||||||
docker compose exec app uv run manage.py fix_fuzzy
|
docker compose exec app uv run manage.py fix_fuzzy
|
||||||
if ($LASTEXITCODE -ne 0) {
|
if ($LASTEXITCODE -ne 0) {
|
||||||
|
Write-Error-Custom "Failed to fix new fuzzy entries"
|
||||||
exit $LASTEXITCODE
|
exit $LASTEXITCODE
|
||||||
}
|
}
|
||||||
Write-Host "New fuzzy entries fixed successfully!" -ForegroundColor Green
|
Write-Success "New fuzzy entries fixed successfully!"
|
||||||
|
|
||||||
Write-Host "Translating with DeepL..." -ForegroundColor Magenta
|
# Translate with DeepL
|
||||||
|
Write-Step "Translating with DeepL..."
|
||||||
docker compose exec app uv run manage.py deepl_translate -l ALL -a ALL
|
docker compose exec app uv run manage.py deepl_translate -l ALL -a ALL
|
||||||
if ($LASTEXITCODE -ne 0) {
|
if ($LASTEXITCODE -ne 0) {
|
||||||
|
Write-Error-Custom "Translation failed"
|
||||||
exit $LASTEXITCODE
|
exit $LASTEXITCODE
|
||||||
}
|
}
|
||||||
Write-Host "Translated successfully!" -ForegroundColor Green
|
Write-Success "Translated successfully!"
|
||||||
|
|
||||||
Write-Host ""
|
Write-Result ""
|
||||||
Write-Host "You can now use compile-messages.ps1 script." -ForegroundColor Cyan
|
Write-Result "You can now use compile-messages.ps1 script or run: lessy.py compile-messages"
|
||||||
|
|
|
||||||
|
|
@ -1,54 +0,0 @@
|
||||||
Set-StrictMode -Version Latest
|
|
||||||
$ErrorActionPreference = 'Stop'
|
|
||||||
|
|
||||||
.\scripts\Windows\starter.ps1
|
|
||||||
if ($LASTEXITCODE -ne 0) {
|
|
||||||
exit $LASTEXITCODE
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
Write-Host "Shutting down..." -ForegroundColor Magenta
|
|
||||||
docker compose down
|
|
||||||
if ($LASTEXITCODE -ne 0) {
|
|
||||||
exit $LASTEXITCODE
|
|
||||||
}
|
|
||||||
Write-Host "Services were shut down successfully!" -ForegroundColor Green
|
|
||||||
|
|
||||||
Write-Host "Spinning services up..." -ForegroundColor Magenta
|
|
||||||
docker compose up -d --build --wait
|
|
||||||
if ($LASTEXITCODE -ne 0) {
|
|
||||||
exit $LASTEXITCODE
|
|
||||||
}
|
|
||||||
Write-Host "Services are up and healthy!" -ForegroundColor Green
|
|
||||||
|
|
||||||
Write-Host "Completing pre-run tasks..." -ForegroundColor Magenta
|
|
||||||
docker compose exec app uv run manage.py migrate --no-input
|
|
||||||
if ($LASTEXITCODE -ne 0) {
|
|
||||||
exit $LASTEXITCODE
|
|
||||||
}
|
|
||||||
docker compose exec app uv run manage.py initialize
|
|
||||||
if ($LASTEXITCODE -ne 0) {
|
|
||||||
exit $LASTEXITCODE
|
|
||||||
}
|
|
||||||
docker compose exec app uv run manage.py set_default_caches
|
|
||||||
if ($LASTEXITCODE -ne 0) {
|
|
||||||
exit $LASTEXITCODE
|
|
||||||
}
|
|
||||||
docker compose exec app uv run manage.py search_index --rebuild -f
|
|
||||||
if ($LASTEXITCODE -ne 0) {
|
|
||||||
exit $LASTEXITCODE
|
|
||||||
}
|
|
||||||
docker compose exec app uv run manage.py collectstatic --clear --no-input
|
|
||||||
if ($LASTEXITCODE -ne 0) {
|
|
||||||
exit $LASTEXITCODE
|
|
||||||
}
|
|
||||||
Write-Host "Pre-run tasks completed successfully!" -ForegroundColor Green
|
|
||||||
|
|
||||||
Write-Host "Cleaning up unused Docker data..." -ForegroundColor Magenta
|
|
||||||
docker system prune -f
|
|
||||||
if ($LASTEXITCODE -ne 0) {
|
|
||||||
exit $LASTEXITCODE
|
|
||||||
}
|
|
||||||
Write-Host "Unused Docker data cleaned successfully!" -ForegroundColor Green
|
|
||||||
|
|
||||||
Write-Host "All done! eVibes is up and running!" -ForegroundColor Cyan
|
|
||||||
80
scripts/Windows/restart.ps1
Normal file
80
scripts/Windows/restart.ps1
Normal file
|
|
@ -0,0 +1,80 @@
|
||||||
|
Set-StrictMode -Version Latest
|
||||||
|
$ErrorActionPreference = 'Stop'
|
||||||
|
|
||||||
|
# Load shared utilities
|
||||||
|
$utilsPath = Join-Path $PSScriptRoot '..\lib\utils.ps1'
|
||||||
|
. $utilsPath
|
||||||
|
|
||||||
|
.\scripts\Windows\starter.ps1
|
||||||
|
if ($LASTEXITCODE -ne 0) {
|
||||||
|
exit $LASTEXITCODE
|
||||||
|
}
|
||||||
|
|
||||||
|
# Shutdown services
|
||||||
|
Write-Step "Shutting down..."
|
||||||
|
docker compose down
|
||||||
|
if ($LASTEXITCODE -ne 0) {
|
||||||
|
Write-Error-Custom "Failed to shut down services"
|
||||||
|
exit $LASTEXITCODE
|
||||||
|
}
|
||||||
|
Write-Success "Services were shut down successfully!"
|
||||||
|
|
||||||
|
# Rebuild and start services
|
||||||
|
Write-Step "Spinning services up with rebuild..."
|
||||||
|
docker compose up -d --build --wait
|
||||||
|
if ($LASTEXITCODE -ne 0) {
|
||||||
|
Write-Error-Custom "Failed to start services"
|
||||||
|
exit $LASTEXITCODE
|
||||||
|
}
|
||||||
|
Write-Success "Services are up and healthy!"
|
||||||
|
|
||||||
|
# Run pre-run tasks
|
||||||
|
Write-Step "Completing pre-run tasks..."
|
||||||
|
|
||||||
|
Write-Info " → Running migrations..."
|
||||||
|
docker compose exec app uv run manage.py migrate --no-input
|
||||||
|
if ($LASTEXITCODE -ne 0) {
|
||||||
|
Write-Error-Custom "Migrations failed"
|
||||||
|
exit $LASTEXITCODE
|
||||||
|
}
|
||||||
|
|
||||||
|
Write-Info " → Initializing..."
|
||||||
|
docker compose exec app uv run manage.py initialize
|
||||||
|
if ($LASTEXITCODE -ne 0) {
|
||||||
|
Write-Error-Custom "Initialization failed"
|
||||||
|
exit $LASTEXITCODE
|
||||||
|
}
|
||||||
|
|
||||||
|
Write-Info " → Setting default caches..."
|
||||||
|
docker compose exec app uv run manage.py set_default_caches
|
||||||
|
if ($LASTEXITCODE -ne 0) {
|
||||||
|
Write-Error-Custom "Cache setup failed"
|
||||||
|
exit $LASTEXITCODE
|
||||||
|
}
|
||||||
|
|
||||||
|
Write-Info " → Rebuilding search index..."
|
||||||
|
docker compose exec app uv run manage.py search_index --rebuild -f
|
||||||
|
if ($LASTEXITCODE -ne 0) {
|
||||||
|
Write-Error-Custom "Search index rebuild failed"
|
||||||
|
exit $LASTEXITCODE
|
||||||
|
}
|
||||||
|
|
||||||
|
Write-Info " → Collecting static files..."
|
||||||
|
docker compose exec app uv run manage.py collectstatic --clear --no-input
|
||||||
|
if ($LASTEXITCODE -ne 0) {
|
||||||
|
Write-Error-Custom "Static files collection failed"
|
||||||
|
exit $LASTEXITCODE
|
||||||
|
}
|
||||||
|
|
||||||
|
Write-Success "Pre-run tasks completed successfully!"
|
||||||
|
|
||||||
|
# Cleanup
|
||||||
|
Write-Step "Cleaning up unused Docker data..."
|
||||||
|
docker system prune -f
|
||||||
|
if ($LASTEXITCODE -ne 0) {
|
||||||
|
Write-Warning-Custom "Docker cleanup had issues, but continuing..."
|
||||||
|
}
|
||||||
|
Write-Success "Unused Docker data cleaned successfully!"
|
||||||
|
|
||||||
|
Write-Result ""
|
||||||
|
Write-Result "All done! eVibes is up and running!"
|
||||||
|
|
@ -1,12 +1,17 @@
|
||||||
Set-StrictMode -Version Latest
|
Set-StrictMode -Version Latest
|
||||||
$ErrorActionPreference = 'Stop'
|
$ErrorActionPreference = 'Stop'
|
||||||
|
|
||||||
|
# Load shared utilities
|
||||||
|
$utilsPath = Join-Path $PSScriptRoot '..\lib\utils.ps1'
|
||||||
|
. $utilsPath
|
||||||
|
|
||||||
.\scripts\Windows\starter.ps1
|
.\scripts\Windows\starter.ps1
|
||||||
if ($LASTEXITCODE -ne 0) {
|
if ($LASTEXITCODE -ne 0) {
|
||||||
exit $LASTEXITCODE
|
exit $LASTEXITCODE
|
||||||
}
|
}
|
||||||
|
|
||||||
Write-Host "Verifying all images are present…" -ForegroundColor Green
|
# Verify Docker images
|
||||||
|
Write-Step "Verifying all images are present…"
|
||||||
|
|
||||||
$config = docker compose config --format json | ConvertFrom-Json
|
$config = docker compose config --format json | ConvertFrom-Json
|
||||||
|
|
||||||
|
|
@ -21,50 +26,71 @@ foreach ($prop in $config.services.PSObject.Properties)
|
||||||
|
|
||||||
$image = $svc.PSObject.Properties['image'].Value
|
$image = $svc.PSObject.Properties['image'].Value
|
||||||
|
|
||||||
if (-not (docker image inspect $image))
|
if (-not (docker image inspect $image 2>$null))
|
||||||
{
|
{
|
||||||
Write-Error "Required images not found. Please run install.ps1 first."
|
Write-Error-Custom "Required images not found. Please run install.ps1 first."
|
||||||
exit 1
|
exit 1
|
||||||
}
|
}
|
||||||
|
|
||||||
Write-Host " • Found image: $image"
|
Write-Info " • Found image: $image"
|
||||||
}
|
}
|
||||||
|
|
||||||
Write-Host "Spinning services up..." -ForegroundColor Magenta
|
# Start services
|
||||||
|
Write-Step "Spinning services up..."
|
||||||
docker compose up --no-build --detach --wait
|
docker compose up --no-build --detach --wait
|
||||||
if ($LASTEXITCODE -ne 0) {
|
if ($LASTEXITCODE -ne 0) {
|
||||||
|
Write-Error-Custom "Failed to start services"
|
||||||
exit $LASTEXITCODE
|
exit $LASTEXITCODE
|
||||||
}
|
}
|
||||||
Write-Host "Services are up and healthy!" -ForegroundColor Green
|
Write-Success "Services are up and healthy!"
|
||||||
|
|
||||||
Write-Host "Completing pre-run tasks..." -ForegroundColor Magenta
|
# Run pre-run tasks
|
||||||
|
Write-Step "Completing pre-run tasks..."
|
||||||
|
|
||||||
|
Write-Info " → Running migrations..."
|
||||||
docker compose exec app uv run manage.py migrate --no-input
|
docker compose exec app uv run manage.py migrate --no-input
|
||||||
if ($LASTEXITCODE -ne 0) {
|
if ($LASTEXITCODE -ne 0) {
|
||||||
|
Write-Error-Custom "Migrations failed"
|
||||||
exit $LASTEXITCODE
|
exit $LASTEXITCODE
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Write-Info " → Initializing..."
|
||||||
docker compose exec app uv run manage.py initialize
|
docker compose exec app uv run manage.py initialize
|
||||||
if ($LASTEXITCODE -ne 0) {
|
if ($LASTEXITCODE -ne 0) {
|
||||||
|
Write-Error-Custom "Initialization failed"
|
||||||
exit $LASTEXITCODE
|
exit $LASTEXITCODE
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Write-Info " → Setting default caches..."
|
||||||
docker compose exec app uv run manage.py set_default_caches
|
docker compose exec app uv run manage.py set_default_caches
|
||||||
if ($LASTEXITCODE -ne 0) {
|
if ($LASTEXITCODE -ne 0) {
|
||||||
|
Write-Error-Custom "Cache setup failed"
|
||||||
exit $LASTEXITCODE
|
exit $LASTEXITCODE
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Write-Info " → Rebuilding search index..."
|
||||||
docker compose exec app uv run manage.py search_index --rebuild -f
|
docker compose exec app uv run manage.py search_index --rebuild -f
|
||||||
if ($LASTEXITCODE -ne 0) {
|
if ($LASTEXITCODE -ne 0) {
|
||||||
|
Write-Error-Custom "Search index rebuild failed"
|
||||||
exit $LASTEXITCODE
|
exit $LASTEXITCODE
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Write-Info " → Collecting static files..."
|
||||||
docker compose exec app uv run manage.py collectstatic --clear --no-input
|
docker compose exec app uv run manage.py collectstatic --clear --no-input
|
||||||
if ($LASTEXITCODE -ne 0) {
|
if ($LASTEXITCODE -ne 0) {
|
||||||
|
Write-Error-Custom "Static files collection failed"
|
||||||
exit $LASTEXITCODE
|
exit $LASTEXITCODE
|
||||||
}
|
}
|
||||||
Write-Host "Pre-run tasks completed successfully!" -ForegroundColor Green
|
|
||||||
|
|
||||||
Write-Host "Cleaning unused Docker data..." -ForegroundColor Magenta
|
Write-Success "Pre-run tasks completed successfully!"
|
||||||
|
|
||||||
|
# Cleanup
|
||||||
|
Write-Step "Cleaning unused Docker data..."
|
||||||
docker system prune -f
|
docker system prune -f
|
||||||
if ($LASTEXITCODE -ne 0) {
|
if ($LASTEXITCODE -ne 0) {
|
||||||
exit $LASTEXITCODE
|
Write-Warning-Custom "Docker cleanup had issues, but continuing..."
|
||||||
}
|
}
|
||||||
Write-Host "Unused Docker data cleaned successfully!" -ForegroundColor Green
|
Write-Success "Unused Docker data cleaned successfully!"
|
||||||
|
|
||||||
Write-Host "All done! eVibes is up and running!" -ForegroundColor Cyan
|
Write-Result ""
|
||||||
|
Write-Result "All done! eVibes is up and running!"
|
||||||
|
|
|
||||||
|
|
@ -1,22 +1,35 @@
|
||||||
Set-StrictMode -Version Latest
|
Set-StrictMode -Version Latest
|
||||||
$ErrorActionPreference = 'Stop'
|
$ErrorActionPreference = 'Stop'
|
||||||
|
|
||||||
|
# Load shared utilities
|
||||||
|
$utilsPath = Join-Path (Split-Path -Parent $MyInvocation.MyCommand.Definition) '..\lib\utils.ps1'
|
||||||
|
. $utilsPath
|
||||||
|
|
||||||
if (-not (Test-Path -Path ".\evibes" -PathType Container))
|
if (-not (Test-Path -Path ".\evibes" -PathType Container))
|
||||||
{
|
{
|
||||||
Write-Host "❌ Please run this script from the project's root (where the 'evibes' directory lives)." -ForegroundColor Red
|
Write-Error-Custom "❌ Please run this script from the project's root (where the 'evibes' directory lives)."
|
||||||
exit 1
|
exit 1
|
||||||
}
|
}
|
||||||
|
|
||||||
$purple = "`e[38;2;121;101;209m"
|
|
||||||
$reset = "`e[0m"
|
|
||||||
$artPath = Join-Path (Split-Path -Parent $MyInvocation.MyCommand.Definition) '..\ASCII_ART_EVIBES'
|
$artPath = Join-Path (Split-Path -Parent $MyInvocation.MyCommand.Definition) '..\ASCII_ART_EVIBES'
|
||||||
|
|
||||||
if (-not (Test-Path $artPath))
|
if (-not (Test-Path $artPath))
|
||||||
{
|
{
|
||||||
Write-Host "❌ Could not find ASCII art at $artPath" -ForegroundColor Red
|
Write-Error-Custom "❌ Could not find ASCII art at $artPath"
|
||||||
exit 1
|
exit 1
|
||||||
}
|
}
|
||||||
|
|
||||||
Get-Content -Raw -Path $artPath | ForEach-Object { Write-Host "$purple$_$reset" }
|
if (Test-Interactive)
|
||||||
Write-Host "`n by WISELESS TEAM`n" -ForegroundColor Gray
|
{
|
||||||
|
$purple = "`e[38;2;121;101;209m"
|
||||||
|
$reset = "`e[0m"
|
||||||
|
Get-Content -Raw -Path $artPath | ForEach-Object { Write-Host "$purple$_$reset" }
|
||||||
|
Write-Host "`n by WISELESS TEAM`n" -ForegroundColor Gray
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
# In non-interactive mode, just show simple banner
|
||||||
|
Write-Output "eVibes by WISELESS TEAM"
|
||||||
|
}
|
||||||
|
|
||||||
exit 0
|
exit 0
|
||||||
|
|
@ -6,33 +6,62 @@ param(
|
||||||
Set-StrictMode -Version Latest
|
Set-StrictMode -Version Latest
|
||||||
$ErrorActionPreference = 'Stop'
|
$ErrorActionPreference = 'Stop'
|
||||||
|
|
||||||
|
# Load shared utilities
|
||||||
|
$utilsPath = Join-Path $PSScriptRoot '..\lib\utils.ps1'
|
||||||
|
. $utilsPath
|
||||||
|
|
||||||
& .\scripts\Windows\starter.ps1
|
& .\scripts\Windows\starter.ps1
|
||||||
if ($LASTEXITCODE -ne 0) {
|
if ($LASTEXITCODE -ne 0) {
|
||||||
exit $LASTEXITCODE
|
exit $LASTEXITCODE
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$omitPattern = 'storefront/*,monitoring/*,Dockerfiles/*,*/__init__.py,*/tests/*,*/migrations/*,manage.py,evibes/*'
|
||||||
|
|
||||||
if (-not $PSBoundParameters.ContainsKey('Report') -or [string]::IsNullOrWhiteSpace($Report)) {
|
if (-not $PSBoundParameters.ContainsKey('Report') -or [string]::IsNullOrWhiteSpace($Report)) {
|
||||||
|
Write-Step "Running tests with coverage..."
|
||||||
|
|
||||||
|
Write-Info " → Erasing previous coverage data..."
|
||||||
docker compose exec app uv run coverage erase
|
docker compose exec app uv run coverage erase
|
||||||
if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }
|
if ($LASTEXITCODE -ne 0) {
|
||||||
|
Write-Error-Custom "Failed to erase coverage data"
|
||||||
|
exit $LASTEXITCODE
|
||||||
|
}
|
||||||
|
|
||||||
docker compose exec app uv run coverage run --source='.' --omit='storefront/*,monitoring/*,Dockerfiles/*,*/__init__.py,*/tests/*,*/migrations/*,manage.py,evibes/*' manage.py test
|
Write-Info " → Running tests..."
|
||||||
if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }
|
docker compose exec app uv run coverage run --source='.' --omit=$omitPattern manage.py test
|
||||||
|
if ($LASTEXITCODE -ne 0) {
|
||||||
|
Write-Error-Custom "Tests failed"
|
||||||
|
exit $LASTEXITCODE
|
||||||
|
}
|
||||||
|
|
||||||
|
Write-Info " → Generating coverage report..."
|
||||||
docker compose exec app uv run coverage report -m
|
docker compose exec app uv run coverage report -m
|
||||||
exit $LASTEXITCODE
|
exit $LASTEXITCODE
|
||||||
}
|
}
|
||||||
|
|
||||||
switch ($Report.ToLowerInvariant()) {
|
switch ($Report.ToLowerInvariant()) {
|
||||||
'xml' {
|
'xml' {
|
||||||
docker compose exec app uv run coverage xml --omit='storefront/*,monitoring/*,Dockerfiles/*,*/__init__.py,*/tests/*,*/migrations/*,manage.py,evibes/*'
|
Write-Step "Generating XML coverage report..."
|
||||||
|
docker compose exec app uv run coverage xml --omit=$omitPattern
|
||||||
|
if ($LASTEXITCODE -eq 0) {
|
||||||
|
Write-Success "XML coverage report generated"
|
||||||
|
} else {
|
||||||
|
Write-Error-Custom "Failed to generate XML coverage report"
|
||||||
|
}
|
||||||
exit $LASTEXITCODE
|
exit $LASTEXITCODE
|
||||||
}
|
}
|
||||||
'html' {
|
'html' {
|
||||||
docker compose exec app uv run coverage html --omit='storefront/*,monitoring/*,Dockerfiles/*,*/__init__.py,*/tests/*,*/migrations/*,manage.py,evibes/*'
|
Write-Step "Generating HTML coverage report..."
|
||||||
|
docker compose exec app uv run coverage html --omit=$omitPattern
|
||||||
|
if ($LASTEXITCODE -eq 0) {
|
||||||
|
Write-Success "HTML coverage report generated"
|
||||||
|
} else {
|
||||||
|
Write-Error-Custom "Failed to generate HTML coverage report"
|
||||||
|
}
|
||||||
exit $LASTEXITCODE
|
exit $LASTEXITCODE
|
||||||
}
|
}
|
||||||
default {
|
default {
|
||||||
Write-Error "Invalid -Report/-r value '$Report'. Expected 'xml' or 'html'."
|
Write-Error-Custom "Invalid -Report/-r value '$Report'. Expected 'xml' or 'html'."
|
||||||
exit 1
|
exit 1
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,39 +1,49 @@
|
||||||
Set-StrictMode -Version Latest
|
Set-StrictMode -Version Latest
|
||||||
$ErrorActionPreference = 'Stop'
|
$ErrorActionPreference = 'Stop'
|
||||||
|
|
||||||
|
# Load shared utilities
|
||||||
|
$utilsPath = Join-Path $PSScriptRoot '..\lib\utils.ps1'
|
||||||
|
. $utilsPath
|
||||||
|
|
||||||
.\scripts\Windows\starter.ps1
|
.\scripts\Windows\starter.ps1
|
||||||
if ($LASTEXITCODE -ne 0) {
|
if ($LASTEXITCODE -ne 0) {
|
||||||
exit $LASTEXITCODE
|
exit $LASTEXITCODE
|
||||||
}
|
}
|
||||||
|
|
||||||
Write-Host "Shutting down..." -ForegroundColor Magenta
|
# Shutdown services
|
||||||
|
Write-Step "Shutting down..."
|
||||||
docker compose down
|
docker compose down
|
||||||
if ($LASTEXITCODE -ne 0) {
|
if ($LASTEXITCODE -ne 0) {
|
||||||
|
Write-Error-Custom "Failed to shut down services"
|
||||||
exit $LASTEXITCODE
|
exit $LASTEXITCODE
|
||||||
}
|
}
|
||||||
Write-Host "Services were shut down successfully!" -ForegroundColor Green
|
Write-Success "Services were shut down successfully!"
|
||||||
|
|
||||||
Write-Host "Removing volumes..." -ForegroundColor Magenta
|
# Remove volumes
|
||||||
|
Write-Step "Removing volumes..."
|
||||||
docker volume remove -f evibes_prometheus-data
|
docker volume remove -f evibes_prometheus-data
|
||||||
if ($LASTEXITCODE -ne 0) {
|
if ($LASTEXITCODE -ne 0) {
|
||||||
exit $LASTEXITCODE
|
Write-Warning-Custom "Failed to remove prometheus-data volume"
|
||||||
}
|
}
|
||||||
docker volume remove -f evibes_es-data
|
docker volume remove -f evibes_es-data
|
||||||
if ($LASTEXITCODE -ne 0) {
|
if ($LASTEXITCODE -ne 0) {
|
||||||
exit $LASTEXITCODE
|
Write-Warning-Custom "Failed to remove es-data volume"
|
||||||
}
|
}
|
||||||
Write-Host "Volumes were removed successfully!" -ForegroundColor Green
|
Write-Success "Volumes were removed successfully!"
|
||||||
|
|
||||||
Write-Host "Cleaning up unused Docker data..." -ForegroundColor Magenta
|
# Cleanup Docker
|
||||||
|
Write-Step "Cleaning up unused Docker data..."
|
||||||
docker system prune -a -f --volumes
|
docker system prune -a -f --volumes
|
||||||
if ($LASTEXITCODE -ne 0) {
|
if ($LASTEXITCODE -ne 0) {
|
||||||
exit $LASTEXITCODE
|
Write-Warning-Custom "Docker cleanup had issues, but continuing..."
|
||||||
}
|
}
|
||||||
Write-Host "Unused Docker data cleaned successfully!" -ForegroundColor Green
|
Write-Success "Unused Docker data cleaned successfully!"
|
||||||
|
|
||||||
Write-Host "Removing related files..." -ForegroundColor Magenta
|
# Remove local files
|
||||||
|
Write-Step "Removing related files..."
|
||||||
Remove-Item -Recurse -Force -ErrorAction SilentlyContinue ./media
|
Remove-Item -Recurse -Force -ErrorAction SilentlyContinue ./media
|
||||||
Remove-Item -Recurse -Force -ErrorAction SilentlyContinue ./static
|
Remove-Item -Recurse -Force -ErrorAction SilentlyContinue ./static
|
||||||
Write-Host "Related files removed successfully!" -ForegroundColor Green
|
Write-Success "Related files removed successfully!"
|
||||||
|
|
||||||
Write-Host "Bye-bye, hope you return later!" -ForegroundColor Red
|
Write-Result ""
|
||||||
|
Write-Result "Bye-bye, hope you return later!"
|
||||||
298
scripts/lib/utils.ps1
Normal file
298
scripts/lib/utils.ps1
Normal file
|
|
@ -0,0 +1,298 @@
|
||||||
|
# Shared utilities for Windows scripts
|
||||||
|
# Provides: colors, progress indicators, interactive detection
|
||||||
|
|
||||||
|
Set-StrictMode -Version Latest
|
||||||
|
$ErrorActionPreference = 'Stop'
|
||||||
|
|
||||||
|
# Detect if running in interactive shell
|
||||||
|
function Test-Interactive
|
||||||
|
{
|
||||||
|
# Check if both input and output are from terminal
|
||||||
|
# In non-interactive environments (CI/CD), this will be false
|
||||||
|
$isInteractive = [Environment]::UserInteractive -and
|
||||||
|
(-not [Console]::IsInputRedirected) -and
|
||||||
|
(-not [Console]::IsOutputRedirected)
|
||||||
|
return $isInteractive
|
||||||
|
}
|
||||||
|
|
||||||
|
# Global spinner state
|
||||||
|
$script:SpinnerJob = $null
|
||||||
|
$script:SpinnerFrames = @('⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏')
|
||||||
|
|
||||||
|
# Logging functions
|
||||||
|
function Write-Info
|
||||||
|
{
|
||||||
|
param([string]$Message)
|
||||||
|
|
||||||
|
if (Test-Interactive)
|
||||||
|
{
|
||||||
|
Write-Host $Message -ForegroundColor Blue
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Write-Output $Message
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function Write-Success
|
||||||
|
{
|
||||||
|
param([string]$Message)
|
||||||
|
|
||||||
|
if (Test-Interactive)
|
||||||
|
{
|
||||||
|
Write-Host $Message -ForegroundColor Green
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Write-Output $Message
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function Write-Warning-Custom
|
||||||
|
{
|
||||||
|
param([string]$Message)
|
||||||
|
|
||||||
|
if (Test-Interactive)
|
||||||
|
{
|
||||||
|
Write-Host $Message -ForegroundColor Yellow
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Write-Output "WARNING: $Message"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function Write-Error-Custom
|
||||||
|
{
|
||||||
|
param([string]$Message)
|
||||||
|
|
||||||
|
if (Test-Interactive)
|
||||||
|
{
|
||||||
|
Write-Host $Message -ForegroundColor Red
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Write-Output "ERROR: $Message" *>&1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function Write-Step
|
||||||
|
{
|
||||||
|
param([string]$Message)
|
||||||
|
|
||||||
|
if (Test-Interactive)
|
||||||
|
{
|
||||||
|
Write-Host $Message -ForegroundColor Magenta
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Write-Output $Message
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function Write-Result
|
||||||
|
{
|
||||||
|
param([string]$Message)
|
||||||
|
|
||||||
|
if (Test-Interactive)
|
||||||
|
{
|
||||||
|
Write-Host $Message -ForegroundColor Cyan
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Write-Output $Message
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# Spinner functions (only for interactive shells)
|
||||||
|
function Start-Spinner
|
||||||
|
{
|
||||||
|
param([string]$Message = "Processing...")
|
||||||
|
|
||||||
|
if (-not (Test-Interactive))
|
||||||
|
{
|
||||||
|
Write-Output $Message
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
$script:SpinnerJob = Start-Job -ScriptBlock {
|
||||||
|
param($Frames, $Msg)
|
||||||
|
|
||||||
|
$i = 0
|
||||||
|
while ($true)
|
||||||
|
{
|
||||||
|
$frame = $Frames[$i % $Frames.Length]
|
||||||
|
[Console]::SetCursorPosition(0, [Console]::CursorTop)
|
||||||
|
Write-Host -NoNewline "$frame $Msg"
|
||||||
|
Start-Sleep -Milliseconds 100
|
||||||
|
$i++
|
||||||
|
}
|
||||||
|
} -ArgumentList $script:SpinnerFrames, $Message
|
||||||
|
}
|
||||||
|
|
||||||
|
function Stop-Spinner
|
||||||
|
{
|
||||||
|
if ($script:SpinnerJob -ne $null)
|
||||||
|
{
|
||||||
|
Stop-Job -Job $script:SpinnerJob -ErrorAction SilentlyContinue
|
||||||
|
Remove-Job -Job $script:SpinnerJob -Force -ErrorAction SilentlyContinue
|
||||||
|
$script:SpinnerJob = $null
|
||||||
|
|
||||||
|
if (Test-Interactive)
|
||||||
|
{
|
||||||
|
# Clear the spinner line
|
||||||
|
[Console]::SetCursorPosition(0, [Console]::CursorTop)
|
||||||
|
Write-Host (' ' * ([Console]::WindowWidth - 1)) -NoNewline
|
||||||
|
[Console]::SetCursorPosition(0, [Console]::CursorTop)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function Update-Spinner
|
||||||
|
{
|
||||||
|
param([string]$Message)
|
||||||
|
|
||||||
|
if (-not (Test-Interactive))
|
||||||
|
{
|
||||||
|
Write-Output $Message
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($script:SpinnerJob -ne $null)
|
||||||
|
{
|
||||||
|
Stop-Spinner
|
||||||
|
Start-Spinner -Message $Message
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# Execute command with progress indicator
|
||||||
|
function Invoke-WithProgress
|
||||||
|
{
|
||||||
|
param(
|
||||||
|
[string]$Message,
|
||||||
|
[scriptblock]$ScriptBlock
|
||||||
|
)
|
||||||
|
|
||||||
|
if (Test-Interactive)
|
||||||
|
{
|
||||||
|
Start-Spinner -Message $Message
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
$output = & $ScriptBlock 2>&1
|
||||||
|
$exitCode = $LASTEXITCODE
|
||||||
|
|
||||||
|
Stop-Spinner
|
||||||
|
|
||||||
|
if ($exitCode -eq 0 -or $null -eq $exitCode)
|
||||||
|
{
|
||||||
|
Write-Success "✓ $Message - Done!"
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Write-Error-Custom "✗ $Message - Failed!"
|
||||||
|
if ($output)
|
||||||
|
{
|
||||||
|
Write-Output $output
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $exitCode
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
Stop-Spinner
|
||||||
|
Write-Error-Custom "✗ $Message - Failed!"
|
||||||
|
throw
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
# Non-interactive: just show message and run
|
||||||
|
Write-Output "→ $Message"
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
& $ScriptBlock
|
||||||
|
$exitCode = $LASTEXITCODE
|
||||||
|
|
||||||
|
if ($exitCode -eq 0 -or $null -eq $exitCode)
|
||||||
|
{
|
||||||
|
Write-Output "✓ $Message - Done!"
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Write-Output "✗ $Message - Failed!"
|
||||||
|
}
|
||||||
|
|
||||||
|
return $exitCode
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
Write-Output "✗ $Message - Failed!"
|
||||||
|
throw
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# Check system requirements
|
||||||
|
function Test-SystemRequirements
|
||||||
|
{
|
||||||
|
param(
|
||||||
|
[int]$MinCpu = 4,
|
||||||
|
[int]$MinRamGB = 6,
|
||||||
|
[int]$MinDiskGB = 20
|
||||||
|
)
|
||||||
|
|
||||||
|
Write-Step "Checking system requirements..."
|
||||||
|
|
||||||
|
# Check CPU cores
|
||||||
|
$cpuCount = [Environment]::ProcessorCount
|
||||||
|
if ($cpuCount -lt $MinCpu)
|
||||||
|
{
|
||||||
|
Write-Error-Custom "Insufficient CPU cores: $cpuCount (minimum: $MinCpu)"
|
||||||
|
return $false
|
||||||
|
}
|
||||||
|
|
||||||
|
# Check RAM
|
||||||
|
$totalMemBytes = (Get-CimInstance Win32_ComputerSystem).TotalPhysicalMemory
|
||||||
|
$totalMemGB = [Math]::Round($totalMemBytes / 1GB, 2)
|
||||||
|
if ($totalMemGB -lt $MinRamGB)
|
||||||
|
{
|
||||||
|
Write-Error-Custom "Insufficient RAM: ${totalMemGB}GB (minimum: ${MinRamGB}GB)"
|
||||||
|
return $false
|
||||||
|
}
|
||||||
|
|
||||||
|
# Check disk space
|
||||||
|
$currentDrive = Split-Path -Qualifier $PWD
|
||||||
|
$driveLetter = $currentDrive.Substring(0, 1)
|
||||||
|
$freeGB = [Math]::Round((Get-PSDrive -Name $driveLetter).Free / 1GB, 2)
|
||||||
|
if ($freeGB -lt $MinDiskGB)
|
||||||
|
{
|
||||||
|
Write-Error-Custom "Insufficient disk space: ${freeGB}GB (minimum: ${MinDiskGB}GB)"
|
||||||
|
return $false
|
||||||
|
}
|
||||||
|
|
||||||
|
Write-Success "System requirements met: CPU cores=$cpuCount, RAM=${totalMemGB}GB, FreeDisk=${freeGB}GB"
|
||||||
|
return $true
|
||||||
|
}
|
||||||
|
|
||||||
|
# Confirm action
|
||||||
|
function Confirm-Action
|
||||||
|
{
|
||||||
|
param([string]$Prompt = "Are you sure?")
|
||||||
|
|
||||||
|
if (-not (Test-Interactive))
|
||||||
|
{
|
||||||
|
# In non-interactive mode, assume yes
|
||||||
|
return $true
|
||||||
|
}
|
||||||
|
|
||||||
|
$response = Read-Host "$Prompt [y/N]"
|
||||||
|
return $response -match '^[yY]([eE][sS])?$'
|
||||||
|
}
|
||||||
|
|
||||||
|
# Ensure spinner cleanup on exit
|
||||||
|
$MyInvocation.MyCommand.ScriptBlock.Module.OnRemove = {
|
||||||
|
Stop-Spinner
|
||||||
|
}
|
||||||
223
scripts/lib/utils.sh
Normal file
223
scripts/lib/utils.sh
Normal file
|
|
@ -0,0 +1,223 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
# Shared utilities for Unix scripts
|
||||||
|
# Provides: colors, progress indicators, interactive detection
|
||||||
|
|
||||||
|
# Detect if running in interactive shell
|
||||||
|
is_interactive() {
|
||||||
|
[[ -t 0 && -t 1 ]]
|
||||||
|
}
|
||||||
|
|
||||||
|
# Color definitions (only used in interactive mode)
|
||||||
|
if is_interactive && [[ "${NO_COLOR:-}" != "1" ]]; then
|
||||||
|
COLOR_RESET='\033[0m'
|
||||||
|
COLOR_RED='\033[0;31m'
|
||||||
|
COLOR_GREEN='\033[0;32m'
|
||||||
|
COLOR_YELLOW='\033[0;33m'
|
||||||
|
COLOR_BLUE='\033[0;34m'
|
||||||
|
COLOR_MAGENTA='\033[0;35m'
|
||||||
|
COLOR_CYAN='\033[0;36m'
|
||||||
|
COLOR_GRAY='\033[0;90m'
|
||||||
|
COLOR_BOLD='\033[1m'
|
||||||
|
else
|
||||||
|
COLOR_RESET=''
|
||||||
|
COLOR_RED=''
|
||||||
|
COLOR_GREEN=''
|
||||||
|
COLOR_YELLOW=''
|
||||||
|
COLOR_BLUE=''
|
||||||
|
COLOR_MAGENTA=''
|
||||||
|
COLOR_CYAN=''
|
||||||
|
COLOR_GRAY=''
|
||||||
|
COLOR_BOLD=''
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Logging functions
|
||||||
|
log_info() {
|
||||||
|
echo -e "${COLOR_BLUE}${1}${COLOR_RESET}"
|
||||||
|
}
|
||||||
|
|
||||||
|
log_success() {
|
||||||
|
echo -e "${COLOR_GREEN}${1}${COLOR_RESET}"
|
||||||
|
}
|
||||||
|
|
||||||
|
log_warning() {
|
||||||
|
echo -e "${COLOR_YELLOW}${1}${COLOR_RESET}"
|
||||||
|
}
|
||||||
|
|
||||||
|
log_error() {
|
||||||
|
echo -e "${COLOR_RED}${1}${COLOR_RESET}" >&2
|
||||||
|
}
|
||||||
|
|
||||||
|
log_step() {
|
||||||
|
echo -e "${COLOR_MAGENTA}${1}${COLOR_RESET}"
|
||||||
|
}
|
||||||
|
|
||||||
|
log_result() {
|
||||||
|
echo -e "${COLOR_CYAN}${1}${COLOR_RESET}"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Spinner animation (only for interactive shells)
|
||||||
|
SPINNER_FRAMES=('⠋' '⠙' '⠹' '⠸' '⠼' '⠴' '⠦' '⠧' '⠇' '⠏')
|
||||||
|
SPINNER_PID=""
|
||||||
|
|
||||||
|
start_spinner() {
|
||||||
|
local message="${1:-Processing...}"
|
||||||
|
|
||||||
|
if ! is_interactive; then
|
||||||
|
echo "$message"
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
|
||||||
|
{
|
||||||
|
local i=0
|
||||||
|
while true; do
|
||||||
|
printf "\r${COLOR_CYAN}${SPINNER_FRAMES[$i]}${COLOR_RESET} %s" "$message"
|
||||||
|
i=$(( (i + 1) % ${#SPINNER_FRAMES[@]} ))
|
||||||
|
sleep 0.1
|
||||||
|
done
|
||||||
|
} &
|
||||||
|
SPINNER_PID=$!
|
||||||
|
|
||||||
|
# Ensure spinner is cleaned up on script exit
|
||||||
|
trap "stop_spinner" EXIT INT TERM
|
||||||
|
}
|
||||||
|
|
||||||
|
stop_spinner() {
|
||||||
|
if [[ -n "$SPINNER_PID" ]] && kill -0 "$SPINNER_PID" 2>/dev/null; then
|
||||||
|
kill "$SPINNER_PID" 2>/dev/null
|
||||||
|
wait "$SPINNER_PID" 2>/dev/null
|
||||||
|
SPINNER_PID=""
|
||||||
|
fi
|
||||||
|
|
||||||
|
if is_interactive; then
|
||||||
|
printf "\r\033[K" # Clear the spinner line
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
update_spinner_message() {
|
||||||
|
local message="${1:-Processing...}"
|
||||||
|
|
||||||
|
if ! is_interactive; then
|
||||||
|
echo "$message"
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ -n "$SPINNER_PID" ]] && kill -0 "$SPINNER_PID" 2>/dev/null; then
|
||||||
|
stop_spinner
|
||||||
|
start_spinner "$message"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Execute command with progress indicator
|
||||||
|
run_with_progress() {
|
||||||
|
local message="$1"
|
||||||
|
shift
|
||||||
|
local cmd=("$@")
|
||||||
|
|
||||||
|
if is_interactive; then
|
||||||
|
start_spinner "$message"
|
||||||
|
local output
|
||||||
|
local exit_code
|
||||||
|
|
||||||
|
# Capture output and exit code
|
||||||
|
output=$("${cmd[@]}" 2>&1)
|
||||||
|
exit_code=$?
|
||||||
|
|
||||||
|
stop_spinner
|
||||||
|
|
||||||
|
if [[ $exit_code -eq 0 ]]; then
|
||||||
|
log_success "✓ $message - Done!"
|
||||||
|
else
|
||||||
|
log_error "✗ $message - Failed!"
|
||||||
|
echo "$output"
|
||||||
|
fi
|
||||||
|
|
||||||
|
return $exit_code
|
||||||
|
else
|
||||||
|
# Non-interactive: just show message and run
|
||||||
|
echo "→ $message"
|
||||||
|
"${cmd[@]}"
|
||||||
|
local exit_code=$?
|
||||||
|
|
||||||
|
if [[ $exit_code -eq 0 ]]; then
|
||||||
|
echo "✓ $message - Done!"
|
||||||
|
else
|
||||||
|
echo "✗ $message - Failed!"
|
||||||
|
fi
|
||||||
|
|
||||||
|
return $exit_code
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Check if command exists
|
||||||
|
command_exists() {
|
||||||
|
command -v "$1" >/dev/null 2>&1
|
||||||
|
}
|
||||||
|
|
||||||
|
# Validate system requirements
|
||||||
|
check_system_requirements() {
|
||||||
|
local min_cpu="${1:-4}"
|
||||||
|
local min_ram_gb="${2:-6}"
|
||||||
|
local min_disk_gb="${3:-20}"
|
||||||
|
|
||||||
|
log_step "Checking system requirements..."
|
||||||
|
|
||||||
|
# Check CPU cores
|
||||||
|
local cpu_count
|
||||||
|
cpu_count=$(getconf _NPROCESSORS_ONLN)
|
||||||
|
|
||||||
|
if [[ "$cpu_count" -lt "$min_cpu" ]]; then
|
||||||
|
log_error "Insufficient CPU cores: $cpu_count (minimum: $min_cpu)"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Check RAM
|
||||||
|
local total_mem_gb
|
||||||
|
if [[ -f /proc/meminfo ]]; then
|
||||||
|
local mem_kb
|
||||||
|
mem_kb=$(grep MemTotal /proc/meminfo | awk '{print $2}')
|
||||||
|
total_mem_gb=$(awk "BEGIN {printf \"%.2f\", $mem_kb/1024/1024}")
|
||||||
|
else
|
||||||
|
local total_mem_bytes
|
||||||
|
total_mem_bytes=$(sysctl -n hw.memsize 2>/dev/null || echo "0")
|
||||||
|
total_mem_gb=$(awk "BEGIN {printf \"%.2f\", $total_mem_bytes/1024/1024/1024}")
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! awk "BEGIN {exit !($total_mem_gb >= $min_ram_gb)}"; then
|
||||||
|
log_error "Insufficient RAM: ${total_mem_gb}GB (minimum: ${min_ram_gb}GB)"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Check disk space
|
||||||
|
local avail_kb free_gb
|
||||||
|
avail_kb=$(df -k . | tail -1 | awk '{print $4}')
|
||||||
|
free_gb=$(awk "BEGIN {printf \"%.2f\", $avail_kb/1024/1024}")
|
||||||
|
|
||||||
|
if ! awk "BEGIN {exit !($free_gb >= $min_disk_gb)}"; then
|
||||||
|
log_error "Insufficient disk space: ${free_gb}GB (minimum: ${min_disk_gb}GB)"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
log_success "System requirements met: CPU cores=$cpu_count, RAM=${total_mem_gb}GB, FreeDisk=${free_gb}GB"
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
# Confirm action (returns 0 for yes, 1 for no)
|
||||||
|
confirm() {
|
||||||
|
local prompt="${1:-Are you sure?}"
|
||||||
|
local response
|
||||||
|
|
||||||
|
if ! is_interactive; then
|
||||||
|
# In non-interactive mode, assume yes
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
read -r -p "$prompt [y/N]: " response
|
||||||
|
case "$response" in
|
||||||
|
[yY][eE][sS]|[yY])
|
||||||
|
return 0
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
return 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
}
|
||||||
Loading…
Reference in a new issue