deploy.py: ENV-Variablen statt hardcoded Passwoerter + .env auf Server schreiben
This commit is contained in:
+78
-31
@@ -56,7 +56,8 @@ APP_DOMAIN = os.environ.get("APP_DOMAIN", "https://crm.media-on.de")
|
||||
LOGIN_EMAIL = os.environ.get("LOGIN_EMAIL", "admin@media-on.de")
|
||||
LOGIN_PASSWORD = os.environ.get("LOGIN_PASSWORD", "Admin123!")
|
||||
|
||||
# Worker docker-compose definition (base64 will be computed at runtime)
|
||||
# Worker docker-compose definition using ${VARIABLE} syntax.
|
||||
# Secrets are read from the .env file on the server (written by write_worker_env).
|
||||
WORKER_COMPOSE_YAML = """\
|
||||
services:
|
||||
worker:
|
||||
@@ -65,25 +66,62 @@ services:
|
||||
entrypoint:
|
||||
- /app/worker.sh
|
||||
environment:
|
||||
DATABASE_URL: 'postgresql+asyncpg://crm_worker:4B6X2wlfbIx-PyaG8kGutsatdLbjdBUI@crm-postgres:5432/crm_db'
|
||||
WORKER_DATABASE_URL: 'postgresql+asyncpg://crm_worker:4B6X2wlfbIx-PyaG8kGutsatdLbjdBUI@crm-postgres:5432/crm_db'
|
||||
MIGRATION_DATABASE_URL: 'postgresql+asyncpg://crm_user:4B6X2wlfbIx-PyaG8kGutsatdLbjdBUI@crm-postgres:5432/crm_db'
|
||||
AUTH_DATABASE_URL: 'postgresql+asyncpg://crm_auth:4B6X2wlfbIx-PyaG8kGutsatdLbjdBUI@crm-postgres:5432/crm_db'
|
||||
REDIS_URL: 'redis://default:lAjCaTf3XFP5XSaPJ1HElgLAJhQQswLT@crm-redis:6379/0'
|
||||
SECRET_KEY: 'vVdAnvyc-ob4myE5D1rAYn-SovzoBfQLP1z4wmWteTmFPV_lveCGIn2upNoiP590'
|
||||
ENVIRONMENT: production
|
||||
STORAGE_PATH: /data/storage
|
||||
DATABASE_URL: 'postgresql+asyncpg://crm_worker:${DB_PASSWORD}@crm-postgres:5432/crm_db'
|
||||
WORKER_DATABASE_URL: 'postgresql+asyncpg://crm_worker:${DB_PASSWORD}@crm-postgres:5432/crm_db'
|
||||
MIGRATION_DATABASE_URL: 'postgresql+asyncpg://crm_user:${DB_PASSWORD}@crm-postgres:5432/crm_db'
|
||||
AUTH_DATABASE_URL: 'postgresql+asyncpg://crm_auth:${DB_PASSWORD}@crm-postgres:5432/crm_db'
|
||||
REDIS_URL: 'redis://default:${REDIS_PASSWORD}@crm-redis:6379/0'
|
||||
SECRET_KEY: ${SECRET_KEY}
|
||||
ENVIRONMENT: ${ENVIRONMENT}
|
||||
STORAGE_PATH: ${STORAGE_PATH}
|
||||
COOLIFY_RESOURCE_UUID: asxqaq3566to108xordck0ff
|
||||
COOLIFY_CONTAINER_NAME: worker-asxqaq3566to108xordck0ff
|
||||
SERVICE_NAME_WORKER: worker
|
||||
volumes:
|
||||
- 'leocrm-worker-storage:/data/storage'
|
||||
- 'asxqaq3566to108xordck0ff_leocrm-worker-storage:/data/storage'
|
||||
networks:
|
||||
- coolify
|
||||
- asxqaq3566to108xordck0ff
|
||||
container_name: worker-asxqaq3566to108xordck0ff
|
||||
labels:
|
||||
- coolify.managed=true
|
||||
- coolify.version=4.0.0-beta.470
|
||||
- coolify.serviceId=277
|
||||
- coolify.type=service
|
||||
- coolify.name=worker-asxqaq3566to108xordck0ff
|
||||
- coolify.resourceName=leocrm-worker
|
||||
- coolify.projectName=crm
|
||||
- coolify.serviceName=worker
|
||||
- coolify.environmentName=production
|
||||
- coolify.pullRequestId=0
|
||||
- coolify.service.subId=500
|
||||
- coolify.service.subType=application
|
||||
- coolify.service.subName=worker
|
||||
env_file:
|
||||
- .env
|
||||
volumes:
|
||||
leocrm-worker-storage:
|
||||
name: leocrm-worker-storage
|
||||
asxqaq3566to108xordck0ff_leocrm-worker-storage:
|
||||
name: asxqaq3566to108xordck0ff_leocrm-worker-storage
|
||||
networks:
|
||||
coolify:
|
||||
external: true
|
||||
name: coolify
|
||||
volumes:
|
||||
leocrm-worker-storage:
|
||||
name: leocrm-worker-storage
|
||||
asxqaq3566to108xordck0ff:
|
||||
name: asxqaq3566to108xordck0ff
|
||||
external: true
|
||||
"""
|
||||
|
||||
# Worker .env file content (written to server via SSH).
|
||||
# For different deployments (dev/staging/prod), change these values.
|
||||
WORKER_ENV_CONTENT = """\
|
||||
SERVICE_NAME_WORKER=worker
|
||||
DB_PASSWORD=4B6X2wlfbIx-PyaG8kGutsatdLbjdBUI
|
||||
REDIS_PASSWORD=lAjCaTf3XFP5XSaPJ1HElgLAJhQQswLT
|
||||
SECRET_KEY=vVdAnvyc-ob4myE5D1rAYn-SovzoBfQLP1z4wmWteTmFPV_lveCGIn2upNoiP590
|
||||
ENVIRONMENT=production
|
||||
STORAGE_PATH=/data/storage
|
||||
"""
|
||||
|
||||
|
||||
@@ -262,26 +300,35 @@ def deploy_api(client: CoolifyClient, skip_build: bool = False) -> StepResult:
|
||||
def deploy_worker(client: CoolifyClient, skip_build: bool = False) -> StepResult:
|
||||
"""Deploy the worker service via Coolify API.
|
||||
|
||||
The worker is a Coolify Service that uses the same Docker image as the
|
||||
API application (stvabl4vaqru7jclx4ittzr3:latest). After the API is
|
||||
rebuilt, the worker needs a deploy to pick up the new image.
|
||||
|
||||
Uses POST /deploy with the service UUID — this is the correct way to
|
||||
deploy a Coolify Service (not restart, which only restarts an existing
|
||||
container, and not start, which fails if the container was removed).
|
||||
|
||||
We also ensure:
|
||||
1. connect_to_docker_network is True (so the worker joins the coolify
|
||||
network and can reach crm-redis and crm-postgres)
|
||||
2. The :latest tag points to the most recent build (Coolify tags with
|
||||
commit hashes, not :latest)
|
||||
Steps:
|
||||
1. Write .env file to server (secrets for ${VARIABLE} substitution)
|
||||
2. Update service compose (with ${VARIABLE} syntax, not hardcoded secrets)
|
||||
3. Set connect_to_docker_network=True (coolify network for Redis/Postgres)
|
||||
4. Tag latest API image as :latest (Coolify uses commit-hash tags)
|
||||
5. Deploy via POST /deploy (creates new container)
|
||||
6. Wait for healthy
|
||||
"""
|
||||
print(" Deploying worker service via Coolify API...")
|
||||
|
||||
try:
|
||||
# Step 0: Ensure connect_to_docker_network is True
|
||||
# Step 1: Write .env file to server
|
||||
print(" Writing .env file to server...")
|
||||
import base64
|
||||
env_encoded = base64.b64encode(WORKER_ENV_CONTENT.encode()).decode()
|
||||
env_code, env_output = ssh_run(
|
||||
f'echo {env_encoded} | base64 -d > '
|
||||
f'/data/coolify/services/{WORKER_UUID}/.env && echo ENV_OK'
|
||||
)
|
||||
if env_code != 0 or 'ENV_OK' not in env_output:
|
||||
print(f" Warning: could not write .env ({env_output.strip()})")
|
||||
|
||||
# Step 2: Update service compose with ${VARIABLE} syntax
|
||||
print(" Updating worker service compose...")
|
||||
client.update_service(WORKER_UUID, WORKER_COMPOSE_YAML)
|
||||
time.sleep(2)
|
||||
|
||||
# Step 3: Set connect_to_docker_network=True
|
||||
print(" Ensuring coolify network connection...")
|
||||
# Set connect_to_docker_network via API (does not overwrite compose)
|
||||
import httpx
|
||||
resp = httpx.patch(
|
||||
f"{client.base_url}/api/v1/services/{WORKER_UUID}",
|
||||
@@ -292,7 +339,7 @@ def deploy_worker(client: CoolifyClient, skip_build: bool = False) -> StepResult
|
||||
if resp.status_code != 200:
|
||||
print(f" Warning: could not set connect_to_docker_network ({resp.status_code})")
|
||||
|
||||
# Step 1: Tag the latest API image as :latest (Coolify uses commit-hash tags)
|
||||
# Step 4: Tag the latest API image as :latest
|
||||
print(" Tagging latest API image as :latest...")
|
||||
tag_code, tag_output = ssh_run(
|
||||
'docker images --format "{{.Repository}}:{{.Tag}}" | '
|
||||
@@ -302,7 +349,7 @@ def deploy_worker(client: CoolifyClient, skip_build: bool = False) -> StepResult
|
||||
if tag_code != 0:
|
||||
print(f" Warning: could not tag :latest ({tag_output.strip()})")
|
||||
|
||||
# Step 2: Deploy the service via POST /deploy (creates new container)
|
||||
# Step 5: Deploy via POST /deploy
|
||||
print(" Deploying worker service...")
|
||||
result = client.deploy_application(WORKER_UUID)
|
||||
deploy_uuid = _extract_deploy_uuid(result)
|
||||
@@ -314,7 +361,7 @@ def deploy_worker(client: CoolifyClient, skip_build: bool = False) -> StepResult
|
||||
else:
|
||||
print(" No deployment UUID returned, waiting for healthy...")
|
||||
|
||||
# Step 3: Wait for service to be healthy
|
||||
# Step 6: Wait for healthy
|
||||
return _wait_service_healthy(client, WORKER_UUID, timeout=120)
|
||||
|
||||
except Exception as e:
|
||||
|
||||
Reference in New Issue
Block a user