Files
amare/scripts/deploy/dokploy-deploy.sh
Manoel Freitas 1a31dd1cf6 fix: surface Dokploy API errors and normalize panel URL (#8)
curl -f hid 404 bodies; strip accidental /api on DOKPLOY_URL so compose.deploy diagnostics are actionable.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-04 23:14:12 -03:00

115 lines
3.5 KiB
Bash
Executable File

#!/usr/bin/env bash
# Trigger a Dokploy Compose deploy and wait until it finishes.
# Required env:
# DOKPLOY_URL e.g. https://panel.example.com (panel origin, no /api)
# DOKPLOY_API_KEY x-api-key value
# DOKPLOY_COMPOSE_ID target compose id
# Optional env:
# DEPLOY_TITLE deployment title (default: GitHub deploy)
# DEPLOY_TIMEOUT_SEC total wait seconds (default: 900)
# DEPLOY_POLL_SEC poll interval (default: 10)
set -euo pipefail
: "${DOKPLOY_URL:?DOKPLOY_URL is required}"
: "${DOKPLOY_API_KEY:?DOKPLOY_API_KEY is required}"
: "${DOKPLOY_COMPOSE_ID:?DOKPLOY_COMPOSE_ID is required}"
# Panel origin only: strip trailing slash and accidental OpenAPI /api suffix.
DOKPLOY_URL="${DOKPLOY_URL%/}"
DOKPLOY_URL="${DOKPLOY_URL%/api}"
DOKPLOY_URL="${DOKPLOY_URL%/}"
DEPLOY_TITLE="${DEPLOY_TITLE:-GitHub deploy}"
DEPLOY_TIMEOUT_SEC="${DEPLOY_TIMEOUT_SEC:-900}"
DEPLOY_POLL_SEC="${DEPLOY_POLL_SEC:-10}"
echo "Dokploy API base: ${DOKPLOY_URL}/api"
api() {
local method="$1"
local path="$2"
shift 2
local url="${DOKPLOY_URL}/api${path}"
local response http_code body
# Body then HTTP status on the last line (no -f so 4xx/5xx still return a body).
response="$(
curl -sS -w $'\n%{http_code}' -X "$method" \
-H "accept: application/json" \
-H "content-type: application/json" \
-H "x-api-key: ${DOKPLOY_API_KEY}" \
"$url" \
"$@"
)" || {
echo "Dokploy API request failed (curl transport error)." >&2
echo " method: ${method}" >&2
echo " url: ${url}" >&2
exit 1
}
http_code="$(printf '%s' "$response" | tail -n1)"
body="$(printf '%s' "$response" | sed '$d')"
if [[ ! "$http_code" =~ ^2[0-9][0-9]$ ]]; then
echo "Dokploy API error." >&2
echo " method: ${method}" >&2
echo " url: ${url}" >&2
echo " status: ${http_code}" >&2
echo " body:" >&2
printf '%s\n' "$body" >&2
exit 1
fi
printf '%s' "$body"
}
echo "Triggering Dokploy compose.deploy for ${DOKPLOY_COMPOSE_ID}..."
api POST "/compose.deploy" \
-d "$(jq -n \
--arg composeId "$DOKPLOY_COMPOSE_ID" \
--arg title "$DEPLOY_TITLE" \
'{composeId: $composeId, title: $title}')" >/dev/null
echo "Waiting for deployment to finish (timeout ${DEPLOY_TIMEOUT_SEC}s)..."
deadline=$((SECONDS + DEPLOY_TIMEOUT_SEC))
last_status=""
while (( SECONDS < deadline )); do
payload="$(api GET "/deployment.allByCompose?composeId=${DOKPLOY_COMPOSE_ID}")"
latest="$(echo "$payload" | jq -c 'if type=="array" then .[0] else . end')"
if [[ -z "$latest" || "$latest" == "null" ]]; then
echo "No deployment records yet; retrying..."
sleep "$DEPLOY_POLL_SEC"
continue
fi
status="$(echo "$latest" | jq -r '.status // .deploymentStatus // empty')"
created_at="$(echo "$latest" | jq -r '.createdAt // .created_at // empty')"
title="$(echo "$latest" | jq -r '.title // .titleLog // empty')"
if [[ "$status" != "$last_status" ]]; then
echo "Deployment status=${status:-unknown} title=${title:-n/a} createdAt=${created_at:-n/a}"
last_status="$status"
fi
case "${status,,}" in
done|success|successful|finished)
echo "Dokploy deployment succeeded."
exit 0
;;
error|failed|failure)
echo "Dokploy deployment failed." >&2
echo "$latest" | jq . >&2 || true
exit 1
;;
esac
sleep "$DEPLOY_POLL_SEC"
done
echo "Timed out waiting for Dokploy deployment after ${DEPLOY_TIMEOUT_SEC}s." >&2
api GET "/deployment.allByCompose?composeId=${DOKPLOY_COMPOSE_ID}" | jq . >&2 || true
exit 1