diff --git a/.gitea/workflows/ci-cd.yaml b/.gitea/workflows/ci-cd.yaml index ff215f9..df6bc1b 100644 --- a/.gitea/workflows/ci-cd.yaml +++ b/.gitea/workflows/ci-cd.yaml @@ -65,17 +65,54 @@ jobs: - name: Redeploy stack on Portainer if: github.event_name == 'workflow_dispatch' && inputs.deploy == true env: - PORTAINER_URL: ${{ secrets.PORTAINER_URL }} PORTAINER_TOKEN: ${{ secrets.PORTAINER_TOKEN }} PORTAINER_ENDPOINT_ID: ${{ secrets.PORTAINER_ENDPOINT_ID }} PORTAINER_STACK_ID: ${{ secrets.PORTAINER_STACK_ID }} run: | set -euo pipefail + echo "--- Step 0: auto-discover a reachable Portainer URL ---" + # The runner creates an ephemeral docker network per job, so the + # canonical PORTAINER_URL (e.g. http://192.168.1.30:9000) often isn't + # reachable from inside the job container. Strategy: probe every IP + # we can find (container's own IPs + default gateways) against + # :9000/api/status and use the first one that responds 200. + PORTAINER_URL="" + CANDIDATES=() + + # 1) Container's own IPv4 addresses + for ip in $(hostname -I 2>/dev/null | tr ' ' '\n' | grep -E '^[0-9]+\.'); do + CANDIDATES+=("http://${ip}:9000") + done + + # 2) Default gateways of every default route + while IFS= read -r gw; do + [ -n "$gw" ] && CANDIDATES+=("http://${gw}:9000") + done < <(ip -4 route show default 2>/dev/null | awk '{print $3}' | sort -u) + + # 3) Fallback: secret value (in case everything else fails) + CANDIDATES+=("${{ secrets.PORTAINER_URL }}") + + echo "Candidates: ${CANDIDATES[@]}" + for url in "${CANDIDATES[@]}"; do + code=$(curl -sS -o /dev/null -w '%{http_code}' --max-time 3 "${url}/api/status" 2>/dev/null || echo "000") + echo " probe ${url}/api/status -> ${code}" + if [ "$code" = "200" ]; then + PORTAINER_URL="$url" + echo " -> using ${PORTAINER_URL}" + break + fi + done + + if [ -z "$PORTAINER_URL" ]; then + echo "ERROR: no candidate URL reached Portainer. Tried: ${CANDIDATES[@]}" >&2 + exit 1 + fi + echo "--- Step 1: pre-flight (delete existing stack if present) ---" DELETE_HTTP_CODE=$(curl -sS -o /tmp/portainer-delete.json -w '%{http_code}' \ -X DELETE \ - -H "X-API-Key: ${PORTAINER_TOKEN}" \ + -H "X-API-Key: ${PORT...EN}" \ "${PORTAINER_URL}/api/stacks/${PORTAINER_STACK_ID}?endpointId=${PORTAINER_ENDPOINT_ID}") echo "DELETE HTTP ${DELETE_HTTP_CODE}" if [ "${DELETE_HTTP_CODE}" != "204" ] && [ "${DELETE_HTTP_CODE}" != "404" ]; then @@ -110,10 +147,15 @@ jobs: echo "--- Step 4: smoke-test the freshly deployed stack ---" # Give the container a brief window to start before checking. sleep 8 + # The Portainer host:9000 is reachable from the job container + # (we just proved that with the auto-discovery step above). The + # portfolio container itself is published on host port 3001, so + # smoke-test through the same host. We don't fail the job if the + # proxy upstream isn't reachable from the runner's network. HEALTH=$(curl -sS -o /dev/null -w '%{http_code}' \ --max-time 5 \ - "http://${PORTAINER_URL#http://}:3001/" 2>/dev/null || true) - echo "Health check on http://:3001/ returned: ${HEALTH:-}" + "${PORTAINER_URL%:[0-9]*}:3001/" 2>/dev/null || true) + echo "Health check on ${PORTAINER_URL%:[0-9]*}:3001/ returned: ${HEALTH:-}" # We log but don't fail the job if 3001 isn't reachable — the upstream # proxy (Nginx Proxy Manager → davidaragon.impresion3d.pro) is a better # place to wire a hard-fail check in a future iteration.