-
Notifications
You must be signed in to change notification settings - Fork 113
155 lines (139 loc) · 6.17 KB
/
Copy pathcleanup.yml
File metadata and controls
155 lines (139 loc) · 6.17 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
name: Cleanup
on:
pull_request_target:
types: [closed]
schedule:
- cron: '0 2 * * *'
workflow_dispatch:
permissions:
contents: read
actions: write
pull-requests: read
jobs:
# --- PR close: worker/container cleanup ---
# Uses pull_request_target so it fires for fork PRs too (needs secrets).
# Safe: only deletes resources keyed by PR number, never executes PR code.
#
# Image tags (pr-* and ci-*) are NOT deleted here. Deleting pr-* tags was
# observed to break the ci-* content-addressed Docker cache, likely because
# they share underlying manifests and deletion triggers GC. Image cleanup
# happens in the daily sweep only for closed/stale PRs.
cleanup-pr:
if: github.event_name == 'pull_request_target'
runs-on: ubuntu-latest
timeout-minutes: 5
steps:
- name: Install wrangler
run: npm install -g wrangler@latest
- name: Delete GHA caches
timeout-minutes: 1
continue-on-error: true
run: gh cache delete --all --ref "refs/pull/${{ github.event.pull_request.number }}/merge" || true
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Delete test workers and containers
timeout-minutes: 3
continue-on-error: true
run: |
PR=${{ github.event.pull_request.number }}
CONTAINERS=$(wrangler containers list --json 2>/dev/null || echo "[]")
WORKER="sandbox-e2e-test-worker-pr-${PR}"
IDS=$(echo "$CONTAINERS" | jq -r ".[] | select(.name == \"$WORKER\" or (.name | startswith(\"$WORKER-\"))) | .id" 2>/dev/null)
wrangler delete --name "$WORKER" 2>/dev/null || true
for id in $IDS; do
wrangler containers delete "$id" 2>/dev/null || true
done
env:
CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}
CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
# --- Daily sweep: backstop for stale resources + image cache eviction ---
cleanup-stale:
if: github.event_name == 'schedule' || github.event_name == 'workflow_dispatch'
runs-on: ubuntu-latest
timeout-minutes: 15
steps:
- uses: actions/checkout@v6
with:
sparse-checkout: |
docker-images.txt
.github/load-docker-images.sh
sparse-checkout-cone-mode: false
persist-credentials: false
fetch-depth: 1
- name: Install wrangler
run: npm install -g wrangler@latest
- name: Clean stale PR resources
timeout-minutes: 10
run: |
# Discover candidate PRs from BOTH containers and images so we
# catch orphaned resources even when one source is already cleaned.
CONTAINERS=$(wrangler containers list --json 2>/dev/null || echo "[]")
CONTAINER_PRS=$(echo "$CONTAINERS" | jq -r '.[].name' 2>/dev/null \
| grep -oE 'pr-[0-9]+' | sed 's/pr-//' | sort -u || true)
ALL_IMAGES=$(wrangler containers images list --json 2>/dev/null || echo "[]")
IMAGE_PRS=$(echo "$ALL_IMAGES" | jq -r '.[].tags[]?' 2>/dev/null \
| grep -oE '^pr-[0-9]+$' | sed 's/pr-//' | sort -u || true)
PR_NUMBERS=$(printf '%s\n' $CONTAINER_PRS $IMAGE_PRS | sort -un)
if [ -z "$PR_NUMBERS" ]; then
echo "No PR resources found"
exit 0
fi
STALE_THRESHOLD=$((7 * 24 * 60 * 60))
NOW=$(date +%s)
declare -A SHOULD_CLEAN
for PR in $PR_NUMBERS; do
PR_INFO=$(gh pr view "$PR" --json state,updatedAt 2>/dev/null || echo "")
if [ -z "$PR_INFO" ]; then
SHOULD_CLEAN[$PR]=true
continue
fi
STATE=$(echo "$PR_INFO" | jq -r '.state')
if [ "$STATE" = "CLOSED" ] || [ "$STATE" = "MERGED" ]; then
SHOULD_CLEAN[$PR]=true
elif [ "$STATE" = "OPEN" ]; then
UPDATED=$(echo "$PR_INFO" | jq -r '.updatedAt')
UPDATED_EPOCH=$(date -d "$UPDATED" +%s 2>/dev/null || date -j -f "%Y-%m-%dT%H:%M:%SZ" "$UPDATED" +%s 2>/dev/null)
if [ $((NOW - UPDATED_EPOCH)) -gt $STALE_THRESHOLD ]; then
SHOULD_CLEAN[$PR]=true
else
SHOULD_CLEAN[$PR]=false
fi
fi
done
source .github/load-docker-images.sh
for PR in $PR_NUMBERS; do
if [ "${SHOULD_CLEAN[$PR]}" = "true" ]; then
echo "PR #$PR is stale — cleaning up"
WORKER="sandbox-e2e-test-worker-pr-${PR}"
IDS=$(echo "$CONTAINERS" | jq -r ".[] | select(.name == \"$WORKER\" or (.name | startswith(\"$WORKER-\"))) | .id" 2>/dev/null)
wrangler delete --name "$WORKER" 2>/dev/null || true
for id in $IDS; do
wrangler containers delete "$id" 2>/dev/null || true
done
# Delete pr-* images for closed/stale PRs. These tags are no
# longer referenced by any active CI run. Note: if the CF
# registry GC's shared manifests on tag deletion, this could
# invalidate ci-* tags sharing the same digest — the cache
# rebuilds on next use (~6 min one-time cost).
for image in "${DOCKER_IMAGES[@]}"; do
wrangler containers images delete "$image:pr-$PR" 2>/dev/null || true
done
fi
done
env:
CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}
CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Clean stale GHA caches
timeout-minutes: 2
run: |
CACHES=$(gh cache list --json ref -q '.[].ref' 2>/dev/null || echo "")
echo "$CACHES" | grep -E 'refs/pull/[0-9]+/merge' | while read -r REF; do
PR=$(echo "$REF" | grep -oE '[0-9]+')
STATE=$(gh pr view "$PR" --json state -q '.state' 2>/dev/null || echo "NOT_FOUND")
if [ "$STATE" = "CLOSED" ] || [ "$STATE" = "MERGED" ] || [ "$STATE" = "NOT_FOUND" ]; then
gh cache delete --all --ref "$REF" || true
fi
done || true
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}