mirror of
https://github.com/we-promise/sure.git
synced 2026-05-24 13:04:56 +00:00
217 lines
7.9 KiB
YAML
217 lines
7.9 KiB
YAML
name: Cleanup PR Previews
|
|
|
|
on:
|
|
# Run hourly to check for expired previews
|
|
schedule:
|
|
- cron: '0 * * * *'
|
|
|
|
# Immediately cleanup when PR is closed
|
|
pull_request:
|
|
types: [closed, unlabeled]
|
|
|
|
# Allow manual trigger
|
|
workflow_dispatch:
|
|
inputs:
|
|
pr_number:
|
|
description: 'PR number to cleanup (optional, cleans all expired if empty)'
|
|
required: false
|
|
type: string
|
|
|
|
permissions:
|
|
contents: read
|
|
deployments: write
|
|
|
|
jobs:
|
|
cleanup-on-close:
|
|
name: Cleanup closed PR preview
|
|
if: github.event_name == 'pull_request' && (github.event.action == 'closed' || (github.event.action == 'unlabeled' && github.event.label.name == 'preview-cf'))
|
|
runs-on: ubuntu-latest
|
|
timeout-minutes: 10
|
|
|
|
steps:
|
|
- name: Checkout code
|
|
uses: actions/checkout@v5
|
|
|
|
- name: Setup Node.js
|
|
uses: actions/setup-node@v5
|
|
with:
|
|
node-version: "24"
|
|
|
|
- name: Install Wrangler
|
|
run: npm install -g wrangler
|
|
|
|
- name: Delete preview Worker
|
|
env:
|
|
CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}
|
|
CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
|
|
run: |
|
|
WORKER_NAME="sure-preview-${{ github.event.pull_request.number }}"
|
|
echo "Deleting Worker: $WORKER_NAME"
|
|
|
|
# Delete the worker (this also stops any running containers)
|
|
wrangler delete --name "$WORKER_NAME" --force || echo "Worker may not exist"
|
|
|
|
- name: Delete GitHub Deployment
|
|
uses: actions/github-script@v7
|
|
with:
|
|
script: |
|
|
const environment = `preview-pr-${{ github.event.pull_request.number }}`;
|
|
const description = context.payload.action === 'closed'
|
|
? 'PR closed - preview deleted'
|
|
: 'preview-cf label removed - preview deleted';
|
|
|
|
try {
|
|
// Get deployments for this environment
|
|
const { data: deployments } = await github.rest.repos.listDeployments({
|
|
owner: context.repo.owner,
|
|
repo: context.repo.repo,
|
|
environment: environment
|
|
});
|
|
|
|
// Mark all deployments as inactive
|
|
for (const deployment of deployments) {
|
|
await github.rest.repos.createDeploymentStatus({
|
|
owner: context.repo.owner,
|
|
repo: context.repo.repo,
|
|
deployment_id: deployment.id,
|
|
state: 'inactive',
|
|
description
|
|
});
|
|
}
|
|
|
|
console.log(`Marked ${deployments.length} deployments as inactive`);
|
|
} catch (error) {
|
|
console.log('No deployments to cleanup or error:', error.message);
|
|
}
|
|
|
|
cleanup-expired:
|
|
name: Cleanup expired previews
|
|
if: github.event_name == 'schedule' || github.event_name == 'workflow_dispatch'
|
|
runs-on: ubuntu-latest
|
|
timeout-minutes: 15
|
|
|
|
steps:
|
|
- name: Checkout code
|
|
uses: actions/checkout@v5
|
|
|
|
- name: Setup Node.js
|
|
uses: actions/setup-node@v5
|
|
with:
|
|
node-version: "24"
|
|
|
|
- name: Install Wrangler
|
|
run: npm install -g wrangler
|
|
|
|
- name: Cleanup expired previews
|
|
env:
|
|
CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}
|
|
CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
|
|
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
PR_INPUT: ${{ inputs.pr_number }}
|
|
run: |
|
|
# If specific PR number provided, only cleanup that one
|
|
if [ -n "$PR_INPUT" ]; then
|
|
if [[ "$PR_INPUT" =~ ^[1-9][0-9]*$ ]]; then
|
|
PR_NUM="$PR_INPUT"
|
|
WORKER_NAME="sure-preview-$PR_NUM"
|
|
echo "Manually deleting Worker: $WORKER_NAME"
|
|
wrangler delete --name "$WORKER_NAME" --force || echo "Worker may not exist"
|
|
|
|
# Cleanup GitHub deployment for this PR
|
|
echo "Cleaning up GitHub deployment for PR #$PR_NUM"
|
|
gh api \
|
|
-X GET "/repos/${{ github.repository }}/deployments?environment=preview-pr-$PR_NUM" \
|
|
--jq '.[].id' 2>/dev/null | while read -r DEPLOY_ID; do
|
|
if [ -n "$DEPLOY_ID" ]; then
|
|
gh api \
|
|
-X POST "/repos/${{ github.repository }}/deployments/$DEPLOY_ID/statuses" \
|
|
-f state=inactive \
|
|
-f description="Preview manually deleted" || true
|
|
fi
|
|
done || echo "No deployments to cleanup or error occurred"
|
|
else
|
|
echo "Invalid PR number input '$PR_INPUT'; skipping manual cleanup"
|
|
fi
|
|
|
|
exit 0
|
|
fi
|
|
|
|
# Get list of all preview workers
|
|
echo "Fetching list of preview workers..."
|
|
|
|
# Use Cloudflare API to list workers and read modified_on from the list response.
|
|
# The per-script endpoint returns raw script content, not JSON metadata.
|
|
WORKERS_RESPONSE=$(curl -fsS -X GET \
|
|
"https://api.cloudflare.com/client/v4/accounts/$CLOUDFLARE_ACCOUNT_ID/workers/scripts" \
|
|
-H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \
|
|
-H "Content-Type: application/json") || {
|
|
echo "Failed to fetch preview worker list from Cloudflare"
|
|
exit 1
|
|
}
|
|
|
|
if ! echo "$WORKERS_RESPONSE" | jq -e '.success == true and (.result | type == "array")' >/dev/null 2>&1; then
|
|
echo "Cloudflare API returned an invalid worker list response"
|
|
echo "$WORKERS_RESPONSE" | jq -c '.errors // .'
|
|
exit 1
|
|
fi
|
|
|
|
WORKERS=$(echo "$WORKERS_RESPONSE" | jq -r '
|
|
.result[]
|
|
| select(.id | startswith("sure-preview-"))
|
|
| [.id, (.modified_on // "")]
|
|
| @tsv
|
|
')
|
|
|
|
if [ -z "$WORKERS" ]; then
|
|
echo "No preview workers found"
|
|
exit 0
|
|
fi
|
|
|
|
echo "Found preview workers:"
|
|
echo "$WORKERS" | cut -f1
|
|
|
|
# Check each worker's deployment time
|
|
CUTOFF_TIME=$(date -d '24 hours ago' +%s)
|
|
|
|
while IFS=$'\t' read -r WORKER MODIFIED_ON; do
|
|
[ -n "$WORKER" ] || continue
|
|
echo "Checking $WORKER..."
|
|
|
|
if [ -z "$MODIFIED_ON" ]; then
|
|
echo "No modified_on timestamp for $WORKER; skipping"
|
|
continue
|
|
fi
|
|
|
|
if ! MODIFIED_TS=$(date -d "$MODIFIED_ON" +%s 2>/dev/null); then
|
|
echo "Invalid modified_on timestamp for $WORKER ($MODIFIED_ON); skipping"
|
|
continue
|
|
fi
|
|
|
|
if [ "$MODIFIED_TS" -lt "$CUTOFF_TIME" ]; then
|
|
echo "Worker $WORKER is older than 24 hours, deleting..."
|
|
if wrangler delete --name "$WORKER" --force; then
|
|
# Extract PR number and cleanup GitHub deployment
|
|
PR_NUM=$(echo "$WORKER" | sed 's/sure-preview-//')
|
|
if [[ "$PR_NUM" =~ ^[1-9][0-9]*$ ]]; then
|
|
echo "Cleaning up GitHub deployment for PR #$PR_NUM"
|
|
gh api \
|
|
-X GET "/repos/${{ github.repository }}/deployments?environment=preview-pr-$PR_NUM" \
|
|
--jq '.[].id' 2>/dev/null | while read -r DEPLOY_ID; do
|
|
gh api \
|
|
-X POST "/repos/${{ github.repository }}/deployments/$DEPLOY_ID/statuses" \
|
|
-f state=inactive \
|
|
-f description="Preview expired after 24 hours" || true
|
|
done || echo "No deployments to cleanup or error occurred"
|
|
else
|
|
echo "Could not extract a valid PR number from $WORKER; skipping deployment cleanup"
|
|
fi
|
|
else
|
|
echo "Failed to delete $WORKER; skipping deployment status update"
|
|
fi
|
|
else
|
|
echo "Worker $WORKER is still within 24-hour window, keeping..."
|
|
fi
|
|
done <<< "$WORKERS"
|
|
|
|
echo "Cleanup complete"
|