Files
appfactory-tools/scripts/deploy-core-service.sh
T

227 lines
4.8 KiB
Bash
Executable File

#!/usr/bin/env bash
set -euo pipefail
SERVICE_NAME="${1:-}"
if [ -z "$SERVICE_NAME" ]; then
echo "Usage: ./deploy-core-service.sh <service-name>"
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"