diff --git a/.gitea/workflows/deploy.yml b/.gitea/workflows/deploy.yml index 8f249b4..23653fd 100644 --- a/.gitea/workflows/deploy.yml +++ b/.gitea/workflows/deploy.yml @@ -153,6 +153,75 @@ jobs: APPLY_HEADERS=$(mktemp) APPLY_ERR=$(mktemp) + # If the stack does not exist yet, remove orphan containers with names defined in compose. + # This enables an idempotent create-or-recreate flow when old standalone containers exist. + if [ -z "${STACK_ID}" ]; then + echo "Stack not found in Portainer; checking for orphan containers with conflicting names" + + mapfile -t CONTAINER_NAMES < <(awk '/container_name:/{print $2}' docker-compose.prod.yml | tr -d '"' | sed '/^$/d') + + for CONTAINER_NAME in "${CONTAINER_NAMES[@]}"; do + FILTERS=$(jq -cn --arg n "^/${CONTAINER_NAME}$" '{name: [$n]}') + FILTERS_URLENC=$(printf '%s' "${FILTERS}" | jq -sRr @uri) + LIST_URL="${ACTIVE_PORTAINER_BASE_URL}/api/endpoints/${PORTAINER_ENDPOINT_ID}/docker/containers/json?all=1&filters=${FILTERS_URLENC}" + + LIST_BODY=$(mktemp) + LIST_ERR=$(mktemp) + LIST_HTTP_CODE=$(curl -sS \ + --noproxy "*" \ + -o "${LIST_BODY}" \ + -w "%{http_code}" \ + "${LIST_URL}" \ + -H "X-API-Key: ${PORTAINER_API_KEY}" \ + 2>"${LIST_ERR}") + LIST_CURL_EXIT=$? + + echo "Container pre-check [${CONTAINER_NAME}] curl=${LIST_CURL_EXIT} http=${LIST_HTTP_CODE}" + + if [ "${LIST_CURL_EXIT}" -ne 0 ]; then + echo "Container pre-check stderr for ${CONTAINER_NAME}:" + cat "${LIST_ERR}" || true + continue + fi + + if [ "${LIST_HTTP_CODE}" -lt 200 ] || [ "${LIST_HTTP_CODE}" -ge 300 ]; then + echo "Container pre-check non-success response for ${CONTAINER_NAME}:" + cat "${LIST_BODY}" || true + continue + fi + + mapfile -t MATCHING_IDS < <(jq -r '.[].Id' "${LIST_BODY}") + if [ "${#MATCHING_IDS[@]}" -eq 0 ]; then + echo "No conflicting container found for ${CONTAINER_NAME}" + continue + fi + + for CONTAINER_ID in "${MATCHING_IDS[@]}"; do + DELETE_URL="${ACTIVE_PORTAINER_BASE_URL}/api/endpoints/${PORTAINER_ENDPOINT_ID}/docker/containers/${CONTAINER_ID}?force=1" + DELETE_BODY=$(mktemp) + DELETE_ERR=$(mktemp) + DELETE_HTTP_CODE=$(curl -sS -X DELETE \ + --noproxy "*" \ + -o "${DELETE_BODY}" \ + -w "%{http_code}" \ + "${DELETE_URL}" \ + -H "X-API-Key: ${PORTAINER_API_KEY}" \ + 2>"${DELETE_ERR}") + DELETE_CURL_EXIT=$? + + echo "Removed conflicting container ${CONTAINER_NAME} (${CONTAINER_ID}) curl=${DELETE_CURL_EXIT} http=${DELETE_HTTP_CODE}" + if [ "${DELETE_CURL_EXIT}" -ne 0 ]; then + echo "Delete stderr:" + cat "${DELETE_ERR}" || true + fi + if [ "${DELETE_HTTP_CODE}" -lt 200 ] || [ "${DELETE_HTTP_CODE}" -ge 300 ]; then + echo "Delete response body:" + cat "${DELETE_BODY}" || true + fi + done + done + fi + 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}"