fix: deploy.py --initial als einzelner docker-compose Stack + docker-compose.yaml rename
This commit is contained in:
+129
-116
@@ -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:
|
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
|
# Set FQDN via PATCH
|
||||||
if APP_DOMAIN:
|
if APP_DOMAIN:
|
||||||
print(f" Setting FQDN to {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:
|
except Exception as e:
|
||||||
print(f" Warning: could not set FQDN: {e}")
|
print(f" Warning: could not set FQDN: {e}")
|
||||||
|
|
||||||
if skip_build:
|
print(" Triggering Coolify deploy via /api/v1/deploy...")
|
||||||
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...")
|
|
||||||
try:
|
try:
|
||||||
result = client.deploy_application(app_uuid)
|
resp = httpx.post(
|
||||||
deploy_uuid = _extract_deploy_uuid(result)
|
f"{client.base_url}/api/v1/deploy",
|
||||||
if not deploy_uuid:
|
headers=client.headers,
|
||||||
return StepResult(False, "No deployment UUID returned from Coolify")
|
json={"uuid": app_uuid},
|
||||||
print(f" Deploy queued: {deploy_uuid[:12]}")
|
timeout=30,
|
||||||
return _wait_deployment(client, deploy_uuid, timeout=300)
|
)
|
||||||
|
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:
|
except Exception as e:
|
||||||
return StepResult(False, f"Deploy trigger failed: {e}")
|
return StepResult(False, f"Deploy trigger failed: {e}")
|
||||||
|
|
||||||
@@ -883,6 +882,7 @@ POSTGRES_COMPOSE = (
|
|||||||
"services:\n"
|
"services:\n"
|
||||||
" postgres:\n"
|
" postgres:\n"
|
||||||
" image: pgvector/pgvector:pg16\n"
|
" image: pgvector/pgvector:pg16\n"
|
||||||
|
" container_name: crm-postgres\n"
|
||||||
" restart: unless-stopped\n"
|
" restart: unless-stopped\n"
|
||||||
" environment:\n"
|
" environment:\n"
|
||||||
" POSTGRES_USER: ${DB_USER}\n"
|
" POSTGRES_USER: ${DB_USER}\n"
|
||||||
@@ -906,6 +906,7 @@ REDIS_COMPOSE = (
|
|||||||
"services:\n"
|
"services:\n"
|
||||||
" redis:\n"
|
" redis:\n"
|
||||||
" image: redis:7-alpine\n"
|
" image: redis:7-alpine\n"
|
||||||
|
" container_name: crm-redis\n"
|
||||||
" restart: unless-stopped\n"
|
" restart: unless-stopped\n"
|
||||||
" command: redis-server --requirepass ${REDIS_PASSWORD}\n"
|
" command: redis-server --requirepass ${REDIS_PASSWORD}\n"
|
||||||
" volumes:\n"
|
" volumes:\n"
|
||||||
@@ -1084,119 +1085,131 @@ def deploy_initial() -> int:
|
|||||||
environment_name = os.environ.get("COOLIFY_ENVIRONMENT", "production")
|
environment_name = os.environ.get("COOLIFY_ENVIRONMENT", "production")
|
||||||
server_uuid = os.environ.get("COOLIFY_SERVER_UUID", "")
|
server_uuid = os.environ.get("COOLIFY_SERVER_UUID", "")
|
||||||
|
|
||||||
# Step 1: Create PostgreSQL service
|
# ─── Single docker-compose application (like the original working app) ──
|
||||||
print("\n[1/7] Creating PostgreSQL service...")
|
# All 4 containers (postgres, redis, crm-app, crm-worker) in one stack.
|
||||||
pg_uuid = create_service(client, project_uuid, environment_name, server_uuid,
|
# Coolify reads docker-compose.yaml from the Git repo, builds images, all
|
||||||
POSTGRES_COMPOSE, "crm-postgres")
|
# containers share one network. Service names work as DNS names. No manual
|
||||||
if pg_uuid:
|
# network configuration needed. No cryptic container names.
|
||||||
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])
|
|
||||||
|
|
||||||
# Step 2: Create Redis service
|
# Step 1: Create application via private-deploy-key (with Git repo)
|
||||||
print("\n[2/7] Creating Redis service...")
|
print("\n[1/4] Creating docker-compose application...")
|
||||||
redis_uuid = create_service(client, project_uuid, environment_name, server_uuid,
|
try:
|
||||||
REDIS_COMPOSE, "crm-redis")
|
resp = httpx.post(
|
||||||
if redis_uuid:
|
f"{client.base_url}/api/v1/applications/private-deploy-key",
|
||||||
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}",
|
|
||||||
headers=client.headers,
|
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,
|
timeout=30,
|
||||||
)
|
)
|
||||||
set_service_envs(client, worker_uuid, get_worker_envs())
|
if resp.status_code == 201:
|
||||||
steps.append(("Worker service", StepResult(True, f"Created: {worker_uuid[:12]}")))
|
app_uuid = resp.json().get("uuid")
|
||||||
else:
|
print(f" Application created: {app_uuid[:12]}")
|
||||||
steps.append(("Worker service", StepResult(False, "Failed to create")))
|
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)
|
print_summary(steps)
|
||||||
return 1
|
return 1
|
||||||
_print_result(steps[-1][1])
|
_print_result(steps[-1][1])
|
||||||
|
|
||||||
# Step 5: Deploy API (builds image + runs migrations via prestart.sh)
|
# Step 2: PATCH to dockercompose build_pack (Coolify reads docker-compose.yaml from Git)
|
||||||
print("\n[5/7] Deploying API (build + migrations)...")
|
print("\n[2/4] Configuring docker-compose build pack...")
|
||||||
try:
|
try:
|
||||||
result = client.deploy_application(api_uuid)
|
resp = httpx.patch(
|
||||||
deploy_uuid = _extract_deploy_uuid(result)
|
f"{client.base_url}/api/v1/applications/{app_uuid}",
|
||||||
if deploy_uuid:
|
headers=client.headers,
|
||||||
print(f" Deploy queued: {deploy_uuid[:12]}")
|
json={"build_pack": "dockercompose"},
|
||||||
r = _wait_deployment(client, deploy_uuid, timeout=300)
|
timeout=30,
|
||||||
|
)
|
||||||
|
if resp.status_code == 200:
|
||||||
|
print(" Build pack set to dockercompose")
|
||||||
|
steps.append(("Configure compose", StepResult(True, "dockercompose build pack set")))
|
||||||
else:
|
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:
|
except Exception as e:
|
||||||
r = StepResult(False, f"Deploy failed: {e}")
|
r = StepResult(False, f"Deploy failed: {e}")
|
||||||
steps.append(("API deploy", r))
|
steps.append(("Deploy", r))
|
||||||
_print_result(r)
|
_print_result(r)
|
||||||
if not r.success:
|
if not r.success:
|
||||||
print_summary(steps)
|
print_summary(steps)
|
||||||
return 1
|
return 1
|
||||||
|
|
||||||
# Step 6: Deploy Worker
|
# Verification
|
||||||
print("\n[6/7] Deploying Worker...")
|
print("\n[5/5] Running verification...")
|
||||||
tag_code, tag_output = ssh_run(
|
verify_results = run_verification(client, worker_uuid=None)
|
||||||
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)
|
|
||||||
steps.extend(verify_results)
|
steps.extend(verify_results)
|
||||||
|
|
||||||
all_ok = print_summary(steps)
|
all_ok = print_summary(steps)
|
||||||
|
|||||||
Reference in New Issue
Block a user