From ff93afd075c3ebd47d398caa5856227acb78b336 Mon Sep 17 00:00:00 2001 From: Gabriel Sancho Date: Sat, 28 Mar 2026 14:54:22 -0300 Subject: [PATCH] feat: add debug information and response headers to Portainer stack deployment --- .gitea/workflows/deploy.yml | 44 +++++++++++++++++++++++++++++++++++-- 1 file changed, 42 insertions(+), 2 deletions(-) diff --git a/.gitea/workflows/deploy.yml b/.gitea/workflows/deploy.yml index e933375..8f249b4 100644 --- a/.gitea/workflows/deploy.yml +++ b/.gitea/workflows/deploy.yml @@ -75,6 +75,8 @@ jobs: ') echo "Loaded $(printf '%s' "${ENV_JSON}" | jq 'length') env entries from ENV_VARS" + echo "ENV names preview:" + printf '%s' "${ENV_JSON}" | jq -r '.[0:10][]?.name' || true echo "Portainer base URL: ${PORTAINER_BASE_URL}" echo "Target stack: ${STACK_NAME}" echo "Endpoint id set: $([ -n "${PORTAINER_ENDPOINT_ID}" ] && echo yes || echo no)" @@ -97,10 +99,12 @@ jobs: fi STACKS_BODY=$(mktemp) + STACKS_HEADERS=$(mktemp) STACKS_ERR=$(mktemp) STACKS_HTTP_CODE=$(curl -sS \ --noproxy "*" \ + -D "${STACKS_HEADERS}" \ -o "${STACKS_BODY}" \ -w "%{http_code}" \ "${ACTIVE_PORTAINER_BASE_URL}/api/stacks" \ @@ -110,11 +114,14 @@ jobs: echo "GET /api/stacks curl exit: ${STACKS_CURL_EXIT}" echo "GET /api/stacks http code: ${STACKS_HTTP_CODE}" + echo "GET /api/stacks headers:" + cat "${STACKS_HEADERS}" || true if [ "${STACKS_CURL_EXIT}" -eq 6 ] && [ -n "${PORTAINER_IP:-}" ]; then echo "Retrying stack list with IP fallback due to DNS failure" STACKS_HTTP_CODE=$(curl -sS \ --noproxy "*" \ + -D "${STACKS_HEADERS}" \ -o "${STACKS_BODY}" \ -w "%{http_code}" \ "${PORTAINER_IP_BASE_URL}/api/stacks" \ @@ -143,20 +150,27 @@ jobs: STACK_ID=$(jq -r --arg stack_name "${STACK_NAME}" '.[] | select(.Name == $stack_name) | .Id' "${STACKS_BODY}" | head -n 1) APPLY_BODY=$(mktemp) + APPLY_HEADERS=$(mktemp) APPLY_ERR=$(mktemp) if [ -n "${STACK_ID}" ]; then echo "Updating existing stack id=${STACK_ID}" + REQUEST_URL="${ACTIVE_PORTAINER_BASE_URL}/api/stacks/${STACK_ID}?endpointId=${PORTAINER_ENDPOINT_ID}" PAYLOAD=$(jq -n \ --rawfile stack_file docker-compose.prod.yml \ --argjson env_vars "${ENV_JSON}" \ '{StackFileContent: $stack_file, Env: $env_vars, Prune: false, PullImage: true}') + echo "Apply request URL: ${REQUEST_URL}" + echo "Apply payload summary:" + printf '%s' "${PAYLOAD}" | jq -r '{stackFileLength: (.StackFileContent | length), envCount: (.Env | length), prune: .Prune, pullImage: .PullImage}' || true + APPLY_HTTP_CODE=$(curl -sS -X PUT \ --noproxy "*" \ + -D "${APPLY_HEADERS}" \ -o "${APPLY_BODY}" \ -w "%{http_code}" \ - "${ACTIVE_PORTAINER_BASE_URL}/api/stacks/${STACK_ID}?endpointId=${PORTAINER_ENDPOINT_ID}" \ + "${REQUEST_URL}" \ -H "X-API-Key: ${PORTAINER_API_KEY}" \ -H "Content-Type: application/json" \ -d "${PAYLOAD}" \ @@ -164,17 +178,23 @@ jobs: APPLY_CURL_EXIT=$? else echo "Creating new stack ${STACK_NAME}" + REQUEST_URL="${ACTIVE_PORTAINER_BASE_URL}/api/stacks/create/standalone/string?endpointId=${PORTAINER_ENDPOINT_ID}" PAYLOAD=$(jq -n \ --arg name "${STACK_NAME}" \ --rawfile stack_file docker-compose.prod.yml \ --argjson env_vars "${ENV_JSON}" \ '{Name: $name, StackFileContent: $stack_file, Env: $env_vars, FromAppTemplate: false}') + echo "Apply request URL: ${REQUEST_URL}" + echo "Apply payload summary:" + printf '%s' "${PAYLOAD}" | jq -r '{name: .Name, stackFileLength: (.StackFileContent | length), envCount: (.Env | length), fromAppTemplate: .FromAppTemplate}' || true + APPLY_HTTP_CODE=$(curl -sS -X POST \ --noproxy "*" \ + -D "${APPLY_HEADERS}" \ -o "${APPLY_BODY}" \ -w "%{http_code}" \ - "${ACTIVE_PORTAINER_BASE_URL}/api/stacks/create/standalone/string?endpointId=${PORTAINER_ENDPOINT_ID}" \ + "${REQUEST_URL}" \ -H "X-API-Key: ${PORTAINER_API_KEY}" \ -H "Content-Type: application/json" \ -d "${PAYLOAD}" \ @@ -184,6 +204,8 @@ jobs: echo "Apply curl exit: ${APPLY_CURL_EXIT}" echo "Apply http code: ${APPLY_HTTP_CODE}" + echo "Apply response headers:" + cat "${APPLY_HEADERS}" || true if [ "${APPLY_CURL_EXIT}" -ne 0 ]; then echo "Apply stderr:" @@ -194,6 +216,24 @@ jobs: if [ "${APPLY_HTTP_CODE}" -lt 200 ] || [ "${APPLY_HTTP_CODE}" -ge 300 ]; then echo "Apply response body:" cat "${APPLY_BODY}" || true + echo "Apply response parsed as JSON (if possible):" + jq -r '.' "${APPLY_BODY}" 2>/dev/null || echo "" + + if [ ! -s "${APPLY_BODY}" ]; then + echo "Apply body is empty; retrying once with verbose curl for diagnostics" + curl -v -X "$( [ -n "${STACK_ID}" ] && echo PUT || echo POST )" \ + --noproxy "*" \ + -o /tmp/portainer-debug-body.txt \ + "${REQUEST_URL}" \ + -H "X-API-Key: ${PORTAINER_API_KEY}" \ + -H "Content-Type: application/json" \ + -d "${PAYLOAD}" \ + 2>/tmp/portainer-debug-stderr.txt || true + echo "Verbose retry stderr:" + cat /tmp/portainer-debug-stderr.txt || true + echo "Verbose retry body:" + cat /tmp/portainer-debug-body.txt || true + fi exit 1 fi