From 9b58b448fdd790293ff97a111b92611d0ef7a581 Mon Sep 17 00:00:00 2001 From: mignot Date: Thu, 8 Oct 2026 01:38:07 +0200 Subject: [PATCH 1/3] feat(trigger-coolify-deploy): cancel stale deploys and alert on failure Co-Authored-By: Claude Haiku 4.5 --- .../actions/trigger-coolify-deploy/action.yml | 49 +++++++++++++++++++ .../tests/deployment_uuid_format.sh | 2 +- .../tests/stale_deployment_filter.sh | 33 +++++++++++++ .github/workflows/test-actions.yml | 3 ++ .gitignore | 3 +- 5 files changed, 88 insertions(+), 2 deletions(-) create mode 100755 .github/actions/trigger-coolify-deploy/tests/stale_deployment_filter.sh diff --git a/.github/actions/trigger-coolify-deploy/action.yml b/.github/actions/trigger-coolify-deploy/action.yml index 85e161c..d988f5e 100644 --- a/.github/actions/trigger-coolify-deploy/action.yml +++ b/.github/actions/trigger-coolify-deploy/action.yml @@ -93,6 +93,12 @@ inputs: pr_number, server_host, server_deploy_username, and server_deploy_ssh_private_key. required: false default: "false" + discord_webhook_url: + description: >- + Discord webhook to alert when this action fails (trigger error, deploy failure, or + timeout). A failed deploy step is otherwise only a red run on GitHub — nothing pages. + required: false + default: "" runs: using: composite @@ -248,6 +254,34 @@ runs: exit 0 fi + # A deployment whose Coolify job died mid-run stays queued/in_progress forever, and + # Coolify runs one deployment per app at a time — so every later trigger is silently + # dropped and polling it 404s until timeout. Anything older than our own poll timeout + # is one no caller is still waiting on: cancel it so this deploy can run. + set +e + stale=$(curl -sf -H "Authorization: Bearer ${COOLIFY_API_TOKEN}" \ + "${COOLIFY_API_URL}/api/v1/deployments/applications/${app_uuid}?take=20" | jq -c \ + --arg pr "${PR_NUMBER:-0}" --argjson max_age "$POLL_TIMEOUT_SECONDS" \ + '.deployments[]? | select((.pull_request_id | tostring) == $pr and (.status == "queued" or .status == "in_progress") and (now - (.created_at | sub("\\.[0-9]+"; "") | sub(" "; "T") | sub("Z?$"; "Z") | fromdateiso8601)) > $max_age)') + stale_query_status=$? + set -e + if [ "$stale_query_status" -ne 0 ]; then + echo " WARNING: failed to query deployments for app ${app_uuid} (exit ${stale_query_status}) — cannot detect stale deployments." >&2 + stale="" + fi + while IFS= read -r deployment; do + [ -z "$deployment" ] && continue + stale_uuid=$(printf '%s' "$deployment" | jq -r '.deployment_uuid') + if ! [[ "$stale_uuid" =~ ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}|[0-9a-z]{20,30})$ ]]; then + echo " WARNING: unexpected stale deployment_uuid format '${stale_uuid}', skipping" + continue + fi + echo "::warning::Cancelling stale Coolify deployment ${stale_uuid} for '${APP_NAME}' (${COOLIFY_ENVIRONMENT}): $(printf '%s' "$deployment" | jq -r '.status') since $(printf '%s' "$deployment" | jq -r '.created_at')" + curl -sf -o /dev/null -X POST -H "Authorization: Bearer ${COOLIFY_API_TOKEN}" \ + "${COOLIFY_API_URL}/api/v1/deployments/${stale_uuid}/cancel" \ + || echo " WARNING: failed to cancel stale deployment ${stale_uuid}" >&2 + done <<< "$stale" + deploy_url="${COOLIFY_API_URL}/api/v1/deploy?uuid=${app_uuid}&force=${FORCE}" if [ -n "$PR_NUMBER" ]; then deploy_url="${deploy_url}&pr=${PR_NUMBER}" @@ -423,3 +457,18 @@ runs: fi echo "Done — '${APP_NAME}' (${COOLIFY_ENVIRONMENT}) deployed${HEALTH_CHECK_PATH:+ and healthy}." + + - name: Alert Discord on failure + if: failure() && inputs.discord_webhook_url != '' + shell: bash + env: + DISCORD_WEBHOOK_URL: ${{ inputs.discord_webhook_url }} + APP_NAME: ${{ inputs.app_name }} + COOLIFY_ENVIRONMENT: ${{ inputs.coolify_environment }} + RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} + run: | + jq -n --arg app "$APP_NAME" --arg env "$COOLIFY_ENVIRONMENT" --arg repo "$GITHUB_REPOSITORY" \ + --arg ref "$GITHUB_REF_NAME" --arg sha "${GITHUB_SHA:0:7}" --arg url "$RUN_URL" \ + '{embeds: [{title: "Coolify deploy failed: \($app) (\($env))", url: $url, color: 15158332, + description: "\($repo)@\($ref) (\($sha)) — see the run for details."}]}' \ + | curl -sf -o /dev/null -X POST -H "Content-Type: application/json" --data @- "$DISCORD_WEBHOOK_URL" diff --git a/.github/actions/trigger-coolify-deploy/tests/deployment_uuid_format.sh b/.github/actions/trigger-coolify-deploy/tests/deployment_uuid_format.sh index f4ee2fd..7d94609 100755 --- a/.github/actions/trigger-coolify-deploy/tests/deployment_uuid_format.sh +++ b/.github/actions/trigger-coolify-deploy/tests/deployment_uuid_format.sh @@ -1,6 +1,6 @@ #!/usr/bin/env bash # Regression test for the deployment_uuid/phantom_uuid format check in action.yml -# (cancel_phantom_builds). Keeps this in sync with the regex at that call site — +# (cancel_phantom_builds and the stale-deployment cancel). Keeps this in sync with the regex at those call sites — # if you change one, change both. set -euo pipefail diff --git a/.github/actions/trigger-coolify-deploy/tests/stale_deployment_filter.sh b/.github/actions/trigger-coolify-deploy/tests/stale_deployment_filter.sh new file mode 100755 index 0000000..0b36e4a --- /dev/null +++ b/.github/actions/trigger-coolify-deploy/tests/stale_deployment_filter.sh @@ -0,0 +1,33 @@ +#!/usr/bin/env bash +# Regression test for the stale-deployment jq filter in action.yml ("Trigger deploy and wait"). +# Keep FILTER in sync with that call site — if you change one, change both. +set -euo pipefail + +FILTER='.deployments[]? | select((.pull_request_id | tostring) == $pr and (.status == "queued" or .status == "in_progress") and (now - (.created_at | sub("\\.[0-9]+"; "") | sub(" "; "T") | sub("Z?$"; "Z") | fromdateiso8601)) > $max_age)' + +recent=$(date -u -v-60S +%Y-%m-%dT%H:%M:%SZ 2>/dev/null || date -u -d '-60 seconds' +%Y-%m-%dT%H:%M:%SZ) +deployments=$(cat < Date: Thu, 8 Oct 2026 01:40:30 +0200 Subject: [PATCH 2/3] docs(trigger-coolify-deploy): document stale cancel and discord alert Co-Authored-By: Claude Opus 5.5 --- CHANGELOG.md | 9 +++++++++ README.md | 3 ++- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c3b9432..76f446c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -52,6 +52,15 @@ All contributors (including maintainers) should update `CHANGELOG.md` when creat ## [Unreleased] +### Added + +- **trigger-coolify-deploy**: before triggering, cancels the app's deployments that have been queued/in-progress longer than `poll_timeout_seconds`, so a hung Coolify job no longer silently blocks every later deploy +- **trigger-coolify-deploy**: optional `discord_webhook_url` input posts a Discord alert when the action fails + +### CI + +- **test-actions**: added a regression test for the stale-deployment jq filter + ## [4.3.4] - 2026-09-15 ### Fixed diff --git a/README.md b/README.md index 8f88a27..1a208bc 100644 --- a/README.md +++ b/README.md @@ -106,12 +106,13 @@ App-agnostic composite action: trigger a Coolify application deploy via API, pol | `coolify_api_token` | Yes | Coolify API token (composite actions have no `secrets:` block — pass via `with:`) | | `pr_number` | No | PR number to target a PR-preview deployment instead of the regular one | | `force` | No | Force a fresh deploy even if the commit is already deployed. Default `false` | -| `poll_timeout_seconds` | No | Max seconds to wait for the deployment (and health check, if any) to finish. Default `900` | +| `poll_timeout_seconds` | No | Max seconds to wait for the deployment (and health check, if any) to finish; deployments older than this are cancelled as stale before triggering. Default `1800` | | `poll_interval_seconds` | No | Seconds between deployment/health-check polls. Default `15` | | `preview_not_found_timeout_seconds`| No | Max seconds to retry a PR-preview deploy trigger while Coolify hasn't created the preview yet. Default `180` | | `health_check_path` | No | Path (e.g. `/health`) to poll for HTTP 200 after the deployment finishes | | `health_check_base_url_override` | No | Base URL to health-check instead of the app's own fqdn. Required when both `pr_number` and `health_check_path` are set (Coolify's API doesn't expose a preview's fqdn) | | `health_check_origin_ip` | No | Origin server IP to `curl --resolve` the health check directly to, bypassing any reverse proxy in front of the hostname. Needed for Cloudflare-proxied hostnames: Bot Fight Mode 403s GitHub Actions runner IPs on any proxied hostname and runs outside the Ruleset Engine, so no Custom Rule can exempt it | +| `discord_webhook_url` | No | Discord webhook to alert when the action fails (trigger error, deploy failure, or poll timeout) | ```yaml - name: Trigger tmd-admin-api deploy From cda0f6a253ab77a537fc5948363503ab36e1cc96 Mon Sep 17 00:00:00 2001 From: mignot Date: Thu, 8 Oct 2026 01:48:35 +0200 Subject: [PATCH 3/3] fix(trigger-coolify-deploy): harden stale cancel per review Use the active-deployments endpoint instead of the paginated per-app history, a dedicated stale_deployment_after_seconds threshold above the poll timeout, a null-safe timestamp parse, SSH force-stop of stale in-progress builds, and alert on cancelled jobs too. Co-Authored-By: Claude Opus 5.5 --- .../actions/trigger-coolify-deploy/action.yml | 98 +++++++++++++------ .../stale_deployments.jq | 13 +++ .../tests/stale_deployment_filter.sh | 40 +++++--- CHANGELOG.md | 8 +- README.md | 5 +- 5 files changed, 115 insertions(+), 49 deletions(-) create mode 100644 .github/actions/trigger-coolify-deploy/stale_deployments.jq diff --git a/.github/actions/trigger-coolify-deploy/action.yml b/.github/actions/trigger-coolify-deploy/action.yml index d988f5e..1010b10 100644 --- a/.github/actions/trigger-coolify-deploy/action.yml +++ b/.github/actions/trigger-coolify-deploy/action.yml @@ -72,8 +72,10 @@ inputs: VPS host to SSH into to force-stop a still-running native (webhook-triggered) preview build that would otherwise keep consuming a build slot after this action's own deploy has started (Coolify's cancel API only marks an in-progress deployment cancelled in the DB — - it does not stop the build process: coollabsio/coolify#5850). Required whenever pr_number - is set — the action fails fast rather than silently skipping the force-stop. + it does not stop the build process: coollabsio/coolify#5850). Also used to force-stop a + stale in-progress build (see stale_deployment_after_seconds). Required whenever pr_number + is set — the action fails fast rather than silently skipping the force-stop. Optional + otherwise, but setting any of the three SSH inputs requires all three. required: false default: "" server_deploy_username: @@ -93,10 +95,20 @@ inputs: pr_number, server_host, server_deploy_username, and server_deploy_ssh_private_key. required: false default: "false" + stale_deployment_after_seconds: + description: >- + Before triggering, cancel this app's (or this PR preview's) deployments that have been + queued/in-progress longer than this. A Coolify job that dies mid-run leaves its + deployment stuck forever, and Coolify runs one deployment per app, so every later + trigger is silently dropped. Must exceed poll_timeout_seconds of every caller deploying + the same app, or this could cancel a deployment another run is still waiting on. + required: false + default: "3600" discord_webhook_url: description: >- - Discord webhook to alert when this action fails (trigger error, deploy failure, or - timeout). A failed deploy step is otherwise only a red run on GitHub — nothing pages. + Discord webhook to alert when this action fails or its job is cancelled (trigger error, + deploy failure, poll timeout, or the job's own timeout-minutes). A failed deploy step is + otherwise only a red run on GitHub — nothing pages. required: false default: "" @@ -111,7 +123,9 @@ runs: exit 1 - name: Check required build-kill inputs - if: inputs.pr_number != '' || inputs.cancel_native_only == 'true' + if: >- + inputs.pr_number != '' || inputs.cancel_native_only == 'true' || inputs.server_host != '' + || inputs.server_deploy_username != '' || inputs.server_deploy_ssh_private_key != '' shell: bash env: SERVER_HOST: ${{ inputs.server_host }} @@ -123,19 +137,19 @@ runs: [ -z "$SERVER_DEPLOY_USERNAME" ] && missing+=(server_deploy_username) [ -z "$SERVER_DEPLOY_SSH_PRIVATE_KEY" ] && missing+=(server_deploy_ssh_private_key) if [ "${#missing[@]}" -gt 0 ]; then - echo "ERROR: pr_number (or cancel_native_only) is set but missing required input(s): ${missing[*]}." >&2 + echo "ERROR: pr_number, cancel_native_only, or another SSH input is set but missing required input(s): ${missing[*]}." >&2 echo "Cancelling/force-stopping a stuck native build (coollabsio/coolify#5850) requires SSH access to the server — no silent fallback." >&2 exit 1 fi - name: Set up SSH for build-kill fallback - if: inputs.pr_number != '' || inputs.cancel_native_only == 'true' + if: inputs.server_host != '' uses: webfactory/ssh-agent@v0.10.0 with: ssh-private-key: ${{ inputs.server_deploy_ssh_private_key }} - name: Add server to known_hosts - if: inputs.pr_number != '' || inputs.cancel_native_only == 'true' + if: inputs.server_host != '' shell: bash run: | mkdir -p ~/.ssh @@ -152,6 +166,7 @@ runs: PR_NUMBER: ${{ inputs.pr_number }} FORCE: ${{ inputs.force }} POLL_TIMEOUT_SECONDS: ${{ inputs.poll_timeout_seconds }} + STALE_DEPLOYMENT_AFTER_SECONDS: ${{ inputs.stale_deployment_after_seconds }} POLL_INTERVAL_SECONDS: ${{ inputs.poll_interval_seconds }} PREVIEW_NOT_FOUND_TIMEOUT_SECONDS: ${{ inputs.preview_not_found_timeout_seconds }} HEALTH_CHECK_PATH: ${{ inputs.health_check_path }} @@ -165,6 +180,23 @@ runs: COOLIFY_API_URL="https://${COOLIFY_SUBDOMAIN}.${DOMAIN}" + if ! [[ "$STALE_DEPLOYMENT_AFTER_SECONDS" =~ ^[0-9]+$ ]] || [ "$STALE_DEPLOYMENT_AFTER_SECONDS" -le "$POLL_TIMEOUT_SECONDS" ]; then + echo "ERROR: stale_deployment_after_seconds (${STALE_DEPLOYMENT_AFTER_SECONDS}) must be an integer above poll_timeout_seconds (${POLL_TIMEOUT_SECONDS}), or it would cancel deployments still being waited on." >&2 + exit 1 + fi + + # Coolify's cancel endpoint only marks an already-running deployment cancelled + # in the DB — the build process itself keeps running unattended in the + # background (upstream bug: coollabsio/coolify#5850). The only real kill is + # docker stop on the build container, which Coolify names after the + # deployment_uuid (see ApplicationDeploymentJob::graceful_shutdown_container). + force_stop_build() { + echo " Force-stopping in-progress build container $1 via SSH..." + ssh -o StrictHostKeyChecking=accept-new "${SERVER_DEPLOY_USERNAME}@${SERVER_HOST}" \ + "docker stop --time 5 $1" \ + || echo " WARNING: failed to stop $1 (may have already finished)" + } + # Cancels (soft-cancel API + SSH force-stop for in-progress) any native # (webhook-triggered) deployment still queued/in-progress for $PR_NUMBER on $app_uuid, # excluding $1 (our own deployment_uuid, or "" to exclude none — used by cancel_native_only). @@ -202,20 +234,11 @@ runs: curl -s -o /dev/null -X POST -H "Authorization: Bearer ${COOLIFY_API_TOKEN}" \ "${COOLIFY_API_URL}/api/v1/deployments/${phantom_uuid}/cancel" || true - if [ "$phantom_status" != "in_progress" ]; then - continue - fi - # Coolify's cancel endpoint only marks an already-running deployment cancelled - # in the DB — the build process itself keeps running unattended in the - # background (upstream bug: coollabsio/coolify#5850). The only real kill is - # docker stop on the build container, which Coolify names after the - # deployment_uuid (see ApplicationDeploymentJob::graceful_shutdown_container). # SERVER_HOST/SERVER_DEPLOY_USERNAME presence is already enforced by the # "Check required build-kill inputs" step — no fallback here. - echo " Force-stopping in-progress native build container ${phantom_uuid} via SSH..." - ssh -o StrictHostKeyChecking=accept-new "${SERVER_DEPLOY_USERNAME}@${SERVER_HOST}" \ - "docker stop --time 5 ${phantom_uuid}" \ - || echo " WARNING: failed to stop ${phantom_uuid} (may have already finished)" + if [ "$phantom_status" = "in_progress" ]; then + force_stop_build "$phantom_uuid" + fi done <<< "$phantoms" } @@ -256,30 +279,43 @@ runs: # A deployment whose Coolify job died mid-run stays queued/in_progress forever, and # Coolify runs one deployment per app at a time — so every later trigger is silently - # dropped and polling it 404s until timeout. Anything older than our own poll timeout - # is one no caller is still waiting on: cancel it so this deploy can run. + # dropped and polling it 404s until timeout. /api/v1/deployments lists only active + # deployments (unlike the per-app history, which is paginated), and each row's + # deployment_url embeds its application uuid. set +e - stale=$(curl -sf -H "Authorization: Bearer ${COOLIFY_API_TOKEN}" \ - "${COOLIFY_API_URL}/api/v1/deployments/applications/${app_uuid}?take=20" | jq -c \ - --arg pr "${PR_NUMBER:-0}" --argjson max_age "$POLL_TIMEOUT_SECONDS" \ - '.deployments[]? | select((.pull_request_id | tostring) == $pr and (.status == "queued" or .status == "in_progress") and (now - (.created_at | sub("\\.[0-9]+"; "") | sub(" "; "T") | sub("Z?$"; "Z") | fromdateiso8601)) > $max_age)') + stale=$(curl -sf -H "Authorization: Bearer ${COOLIFY_API_TOKEN}" "${COOLIFY_API_URL}/api/v1/deployments" \ + | jq -c --arg app "$app_uuid" --arg pr "${PR_NUMBER:-0}" --argjson max_age "$STALE_DEPLOYMENT_AFTER_SECONDS" \ + -f "${GITHUB_ACTION_PATH}/stale_deployments.jq") stale_query_status=$? set -e if [ "$stale_query_status" -ne 0 ]; then - echo " WARNING: failed to query deployments for app ${app_uuid} (exit ${stale_query_status}) — cannot detect stale deployments." >&2 + echo "::warning::Failed to list active Coolify deployments (exit ${stale_query_status}) — cannot detect stale deployments for '${APP_NAME}'." stale="" fi while IFS= read -r deployment; do [ -z "$deployment" ] && continue stale_uuid=$(printf '%s' "$deployment" | jq -r '.deployment_uuid') + stale_status=$(printf '%s' "$deployment" | jq -r '.status') + stale_created_at=$(printf '%s' "$deployment" | jq -r '.created_at') if ! [[ "$stale_uuid" =~ ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}|[0-9a-z]{20,30})$ ]]; then echo " WARNING: unexpected stale deployment_uuid format '${stale_uuid}', skipping" continue fi - echo "::warning::Cancelling stale Coolify deployment ${stale_uuid} for '${APP_NAME}' (${COOLIFY_ENVIRONMENT}): $(printf '%s' "$deployment" | jq -r '.status') since $(printf '%s' "$deployment" | jq -r '.created_at')" + if [ "$(printf '%s' "$deployment" | jq -r '.age_seconds')" = "null" ]; then + echo "::warning::Active Coolify deployment ${stale_uuid} for '${APP_NAME}' has unparseable created_at '${stale_created_at}' — cannot tell if it is stale, leaving it." + continue + fi + echo "::warning::Cancelling stale Coolify deployment ${stale_uuid} for '${APP_NAME}' (${COOLIFY_ENVIRONMENT}): ${stale_status} since ${stale_created_at}" curl -sf -o /dev/null -X POST -H "Authorization: Bearer ${COOLIFY_API_TOKEN}" \ "${COOLIFY_API_URL}/api/v1/deployments/${stale_uuid}/cancel" \ || echo " WARNING: failed to cancel stale deployment ${stale_uuid}" >&2 + if [ "$stale_status" = "in_progress" ]; then + if [ -n "$SERVER_HOST" ]; then + force_stop_build "$stale_uuid" + else + echo "::warning::Stale build ${stale_uuid} was only marked cancelled — without server_host its build container may still be running (coollabsio/coolify#5850)." + fi + fi done <<< "$stale" deploy_url="${COOLIFY_API_URL}/api/v1/deploy?uuid=${app_uuid}&force=${FORCE}" @@ -459,16 +495,18 @@ runs: echo "Done — '${APP_NAME}' (${COOLIFY_ENVIRONMENT}) deployed${HEALTH_CHECK_PATH:+ and healthy}." - name: Alert Discord on failure - if: failure() && inputs.discord_webhook_url != '' + if: (failure() || cancelled()) && inputs.discord_webhook_url != '' shell: bash env: DISCORD_WEBHOOK_URL: ${{ inputs.discord_webhook_url }} APP_NAME: ${{ inputs.app_name }} COOLIFY_ENVIRONMENT: ${{ inputs.coolify_environment }} + OUTCOME: ${{ job.status == 'cancelled' && 'cancelled' || 'failed' }} RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} run: | jq -n --arg app "$APP_NAME" --arg env "$COOLIFY_ENVIRONMENT" --arg repo "$GITHUB_REPOSITORY" \ --arg ref "$GITHUB_REF_NAME" --arg sha "${GITHUB_SHA:0:7}" --arg url "$RUN_URL" \ - '{embeds: [{title: "Coolify deploy failed: \($app) (\($env))", url: $url, color: 15158332, + --arg outcome "$OUTCOME" \ + '{embeds: [{title: "Coolify deploy \($outcome): \($app) (\($env))", url: $url, color: 15158332, description: "\($repo)@\($ref) (\($sha)) — see the run for details."}]}' \ | curl -sf -o /dev/null -X POST -H "Content-Type: application/json" --data @- "$DISCORD_WEBHOOK_URL" diff --git a/.github/actions/trigger-coolify-deploy/stale_deployments.jq b/.github/actions/trigger-coolify-deploy/stale_deployments.jq new file mode 100644 index 0000000..5e8d1fc --- /dev/null +++ b/.github/actions/trigger-coolify-deploy/stale_deployments.jq @@ -0,0 +1,13 @@ +# Input: GET /api/v1/deployments (active deployments, array or id-keyed object). +# Emits this app's (or this PR preview's) queued/in_progress deployments older than $max_age +# seconds, with age_seconds added — null when created_at can't be parsed, so the caller can +# report it instead of one bad row aborting the whole filter. +def age_seconds: + try (now - (.created_at | sub("\\.[0-9]+"; "") | sub(" "; "T") | sub("(Z|[+-]00:?00)$"; "") | . + "Z" | fromdateiso8601)) + catch null; + +.[]? +| select((.deployment_url // "") | contains("/application/\($app)/")) +| select((.pull_request_id | tostring) == $pr and (.status == "queued" or .status == "in_progress")) +| . + {age_seconds: age_seconds} +| select(.age_seconds == null or .age_seconds > $max_age) diff --git a/.github/actions/trigger-coolify-deploy/tests/stale_deployment_filter.sh b/.github/actions/trigger-coolify-deploy/tests/stale_deployment_filter.sh index 0b36e4a..87ca9bb 100755 --- a/.github/actions/trigger-coolify-deploy/tests/stale_deployment_filter.sh +++ b/.github/actions/trigger-coolify-deploy/tests/stale_deployment_filter.sh @@ -1,25 +1,32 @@ #!/usr/bin/env bash -# Regression test for the stale-deployment jq filter in action.yml ("Trigger deploy and wait"). -# Keep FILTER in sync with that call site — if you change one, change both. +# Regression test for stale_deployments.jq, the stale-deployment filter used by action.yml. set -euo pipefail -FILTER='.deployments[]? | select((.pull_request_id | tostring) == $pr and (.status == "queued" or .status == "in_progress") and (now - (.created_at | sub("\\.[0-9]+"; "") | sub(" "; "T") | sub("Z?$"; "Z") | fromdateiso8601)) > $max_age)' +FILTER="$(dirname "$0")/../stale_deployments.jq" +APP=app0uuid +recent=$(date -u -v-60S +%Y-%m-%dT%H:%M:%S.000000Z 2>/dev/null || date -u -d '-60 seconds' +%Y-%m-%dT%H:%M:%S.000000Z) +url() { printf '/project/p1/environment/e1/application/%s/deployment/x' "$1"; } -recent=$(date -u -v-60S +%Y-%m-%dT%H:%M:%SZ 2>/dev/null || date -u -d '-60 seconds' +%Y-%m-%dT%H:%M:%SZ) +# /api/v1/deployments serializes a sortBy()'d collection, so it can come back as an id-keyed object. deployments=$(cat <