Preview reconcile #3
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: Preview reconcile | |
| # `cleanup-preview.yml` deletes a preview when its pull request closes. That request can fail — most | |
| # plausibly because Coolify was unreachable at the time — and nothing afterwards notices, so the | |
| # preview keeps its cloned database and git checkout indefinitely. This re-checks on a schedule. | |
| # | |
| # Deleting an already-deleted preview answers 404, so 404 is the expected result rather than a fault. | |
| on: | |
| schedule: | |
| - cron: "0 4 * * *" | |
| workflow_dispatch: | |
| inputs: | |
| days: | |
| description: "How far back to look for closed pull requests" | |
| type: number | |
| default: 45 | |
| permissions: | |
| contents: read | |
| pull-requests: read | |
| concurrency: | |
| group: preview-reconcile | |
| cancel-in-progress: false | |
| jobs: | |
| reconcile: | |
| name: "Preview / Reconcile orphans" | |
| runs-on: ubuntu-latest | |
| if: vars.COOLIFY_URL != '' && vars.COOLIFY_APP_UUID != '' | |
| timeout-minutes: 20 | |
| env: | |
| COOLIFY_URL: ${{ vars.COOLIFY_URL }} | |
| COOLIFY_APP_UUID: ${{ vars.COOLIFY_APP_UUID }} | |
| COOLIFY_TOKEN: ${{ secrets.COOLIFY_API_TOKEN }} | |
| GH_TOKEN: ${{ github.token }} | |
| DAYS: ${{ inputs.days || 45 }} | |
| steps: | |
| - name: Reclaim previews whose pull request has closed | |
| run: | | |
| set -euo pipefail | |
| if [ -z "${COOLIFY_TOKEN}" ]; then | |
| echo "::notice::COOLIFY_API_TOKEN is not configured; nothing to reconcile against." | |
| exit 0 | |
| fi | |
| since=$(date -u -d "-${DAYS} days" +%Y-%m-%d) | |
| closed=$(gh pr list --repo "${GITHUB_REPOSITORY}" --state closed \ | |
| --search "closed:>=${since}" --limit 500 --json number --jq '.[].number') | |
| # --limit truncates; it is not the window. Passing --search also switches gh to | |
| # relevance ordering, so a truncated page would drop arbitrary pull requests. | |
| if [ "$(printf '%s\n' "${closed}" | grep -c .)" -ge 500 ]; then | |
| echo "::error::More than 500 pull requests closed since ${since}; raise --limit." | |
| exit 1 | |
| fi | |
| reclaimed=0 | |
| checked=0 | |
| failed=0 | |
| summary="${RUNNER_TEMP}/summary.md" | |
| body="${RUNNER_TEMP}/response.json" | |
| { | |
| echo "| PR | Result |" | |
| echo "|---:|---|" | |
| } > "${summary}" | |
| while read -r number; do | |
| [ -n "${number:-}" ] || continue | |
| checked=$((checked + 1)) | |
| status=$(curl -sS -o "${body}" -w '%{http_code}' -X DELETE \ | |
| -H "Authorization: Bearer ${COOLIFY_TOKEN}" \ | |
| -H 'Accept: application/json' \ | |
| --retry 3 --retry-connrefused --max-time 15 \ | |
| "${COOLIFY_URL}/api/v1/applications/${COOLIFY_APP_UUID}/previews/${number}") || status=000 | |
| case "${status}" in | |
| 404) | |
| : # already clean | |
| ;; | |
| 2*) | |
| reclaimed=$((reclaimed + 1)) | |
| echo "| #${number} | reclaimed |" >> "${summary}" | |
| echo "::warning::Reclaimed an orphaned preview for closed PR #${number}; cleanup on close did not reach Coolify." | |
| ;; | |
| *) | |
| # Stop on the first failure: an unreachable Coolify is one finding, not one per | |
| # pull request. The summary below still reports what was reclaimed before it. | |
| failed=1 | |
| echo "| #${number} | HTTP ${status} |" >> "${summary}" | |
| echo "::error::Coolify returned HTTP ${status} for PR #${number}: $(head -c 200 "${body}" | tr '\n' ' ')" | |
| break | |
| ;; | |
| esac | |
| done <<< "${closed}" | |
| { | |
| echo "## Preview reconcile" | |
| echo | |
| echo "Checked **${checked}** pull requests closed in the last ${DAYS} days." | |
| echo | |
| if [ "${reclaimed}" -eq 0 ] && [ "${failed}" -eq 0 ]; then | |
| echo "No orphaned previews." | |
| else | |
| cat "${summary}" | |
| fi | |
| } >> "${GITHUB_STEP_SUMMARY}" | |
| echo "checked=${checked} reclaimed=${reclaimed} failed=${failed}" | |
| [ "${failed}" -eq 0 ] |