#!/usr/bin/env bash set -euo pipefail CONFIG_FILE="/opt/appfactory/config/appfactory.env" source "$CONFIG_FILE" APP_ID="${1:-}" if [ -z "$APP_ID" ]; then echo "Usage: ./deploy-app.sh " exit 1 fi export DOCKER_CONFIG="/tmp/.docker" mkdir -p "$DOCKER_CONFIG" DB_FILE="/opt/appfactory/data/appfactory/appfactory.db" APP_SOURCE_DIR="$WORKSPACE_DIR/$APP_ID" TOOLS_DIR="$WORKSPACE_DIR/appfactory-tools" if [ ! -d "$APP_SOURCE_DIR" ]; then echo "Missing app source: $APP_SOURCE_DIR" exit 1 fi if [ ! -f "$APP_SOURCE_DIR/Dockerfile" ]; then echo "Missing Dockerfile in: $APP_SOURCE_DIR" exit 1 fi DEPLOY_ID="$(python3 - "$DB_FILE" "$APP_ID" <<'PY' import sqlite3 import sys db_file = sys.argv[1] app_id = sys.argv[2] con = sqlite3.connect(db_file) cur = con.execute( """ INSERT INTO deployments ( app_id, kind, status, started_at ) VALUES (?, ?, ?, CURRENT_TIMESTAMP) """, (app_id, "app", "running"), ) con.execute( """ UPDATE apps SET status = 'deploying', updated_at = CURRENT_TIMESTAMP WHERE id = ? """, (app_id,), ) con.commit() print(cur.lastrowid) con.close() PY )" TMP_DIR="$(mktemp -d)" STDOUT_FILE="$TMP_DIR/stdout.log" STDERR_FILE="$TMP_DIR/stderr.log" finish_deploy() { local status="$1" local returncode="$2" python3 - "$DB_FILE" "$DEPLOY_ID" "$APP_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] app_id = sys.argv[3] status = sys.argv[4] returncode = int(sys.argv[5]) stdout_file = Path(sys.argv[6]) stderr_file = Path(sys.argv[7]) stdout = stdout_file.read_text(errors="replace") if stdout_file.exists() else "" stderr = stderr_file.read_text(errors="replace") if stderr_file.exists() else "" app_status = "deployed" if status == "success" else "failed" 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.execute( """ UPDATE apps SET status = ?, updated_at = CURRENT_TIMESTAMP WHERE id = ? """, (app_status, app_id), ) con.commit() con.close() PY rm -rf "$TMP_DIR" } run_step() { local title="$1" shift { echo "" echo "==================================================" echo "$title" echo "==================================================" "$@" } >>"$STDOUT_FILE" 2>>"$STDERR_FILE" } set +e run_step "Updating source code" bash -c " cd '$APP_SOURCE_DIR' if [ -d '.git' ]; then git -c safe.directory='$APP_SOURCE_DIR' fetch origin main git -c safe.directory='$APP_SOURCE_DIR' reset --hard origin/main fi " RESULT=$? if [ "$RESULT" -eq 0 ]; then run_step "Building Docker image" docker build --pull --no-cache -t "$APP_ID:latest" "$APP_SOURCE_DIR" RESULT=$? fi if [ "$RESULT" -eq 0 ]; then run_step "Regenerating generated files" bash -c " '$TOOLS_DIR/scripts/generate-catalog.sh' '$TOOLS_DIR/scripts/generate-apps-compose.sh' '$TOOLS_DIR/scripts/generate-caddyfile.sh' '$TOOLS_DIR/scripts/generate-apps-page.sh' " RESULT=$? fi if [ "$RESULT" -eq 0 ]; then run_step "Starting app container" bash -c " cd '$APPFACTORY_DIR/deploy' docker compose -f docker-compose.yml -f apps-compose.yml up -d --force-recreate '$APP_ID' " RESULT=$? fi set -e if [ "$RESULT" -eq 0 ]; then finish_deploy "success" 0 cat "$STDOUT_FILE" cat "$STDERR_FILE" >&2 echo "" echo "DONE" echo "Open:" echo "$APPFACTORY_PUBLIC_URL/apps/$APP_ID/docs" else finish_deploy "failed" "$RESULT" cat "$STDOUT_FILE" cat "$STDERR_FILE" >&2 echo "" echo "FAILED" echo "Deployment ID: $DEPLOY_ID" exit "$RESULT" fi