bytelyst-devops-tools/deploy-clock.sh
root d4f1d1e97d revert(deploy): revert base image changes due to workspace complexity
Reverting deployment script changes that were part of the base image approach.
The base image strategy is too complex for the current pnpm workspace structure.
Reverting to the proven docker-prep.sh tarball approach.

Generated with [Devin](https://cli.devin.ai/docs)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-05-10 00:27:24 +00:00

224 lines
8.2 KiB
Bash
Executable File

#!/usr/bin/env bash
set -euo pipefail
# ═══════════════════════════════════════════════════════════════════════
# ByteLyst ChronoMind - Production Deployment Script
# ═══════════════════════════════════════════════════════════════════════
# Usage: ./deploy-clock.sh [--force] [--skip-health-check]
#
# What it does:
# 1. Dirty check: uncommitted changes, unpushed commits
# 2. Pull and rebase origin/main
# 3. Build and deploy Docker containers
# 4. Verify endpoints: https://api.bytelyst.com/chronomind, http://localhost:3030
#
# Options:
# --force Skip dirty checks and force deployment
# --skip-health-check Skip endpoint health verification
# ═══════════════════════════════════════════════════════════════════════
RED='\033[0;31m'; GREEN='\033[0;32m'; YELLOW='\033[1;33m'; CYAN='\033[0;36m'; NC='\033[0m'
log() { echo -e "${CYAN}[$(date +%H:%M:%S)]${NC} $*"; }
ok() { echo -e "${GREEN}[$(date +%H:%M:%S)] ✓${NC} $*"; }
warn() { echo -e "${YELLOW}[$(date +%H:%M:%S)] ⚠${NC} $*"; }
fail() { echo -e "${RED}[$(date +%H:%M:%S)] ✗${NC} $*"; exit 1; }
# ── Configuration ────────────────────────────────────────────────────
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
REPO_DIR="${SCRIPT_DIR}/../learning_ai_clock"
cd "$SCRIPT_DIR"
FORCE=false
SKIP_HEALTH_CHECK=false
while [[ $# -gt 0 ]]; do
case "$1" in
--force) FORCE=true; shift ;;
--skip-health-check) SKIP_HEALTH_CHECK=true; shift ;;
*) fail "Unknown option: $1" ;;
esac
done
# ── Prerequisites ────────────────────────────────────────────────────
if [ ! -d "$REPO_DIR" ]; then
fail "Repo directory not found: $REPO_DIR"
fi
cd "$REPO_DIR"
# ── Dirty Check ───────────────────────────────────────────────────────
if [ "$FORCE" = false ]; then
log "Running dirty checks..."
# Check for uncommitted changes
if ! git diff-index --quiet HEAD --; then
fail "Uncommitted changes detected. Commit or stash first, or use --force"
fi
# Check for untracked files
if [ -n "$(git ls-files --others --exclude-standard)" ]; then
fail "Untracked files detected. Commit or remove them, or use --force"
fi
# Check for unpushed commits
LOCAL_COMMIT=$(git rev-parse @)
REMOTE_COMMIT=$(git rev-parse '@{u}' 2>/dev/null || echo "")
if [ -n "$REMOTE_COMMIT" ] && [ "$LOCAL_COMMIT" != "$REMOTE_COMMIT" ]; then
fail "Unpushed commits detected. Push first or use --force"
fi
ok "Dirty checks passed"
else
warn "Skipping dirty checks (--force enabled)"
fi
# ── Pull and Rebase ───────────────────────────────────────────────────
log "Pulling latest changes from origin/main..."
git fetch origin
LOCAL_MAIN=$(git rev-parse main)
REMOTE_MAIN=$(git rev-parse origin/main)
if [ "$LOCAL_MAIN" != "$REMOTE_MAIN" ]; then
log "Local main is behind origin/main, rebasing..."
git rebase origin/main || {
fail "Rebase failed. Resolve conflicts and run: git rebase --continue"
}
ok "Rebase completed successfully"
else
ok "Already up to date with origin/main"
fi
# ── Run Smoke Tests ─────────────────────────────────────────────────────
if [ "$SKIP_HEALTH_CHECK" = false ]; then
log "Running smoke tests before deployment..."
if [ -f "scripts/smoke-release.sh" ]; then
chmod +x scripts/smoke-release.sh
./scripts/smoke-release.sh || {
fail "Smoke tests failed. Fix issues before deploying or use --skip-health-check"
}
ok "Smoke tests passed"
else
warn "Smoke test script not found, skipping pre-deployment tests"
fi
fi
# ── Build and Deploy ──────────────────────────────────────────────────
log "Building and deploying Docker containers..."
# Check if docker-compose files exist
if [ ! -f "docker-compose.yml" ]; then
fail "docker-compose.yml not found in $REPO_DIR"
fi
# Build and start services
log "Building Docker images..."
docker compose build || fail "Docker build failed"
log "Starting services..."
docker compose up -d || fail "Docker compose up failed"
ok "Deployment completed"
# ── Health Check ──────────────────────────────────────────────────────
if [ "$SKIP_HEALTH_CHECK" = true ]; then
warn "Skipping health checks (--skip-health-check enabled)"
exit 0
fi
log "Waiting for services to be healthy..."
sleep 10
# Check backend health
BACKEND_HEALTH=false
for _ in {1..30}; do
if curl -sf http://localhost:4011/health > /dev/null 2>&1; then
BACKEND_HEALTH=true
break
fi
echo -n "."
sleep 2
done
echo ""
if [ "$BACKEND_HEALTH" = true ]; then
ok "Backend health check passed (http://localhost:4011)"
else
fail "Backend health check failed"
fi
# Check web health
WEB_HEALTH=false
for _ in {1..30}; do
if curl -sf http://localhost:3030 > /dev/null 2>&1; then
WEB_HEALTH=true
break
fi
echo -n "."
sleep 2
done
echo ""
if [ "$WEB_HEALTH" = true ]; then
ok "Web health check passed (http://localhost:3030)"
else
warn "Web health check failed (may be starting up)"
fi
# ── Endpoint Verification ─────────────────────────────────────────────
log "Verifying production endpoints..."
API_ENDPOINT="https://api.bytelyst.com/chronomind"
WEB_ENDPOINT="http://localhost:3030"
# Check API endpoint
if curl -sf "$API_ENDPOINT/health" > /dev/null 2>&1; then
ok "API endpoint accessible: $API_ENDPOINT"
else
warn "API endpoint not accessible: $API_ENDPOINT (may need DNS propagation or routing)"
fi
# Check web endpoint
if curl -sf "$WEB_ENDPOINT" > /dev/null 2>&1; then
ok "Web endpoint accessible: $WEB_ENDPOINT"
else
warn "Web endpoint not accessible: $WEB_ENDPOINT (may be starting up)"
fi
# ── API Smoke Tests (Post-Deployment) ───────────────────────────────────
log "Running post-deployment API smoke tests..."
# Test backend health endpoint
BACKEND_URL="http://localhost:4011"
if curl -sf "$BACKEND_URL/health" > /dev/null 2>&1; then
ok "Backend health endpoint responding"
# Try to get health details
HEALTH_RESPONSE=$(curl -s "$BACKEND_URL/health" 2>/dev/null || echo "{}")
log "Health response: $HEALTH_RESPONSE"
else
fail "Backend health endpoint not responding"
fi
# Test web is serving content
WEB_URL="http://localhost:3030"
if curl -sf "$WEB_URL" > /dev/null 2>&1; then
ok "Web frontend is serving content"
# Check if it's actually HTML
CONTENT_TYPE=$(curl -sI "$WEB_URL" | grep -i content-type || echo "")
if echo "$CONTENT_TYPE" | grep -qi "text/html"; then
ok "Web frontend is serving HTML content"
else
warn "Web frontend content type unexpected: $CONTENT_TYPE"
fi
else
fail "Web frontend not responding"
fi
log "══════════════════════════════════════════════════════════════════════"
ok "Deployment completed successfully!"
log "Backend: http://localhost:4011 → $API_ENDPOINT"
log "Web: http://localhost:3030"
log "══════════════════════════════════════════════════════════════════════"