fix: deploy.py --initial als einzelner docker-compose Stack + docker-compose.yaml rename

This commit is contained in:
Agent Zero
2026-08-05 22:11:29 +02:00
parent 4b72530566
commit c278597757
2 changed files with 129 additions and 116 deletions
+129 -116
View File
@@ -401,7 +401,8 @@ def ssh_run(cmd: str, timeout: int = 60) -> tuple[int, str]:
def deploy_api(client: CoolifyClient, app_uuid: str, skip_build: bool = False) -> StepResult:
"""Deploy or restart the API application via Coolify API."""
"""Deploy or restart the application via Coolify API.
Uses /api/v1/deploy which works for both dockerfile and dockercompose build packs."""
# Set FQDN via PATCH
if APP_DOMAIN:
print(f" Setting FQDN to {APP_DOMAIN}...")
@@ -410,26 +411,24 @@ def deploy_api(client: CoolifyClient, app_uuid: str, skip_build: bool = False) -
except Exception as e:
print(f" Warning: could not set FQDN: {e}")
if skip_build:
print(" Skip-build mode: restarting application via Coolify API...")
try:
result = client.deploy_application(app_uuid)
deploy_uuid = _extract_deploy_uuid(result)
if deploy_uuid:
print(f" Deploy queued: {deploy_uuid[:12]}")
return _wait_deployment(client, deploy_uuid, timeout=300)
return StepResult(True, "Deploy triggered (no UUID returned)")
except Exception as e:
return StepResult(False, f"Deploy trigger failed: {e}")
print(" Triggering Coolify build & deploy for API...")
print(" Triggering Coolify deploy via /api/v1/deploy...")
try:
result = client.deploy_application(app_uuid)
deploy_uuid = _extract_deploy_uuid(result)
if not deploy_uuid:
return StepResult(False, "No deployment UUID returned from Coolify")
print(f" Deploy queued: {deploy_uuid[:12]}")
return _wait_deployment(client, deploy_uuid, timeout=300)
resp = httpx.post(
f"{client.base_url}/api/v1/deploy",
headers=client.headers,
json={"uuid": app_uuid},
timeout=30,
)
if resp.status_code == 200:
deployments = resp.json().get("deployments", [])
if deployments:
deploy_uuid = deployments[0].get("deployment_uuid", "")
if deploy_uuid:
print(f" Deploy queued: {deploy_uuid[:12]}")
return _wait_deployment(client, deploy_uuid, timeout=600)
return StepResult(True, "Deploy triggered (no UUID returned)")
else:
return StepResult(False, f"Deploy failed: {resp.status_code} {resp.text[:200]}")
except Exception as e:
return StepResult(False, f"Deploy trigger failed: {e}")
@@ -883,6 +882,7 @@ POSTGRES_COMPOSE = (
"services:\n"
" postgres:\n"
" image: pgvector/pgvector:pg16\n"
" container_name: crm-postgres\n"
" restart: unless-stopped\n"
" environment:\n"
" POSTGRES_USER: ${DB_USER}\n"
@@ -906,6 +906,7 @@ REDIS_COMPOSE = (
"services:\n"
" redis:\n"
" image: redis:7-alpine\n"
" container_name: crm-redis\n"
" restart: unless-stopped\n"
" command: redis-server --requirepass ${REDIS_PASSWORD}\n"
" volumes:\n"
@@ -1084,119 +1085,131 @@ def deploy_initial() -> int:
environment_name = os.environ.get("COOLIFY_ENVIRONMENT", "production")
server_uuid = os.environ.get("COOLIFY_SERVER_UUID", "")
# Step 1: Create PostgreSQL service
print("\n[1/7] Creating PostgreSQL service...")
pg_uuid = create_service(client, project_uuid, environment_name, server_uuid,
POSTGRES_COMPOSE, "crm-postgres")
if pg_uuid:
print(f" PostgreSQL service created: {pg_uuid[:12]}")
set_service_envs(client, pg_uuid, get_postgres_envs())
client.deploy_application(pg_uuid)
time.sleep(10)
steps.append(("PostgreSQL service", StepResult(True, f"Created: {pg_uuid[:12]}")))
else:
steps.append(("PostgreSQL service", StepResult(False, "Failed to create")))
print_summary(steps)
return 1
_print_result(steps[-1][1])
# ─── Single docker-compose application (like the original working app) ──
# All 4 containers (postgres, redis, crm-app, crm-worker) in one stack.
# Coolify reads docker-compose.yaml from the Git repo, builds images, all
# containers share one network. Service names work as DNS names. No manual
# network configuration needed. No cryptic container names.
# Step 2: Create Redis service
print("\n[2/7] Creating Redis service...")
redis_uuid = create_service(client, project_uuid, environment_name, server_uuid,
REDIS_COMPOSE, "crm-redis")
if redis_uuid:
print(f" Redis service created: {redis_uuid[:12]}")
set_service_envs(client, redis_uuid, get_redis_envs())
client.deploy_application(redis_uuid)
time.sleep(10)
steps.append(("Redis service", StepResult(True, f"Created: {redis_uuid[:12]}")))
else:
steps.append(("Redis service", StepResult(False, "Failed to create")))
print_summary(steps)
return 1
_print_result(steps[-1][1])
# Step 3: Create API application
print("\n[3/7] Creating API application...")
api_uuid = create_api_application(client, project_uuid, environment_name, server_uuid)
if api_uuid:
print(f" API application created: {api_uuid[:12]}")
set_application_envs(client, api_uuid, get_api_envs())
# Set FQDN
if APP_DOMAIN:
client.update_application(api_uuid, domains=APP_DOMAIN)
steps.append(("API application", StepResult(True, f"Created: {api_uuid[:12]}")))
else:
steps.append(("API application", StepResult(False, "Failed to create")))
print_summary(steps)
return 1
_print_result(steps[-1][1])
# Step 4: Create Worker service (with API image reference)
print("\n[4/7] Creating Worker service...")
worker_compose = generate_worker_compose(api_uuid, "PLACEHOLDER")
# For initial creation, use a temporary compose — Coolify will assign UUID
# We'll update it after creation with the real UUID
worker_uuid = create_service(client, project_uuid, environment_name, server_uuid,
worker_compose, WORKER_NAME)
if worker_uuid:
print(f" Worker service created: {worker_uuid[:12]}")
# Update compose with real worker UUID
real_compose = generate_worker_compose(api_uuid, worker_uuid)
client.update_service(worker_uuid, real_compose)
# Set connect_to_docker_network
httpx.patch(
f"{client.base_url}/api/v1/services/{worker_uuid}",
# Step 1: Create application via private-deploy-key (with Git repo)
print("\n[1/4] Creating docker-compose application...")
try:
resp = httpx.post(
f"{client.base_url}/api/v1/applications/private-deploy-key",
headers=client.headers,
json={"connect_to_docker_network": True},
json={
"project_uuid": project_uuid,
"environment_name": environment_name,
"server_uuid": server_uuid,
"name": APP_NAME,
"git_repository": API_GIT_REPO,
"git_branch": API_GIT_BRANCH,
"private_key_uuid": os.environ.get("COOLIFY_PRIVATE_KEY_UUID", ""),
"build_pack": "dockerfile",
"ports_exposes": "8000",
},
timeout=30,
)
set_service_envs(client, worker_uuid, get_worker_envs())
steps.append(("Worker service", StepResult(True, f"Created: {worker_uuid[:12]}")))
else:
steps.append(("Worker service", StepResult(False, "Failed to create")))
if resp.status_code == 201:
app_uuid = resp.json().get("uuid")
print(f" Application created: {app_uuid[:12]}")
steps.append(("Create application", StepResult(True, f"Created: {app_uuid[:12]}")))
else:
steps.append(("Create application", StepResult(False, f"{resp.status_code}: {resp.text[:200]}")))
print_summary(steps)
return 1
except Exception as e:
steps.append(("Create application", StepResult(False, str(e))))
print_summary(steps)
return 1
_print_result(steps[-1][1])
# Step 5: Deploy API (builds image + runs migrations via prestart.sh)
print("\n[5/7] Deploying API (build + migrations)...")
# Step 2: PATCH to dockercompose build_pack (Coolify reads docker-compose.yaml from Git)
print("\n[2/4] Configuring docker-compose build pack...")
try:
result = client.deploy_application(api_uuid)
deploy_uuid = _extract_deploy_uuid(result)
if deploy_uuid:
print(f" Deploy queued: {deploy_uuid[:12]}")
r = _wait_deployment(client, deploy_uuid, timeout=300)
resp = httpx.patch(
f"{client.base_url}/api/v1/applications/{app_uuid}",
headers=client.headers,
json={"build_pack": "dockercompose"},
timeout=30,
)
if resp.status_code == 200:
print(" Build pack set to dockercompose")
steps.append(("Configure compose", StepResult(True, "dockercompose build pack set")))
else:
r = StepResult(True, "Deploy triggered (no UUID)")
steps.append(("Configure compose", StepResult(False, f"{resp.status_code}: {resp.text[:200]}")))
print_summary(steps)
return 1
except Exception as e:
steps.append(("Configure compose", StepResult(False, str(e))))
print_summary(steps)
return 1
_print_result(steps[-1][1])
# Step 3: Set environment variables (domain, admin credentials, secrets)
print("\n[3/4] Setting environment variables...")
envs = [
{"key": "POSTGRES_USER", "value": os.environ.get("DB_USER", "crm_user")},
{"key": "POSTGRES_DB", "value": DB_NAME},
{"key": "DB_PASSWORD", "value": DB_PASSWORD},
{"key": "REDIS_PASSWORD", "value": REDIS_PASSWORD},
{"key": "SECRET_KEY", "value": SECRET_KEY},
{"key": "ENVIRONMENT", "value": os.environ.get("ENVIRONMENT", "production")},
{"key": "LOG_LEVEL", "value": os.environ.get("LOG_LEVEL", "INFO")},
{"key": "SESSION_COOKIE_SECURE", "value": "true"},
{"key": "STORAGE_PATH", "value": os.environ.get("STORAGE_PATH", "/data/storage")},
{"key": "CORS_ORIGINS", "value": APP_DOMAIN},
{"key": "FRONTEND_URL", "value": APP_DOMAIN},
{"key": "ADMIN_EMAIL", "value": os.environ.get("ADMIN_EMAIL", "admin@media-on.de")},
{"key": "ADMIN_PASSWORD", "value": os.environ.get("ADMIN_PASSWORD", "Admin123!")},
]
try:
resp = httpx.patch(
f"{client.base_url}/api/v1/applications/{app_uuid}/envs/bulk",
headers=client.headers,
json={"data": envs},
timeout=30,
)
if resp.status_code in (200, 201):
print(f" {len(envs)} environment variables set")
steps.append(("Set envs", StepResult(True, f"{len(envs)} envs set")))
else:
steps.append(("Set envs", StepResult(False, f"{resp.status_code}: {resp.text[:200]}")))
except Exception as e:
steps.append(("Set envs", StepResult(False, str(e))))
_print_result(steps[-1][1])
# Step 4: Deploy via /api/v1/deploy (works for dockercompose build_pack)
print("\n[4/4] Deploying docker-compose stack...")
try:
resp = httpx.post(
f"{client.base_url}/api/v1/deploy",
headers=client.headers,
json={"uuid": app_uuid},
timeout=30,
)
if resp.status_code == 200:
deploy_data = resp.json()
deployments = deploy_data.get("deployments", [])
if deployments:
deploy_uuid = deployments[0].get("deployment_uuid", "")
print(f" Deploy queued: {deploy_uuid[:12]}")
r = _wait_deployment(client, deploy_uuid, timeout=600)
else:
r = StepResult(True, "Deploy triggered (no UUID)")
else:
r = StepResult(False, f"{resp.status_code}: {resp.text[:200]}")
except Exception as e:
r = StepResult(False, f"Deploy failed: {e}")
steps.append(("API deploy", r))
steps.append(("Deploy", r))
_print_result(r)
if not r.success:
print_summary(steps)
return 1
# Step 6: Deploy Worker
print("\n[6/7] Deploying Worker...")
tag_code, tag_output = ssh_run(
f'docker images --format "{{{{.Repository}}}}:{{{{.Tag}}}}" | '
f'grep "^{api_uuid}:" | grep -v latest | head -1 | '
f'xargs -I{{}} docker tag {{}} {api_uuid}:latest'
)
result = client.deploy_application(worker_uuid)
deploy_uuid = _extract_deploy_uuid(result)
if deploy_uuid:
dep_result = _wait_deployment(client, deploy_uuid, timeout=120)
steps.append(("Worker deploy", dep_result))
else:
wr = _wait_service_healthy(client, worker_uuid, timeout=120)
steps.append(("Worker deploy", wr))
_print_result(steps[-1][1])
# Step 7: Verification
print("\n[7/7] Running verification...")
verify_results = run_verification(client, worker_uuid=worker_uuid)
# Verification
print("\n[5/5] Running verification...")
verify_results = run_verification(client, worker_uuid=None)
steps.extend(verify_results)
all_ok = print_summary(steps)