#!/usr/bin/env bash set -euo pipefail SERVICE_NAME="${1:-}" if [ -z "$SERVICE_NAME" ]; then echo "Usage: ./deploy-core-service.sh " exit 1 fi CONFIG_FILE="/opt/appfactory/config/appfactory.env" set -a source "$CONFIG_FILE" set +a export DOCKER_CONFIG="/tmp/.docker" mkdir -p "$DOCKER_CONFIG" DB_FILE="/opt/appfactory/data/appfactory/appfactory.db" SERVICE_DIR="$WORKSPACE_DIR/$SERVICE_NAME" DEPLOY_DIR="$APPFACTORY_DIR/deploy" TRIGGER_SOURCE="${APPFACTORY_TRIGGER_SOURCE:-cli}" TRIGGERED_BY_USER_ID="${APPFACTORY_TRIGGERED_BY_USER_ID:-}" TRIGGERED_BY_USERNAME="${APPFACTORY_TRIGGERED_BY_USERNAME:-}" TRIGGERED_BY_DISPLAY_NAME="${APPFACTORY_TRIGGERED_BY_DISPLAY_NAME:-}" COMMIT_AUTHOR="${APPFACTORY_COMMIT_AUTHOR:-}" PUSHER="${APPFACTORY_PUSHER:-}" if [ ! -d "$SERVICE_DIR" ]; then echo "Missing service directory: $SERVICE_DIR" exit 1 fi if [ ! -f "$SERVICE_DIR/Dockerfile" ]; then echo "Missing Dockerfile: $SERVICE_DIR/Dockerfile" exit 1 fi DEPLOY_ID="$(python3 - "$DB_FILE" "$SERVICE_NAME" "$TRIGGER_SOURCE" "$TRIGGERED_BY_USER_ID" "$TRIGGERED_BY_USERNAME" "$TRIGGERED_BY_DISPLAY_NAME" "$COMMIT_AUTHOR" "$PUSHER" <<'PY' import sqlite3 import sys db_file = sys.argv[1] app_id = sys.argv[2] trigger_source = sys.argv[3] triggered_by_user_id = sys.argv[4] or None triggered_by_username = sys.argv[5] or None triggered_by_display_name = sys.argv[6] or None commit_author = sys.argv[7] or None pusher = sys.argv[8] or None con = sqlite3.connect(db_file) cur = con.execute( """ INSERT INTO deployments ( app_id, kind, status, started_at, trigger_source, triggered_by_user_id, triggered_by_username, triggered_by_display_name, commit_author, pusher ) VALUES (?, ?, ?, CURRENT_TIMESTAMP, ?, ?, ?, ?, ?, ?) """, ( app_id, "core", "running", trigger_source, triggered_by_user_id, triggered_by_username, triggered_by_display_name, commit_author, pusher, ), ) con.commit() print(cur.lastrowid) con.close() PY )" TMP_DIR="$(mktemp -d)" STDOUT_FILE="$TMP_DIR/stdout.log" STDERR_FILE="$TMP_DIR/stderr.log" touch "$STDOUT_FILE" "$STDERR_FILE" finish_deploy() { local status="$1" local returncode="$2" python3 - "$DB_FILE" "$DEPLOY_ID" "$status" "$returncode" "$STDOUT_FILE" "$STDERR_FILE" <<'PY' import sqlite3 import sys from pathlib import Path db_file = sys.argv[1] deploy_id = sys.argv[2] status = sys.argv[3] returncode = int(sys.argv[4]) stdout_file = Path(sys.argv[5]) stderr_file = Path(sys.argv[6]) stdout = stdout_file.read_text(errors="replace") if stdout_file.exists() else "" stderr = stderr_file.read_text(errors="replace") if stderr_file.exists() else "" con = sqlite3.connect(db_file) con.execute( """ UPDATE deployments SET status = ?, finished_at = CURRENT_TIMESTAMP, stdout = ?, stderr = ?, returncode = ? WHERE id = ? """, (status, stdout, stderr, returncode, deploy_id), ) con.commit() con.close() PY } run_step() { local title="$1" shift { echo "" echo "==================================================" echo "$title" echo "==================================================" "$@" } >>"$STDOUT_FILE" 2>>"$STDERR_FILE" } cleanup_conflicting_containers() { local service="$1" docker ps -a --format "{{.ID}} {{.Names}}" \ | awk -v svc="$service" '$2 == svc || $2 ~ ("_" svc "$") { print $1 }' \ | while read -r container_id; do if [ -n "$container_id" ]; then echo "Removing conflicting container: $container_id" docker rm -f "$container_id" || true fi done } set +e run_step "Updating source" bash -c " cd '$SERVICE_DIR' git \ -c safe.directory='$SERVICE_DIR' \ fetch origin main git \ -c safe.directory='$SERVICE_DIR' \ reset --hard origin/main " RESULT=$? if [ "$RESULT" -eq 0 ]; then run_step "Building image" docker build --pull --no-cache -t "$SERVICE_NAME:latest" "$SERVICE_DIR" RESULT=$? fi if [ "$RESULT" -eq 0 ]; then run_step "Cleaning conflicting containers" cleanup_conflicting_containers "$SERVICE_NAME" RESULT=$? fi if [ "$RESULT" -eq 0 ]; then run_step "Recreating container" bash -c " cd '$DEPLOY_DIR' docker compose \ --env-file '$CONFIG_FILE' \ -f docker-compose.yml \ -f apps-compose.yml \ up -d --force-recreate '$SERVICE_NAME' " RESULT=$? fi set -e if [ "$RESULT" -eq 0 ]; then finish_deploy "success" 0 cat "$STDOUT_FILE" cat "$STDERR_FILE" >&2 echo "" echo "DONE" echo "Core service deployed: $SERVICE_NAME" else finish_deploy "failed" "$RESULT" cat "$STDOUT_FILE" cat "$STDERR_FILE" >&2 echo "" echo "FAILED" echo "Deployment ID: $DEPLOY_ID" rm -rf "$TMP_DIR" exit "$RESULT" fi rm -rf "$TMP_DIR"