chore(devops): tighten deployment scripts

This commit is contained in:
root 2026-05-18 09:01:03 +00:00
parent 85f21ae9f6
commit 1deb832b1a
3 changed files with 275 additions and 27 deletions

View File

@ -4,17 +4,21 @@ set -euo pipefail
# ═══════════════════════════════════════════════════════════════════════
# ByteLyst ChronoMind - Production Deployment Script
# ═══════════════════════════════════════════════════════════════════════
# Usage: ./deploy-clock.sh [--force] [--skip-health-check]
# Usage: ./deploy-clock.sh [option]
#
# 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 (interactive menu when no arguments):
# 1 - Normal deployment (with cache, with health checks)
# 2 - Force deployment (skip dirty checks, with cache)
# 3 - Skip health checks (with cache)
# 4 - No-cache build (force rebuild, with health checks)
# 5 - Force + No-cache (skip checks, force rebuild)
# 6 - Force + Skip health checks (skip both)
# 7 - All options: Force + Skip health + No-cache
#
# Options:
# Command-line options:
# --force Skip dirty checks and force deployment
# --skip-health-check Skip endpoint health verification
# --no-cache Force rebuild without cache
# ═══════════════════════════════════════════════════════════════════════
RED='\033[0;31m'; GREEN='\033[0;32m'; YELLOW='\033[1;33m'; CYAN='\033[0;36m'; NC='\033[0m'
@ -31,15 +35,47 @@ cd "$SCRIPT_DIR"
FORCE=false
SKIP_HEALTH_CHECK=false
NO_CACHE=false
# Parse command-line arguments
while [[ $# -gt 0 ]]; do
case "$1" in
--force) FORCE=true; shift ;;
--skip-health-check) SKIP_HEALTH_CHECK=true; shift ;;
*) fail "Unknown option: $1" ;;
--no-cache) NO_CACHE=true; shift ;;
*) fail "Unknown option: $1";;
esac
done
# ── Interactive Menu ─────────────────────────────────────────────────
if [ "$FORCE" = false ] && [ "$SKIP_HEALTH_CHECK" = false ] && [ "$NO_CACHE" = false ]; then
echo ""
echo -e "${CYAN}╔═══════════════════════════════════════════════════════════════╗${NC}"
echo -e "${CYAN}${NC} ByteLyst ChronoMind - Deployment Options ${CYAN}${NC}"
echo -e "${CYAN}╚═══════════════════════════════════════════════════════════════╝${NC}"
echo ""
echo -e " ${GREEN}1${NC} - Normal deployment (with cache, with health checks)"
echo -e " ${GREEN}2${NC} - Force deployment (skip dirty checks, with cache)"
echo -e " ${GREEN}3${NC} - Skip health checks (with cache)"
echo -e " ${GREEN}4${NC} - No-cache build (force rebuild, with health checks)"
echo -e " ${GREEN}5${NC} - Force + No-cache (skip checks, force rebuild)"
echo -e " ${GREEN}6${NC} - Force + Skip health checks (skip both)"
echo -e " ${GREEN}7${NC} - All options: Force + Skip health + No-cache"
echo ""
read -r -p "Select option (1-7): " choice
case $choice in
1) ;;
2) FORCE=true ;;
3) SKIP_HEALTH_CHECK=true ;;
4) NO_CACHE=true ;;
5) FORCE=true; NO_CACHE=true ;;
6) FORCE=true; SKIP_HEALTH_CHECK=true ;;
7) FORCE=true; SKIP_HEALTH_CHECK=true; NO_CACHE=true ;;
*) fail "Invalid option. Please select 1-7." ;;
esac
fi
# ── Prerequisites ────────────────────────────────────────────────────
if [ ! -d "$REPO_DIR" ]; then
fail "Repo directory not found: $REPO_DIR"
@ -105,6 +141,48 @@ if [ "$SKIP_HEALTH_CHECK" = false ]; then
fi
fi
# ── Package Publication Check ───────────────────────────────────────────
log "Checking @bytelyst package publication status..."
# Read Gitea token from file
unset GITEA_NPM_TOKEN
if [ -f "/opt/bytelyst/.gitea_token" ]; then
GITEA_NPM_TOKEN="$(< /opt/bytelyst/.gitea_token)"
elif [ -f "$HOME/.gitea_npm_token" ]; then
GITEA_NPM_TOKEN="$(< "$HOME/.gitea_npm_token")"
fi
if [ -z "$GITEA_NPM_TOKEN" ]; then
warn "Gitea token not found, skipping package publication check"
else
GITEA_REGISTRY="http://localhost:3300/api/packages/ByteLyst/npm/"
CRITICAL_PACKAGES=(
"@bytelyst/config|@bytelyst%2fconfig"
"@bytelyst/cosmos|@bytelyst%2fcosmos"
"@bytelyst/errors|@bytelyst%2ferrors"
"@bytelyst/fastify-core|@bytelyst%2ffastify-core"
)
MISSING_PACKAGES=()
for entry in "${CRITICAL_PACKAGES[@]}"; do
package="${entry%%|*}"
encoded="${entry##*|}"
if ! curl -sf -H "Authorization: token ${GITEA_NPM_TOKEN}" "${GITEA_REGISTRY}${encoded}" > /dev/null 2>&1; then
MISSING_PACKAGES+=("$package")
fi
done
if [ "${#MISSING_PACKAGES[@]}" -eq 0 ]; then
ok "All critical @bytelyst packages are published"
else
warn "Some @bytelyst packages may not be published: ${MISSING_PACKAGES[*]}"
read -r -p "Continue anyway? (y/N): " continue_anyway
if [[ ! "$continue_anyway" =~ ^[Yy]$ ]]; then
fail "Deployment cancelled"
fi
fi
fi
# ── Build and Deploy ──────────────────────────────────────────────────
log "Building and deploying Docker containers..."
@ -114,8 +192,15 @@ if [ ! -f "docker-compose.yml" ]; then
fi
# Build and start services
log "Building Docker images..."
docker compose build || fail "Docker build failed"
BUILD_ARGS=()
if [ "$NO_CACHE" = true ]; then
BUILD_ARGS+=(--no-cache)
log "Building Docker images without cache..."
else
log "Building Docker images..."
fi
docker compose build "${BUILD_ARGS[@]}" || fail "Docker build failed"
log "Starting services..."
docker compose up -d || fail "Docker compose up failed"
@ -171,7 +256,7 @@ fi
log "Verifying production endpoints..."
API_ENDPOINT="https://api.bytelyst.com/chronomind"
WEB_ENDPOINT="http://localhost:3030"
WEB_ENDPOINT="https://clock.bytelyst.com"
# Check API endpoint
if curl -sf "$API_ENDPOINT/health" > /dev/null 2>&1; then
@ -219,5 +304,5 @@ fi
log "══════════════════════════════════════════════════════════════════════"
ok "Deployment completed successfully!"
log "Backend: http://localhost:4011 → $API_ENDPOINT"
log "Web: http://localhost:3030"
log "Web: http://localhost:3030$WEB_ENDPOINT"
log "══════════════════════════════════════════════════════════════════════"

View File

@ -4,17 +4,21 @@ set -euo pipefail
# ═══════════════════════════════════════════════════════════════════════
# ByteLyst NoteLett - Production Deployment Script
# ═══════════════════════════════════════════════════════════════════════
# Usage: ./deploy-notes.sh [--force] [--skip-health-check]
# Usage: ./deploy-notes.sh [option]
#
# 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/notelett, http://localhost:3000
# Options (interactive menu when no arguments):
# 1 - Normal deployment (with cache, with health checks)
# 2 - Force deployment (skip dirty checks, with cache)
# 3 - Skip health checks (with cache)
# 4 - No-cache build (force rebuild, with health checks)
# 5 - Force + No-cache (skip checks, force rebuild)
# 6 - Force + Skip health checks (skip both)
# 7 - All options: Force + Skip health + No-cache
#
# Options:
# Command-line options:
# --force Skip dirty checks and force deployment
# --skip-health-check Skip endpoint health verification
# --no-cache Force rebuild without cache
# ═══════════════════════════════════════════════════════════════════════
RED='\033[0;31m'; GREEN='\033[0;32m'; YELLOW='\033[1;33m'; CYAN='\033[0;36m'; NC='\033[0m'
@ -31,15 +35,47 @@ cd "$SCRIPT_DIR"
FORCE=false
SKIP_HEALTH_CHECK=false
NO_CACHE=false
# Parse command-line arguments
while [[ $# -gt 0 ]]; do
case "$1" in
--force) FORCE=true; shift ;;
--skip-health-check) SKIP_HEALTH_CHECK=true; shift ;;
*) fail "Unknown option: $1" ;;
--no-cache) NO_CACHE=true; shift ;;
*) fail "Unknown option: $1";;
esac
done
# ── Interactive Menu ─────────────────────────────────────────────────
if [ "$FORCE" = false ] && [ "$SKIP_HEALTH_CHECK" = false ] && [ "$NO_CACHE" = false ]; then
echo ""
echo -e "${CYAN}╔═══════════════════════════════════════════════════════════════╗${NC}"
echo -e "${CYAN}${NC} ByteLyst NoteLett - Deployment Options ${CYAN}${NC}"
echo -e "${CYAN}╚═══════════════════════════════════════════════════════════════╝${NC}"
echo ""
echo -e " ${GREEN}1${NC} - Normal deployment (with cache, with health checks)"
echo -e " ${GREEN}2${NC} - Force deployment (skip dirty checks, with cache)"
echo -e " ${GREEN}3${NC} - Skip health checks (with cache)"
echo -e " ${GREEN}4${NC} - No-cache build (force rebuild, with health checks)"
echo -e " ${GREEN}5${NC} - Force + No-cache (skip checks, force rebuild)"
echo -e " ${GREEN}6${NC} - Force + Skip health checks (skip both)"
echo -e " ${GREEN}7${NC} - All options: Force + Skip health + No-cache"
echo ""
read -r -p "Select option (1-7): " choice
case $choice in
1) ;;
2) FORCE=true ;;
3) SKIP_HEALTH_CHECK=true ;;
4) NO_CACHE=true ;;
5) FORCE=true; NO_CACHE=true ;;
6) FORCE=true; SKIP_HEALTH_CHECK=true ;;
7) FORCE=true; SKIP_HEALTH_CHECK=true; NO_CACHE=true ;;
*) fail "Invalid option. Please select 1-7." ;;
esac
fi
# ── Prerequisites ────────────────────────────────────────────────────
if [ ! -d "$REPO_DIR" ]; then
fail "Repo directory not found: $REPO_DIR"
@ -105,6 +141,48 @@ if [ "$SKIP_HEALTH_CHECK" = false ]; then
fi
fi
# ── Package Publication Check ───────────────────────────────────────────
log "Checking @bytelyst package publication status..."
# Read Gitea token from file
unset GITEA_NPM_TOKEN
if [ -f "/opt/bytelyst/.gitea_token" ]; then
GITEA_NPM_TOKEN="$(< /opt/bytelyst/.gitea_token)"
elif [ -f "$HOME/.gitea_npm_token" ]; then
GITEA_NPM_TOKEN="$(< "$HOME/.gitea_npm_token")"
fi
if [ -z "$GITEA_NPM_TOKEN" ]; then
warn "Gitea token not found, skipping package publication check"
else
GITEA_REGISTRY="http://localhost:3300/api/packages/ByteLyst/npm/"
CRITICAL_PACKAGES=(
"@bytelyst/config|@bytelyst%2fconfig"
"@bytelyst/cosmos|@bytelyst%2fcosmos"
"@bytelyst/errors|@bytelyst%2ferrors"
"@bytelyst/fastify-core|@bytelyst%2ffastify-core"
)
MISSING_PACKAGES=()
for entry in "${CRITICAL_PACKAGES[@]}"; do
package="${entry%%|*}"
encoded="${entry##*|}"
if ! curl -sf -H "Authorization: token ${GITEA_NPM_TOKEN}" "${GITEA_REGISTRY}${encoded}" > /dev/null 2>&1; then
MISSING_PACKAGES+=("$package")
fi
done
if [ "${#MISSING_PACKAGES[@]}" -eq 0 ]; then
ok "All critical @bytelyst packages are published"
else
warn "Some @bytelyst packages may not be published: ${MISSING_PACKAGES[*]}"
read -r -p "Continue anyway? (y/N): " continue_anyway
if [[ ! "$continue_anyway" =~ ^[Yy]$ ]]; then
fail "Deployment cancelled"
fi
fi
fi
# ── Build and Deploy ──────────────────────────────────────────────────
log "Building and deploying Docker containers..."
@ -114,8 +192,15 @@ if [ ! -f "docker-compose.yml" ]; then
fi
# Build and start services
log "Building Docker images..."
docker compose build || fail "Docker build failed"
BUILD_ARGS=()
if [ "$NO_CACHE" = true ]; then
BUILD_ARGS+=(--no-cache)
log "Building Docker images without cache..."
else
log "Building Docker images..."
fi
docker compose build "${BUILD_ARGS[@]}" || fail "Docker build failed"
log "Starting services..."
docker compose up -d || fail "Docker compose up failed"
@ -152,7 +237,7 @@ fi
# Check web health
WEB_HEALTH=false
for _ in {1..30}; do
if curl -sf http://localhost:3000 > /dev/null 2>&1; then
if curl -sf http://localhost:3045 > /dev/null 2>&1; then
WEB_HEALTH=true
break
fi
@ -162,7 +247,7 @@ done
echo ""
if [ "$WEB_HEALTH" = true ]; then
ok "Web health check passed (http://localhost:3000)"
ok "Web health check passed (http://localhost:3045)"
else
warn "Web health check failed (may be starting up)"
fi
@ -171,7 +256,7 @@ fi
log "Verifying production endpoints..."
API_ENDPOINT="https://api.bytelyst.com/notelett"
WEB_ENDPOINT="http://localhost:3000"
WEB_ENDPOINT="https://notes.bytelyst.com"
# Check API endpoint
if curl -sf "$API_ENDPOINT/health" > /dev/null 2>&1; then
@ -202,7 +287,7 @@ else
fi
# Test web is serving content
WEB_URL="http://localhost:3000"
WEB_URL="http://localhost:3045"
if curl -sf "$WEB_URL" > /dev/null 2>&1; then
ok "Web frontend is serving content"
# Check if it's actually HTML
@ -219,5 +304,5 @@ fi
log "══════════════════════════════════════════════════════════════════════"
ok "Deployment completed successfully!"
log "Backend: http://localhost:4016 → $API_ENDPOINT"
log "Web: http://localhost:3000"
log "Web: http://localhost:3045 → $WEB_ENDPOINT"
log "══════════════════════════════════════════════════════════════════════"

View File

@ -0,0 +1,78 @@
#!/usr/bin/env bash
set -euo pipefail
# ════════════════════════════════════════════════════════════════
# Lucky25 Execution Monitoring Script
# ════════════════════════════════════════════════════════════════
# Runs every 15 minutes to monitor lucky25 test plan execution
# Logs status to /var/log/lucky25-monitoring.log
# ════════════════════════════════════════════════════════════════
LOG_FILE="/var/log/lucky25-monitoring.log"
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
BACKEND_DIR="${SCRIPT_DIR}/../learning_ai_invt_trdg/backend"
log() {
echo "[$(date '+%Y-%m-%d %H:%M:%S')] $*" | tee -a "$LOG_FILE"
}
log "════════════════════════════════════════════════════════════════"
log "Starting Lucky25 Execution Monitoring"
log "════════════════════════════════════════════════════════════════"
# Create log file if it doesn't exist
touch "$LOG_FILE"
cd "$BACKEND_DIR"
# Run the status check using node with the compiled JS
node -e "
const { config } = require('./dist/src/config/index.js');
const { MANUAL_ENTRY_CONTAINER, queryDocuments } = require('./dist/src/services/tradingRecordStore.js');
async function main() {
const query = 'SELECT * FROM c WHERE c.productId = @productId AND c.type = @type ORDER BY c.created_at DESC';
const rows = await queryDocuments(MANUAL_ENTRY_CONTAINER, query, [
{ name: '@productId', value: config.PRODUCT_ID },
{ name: '@type', value: 'manual_entry' },
]);
const lucky25Plans = rows.filter(row =>
row.hashtags && Array.isArray(row.hashtags) && row.hashtags.includes('lucky25') && row.active === true
);
const byStatus = {};
for (const plan of lucky25Plans) {
const status = plan.status || 'unknown';
byStatus[status] = (byStatus[status] || 0) + 1;
}
console.log('Lucky25 Plans Status:');
console.log('Total: ' + lucky25Plans.length);
console.log('Status breakdown:');
for (const [status, count] of Object.entries(byStatus)) {
console.log(' ' + status + ': ' + count);
}
const executedPlans = lucky25Plans.filter(p =>
p.status !== 'simple_armed_buy' && p.status !== 'deleted'
);
console.log('Execution Progress:');
console.log('Executed: ' + executedPlans.length + '/' + lucky25Plans.length);
console.log('Rate: ' + ((executedPlans.length / lucky25Plans.length) * 100).toFixed(1) + '%');
if (executedPlans.length > 0) {
console.log('Recent executions:');
const recent = executedPlans.slice(0, 3);
for (const plan of recent) {
console.log(' ' + plan.symbol + ' - ' + plan.status + ' - ' + plan.label);
}
}
}
main().catch(console.error);
"
log "Monitoring check completed"
log "════════════════════════════════════════════════════════════════"