-
-
Notifications
You must be signed in to change notification settings - Fork 11.5k
158 lines (134 loc) · 4.8 KB
/
cleanup-ghcr.yml
File metadata and controls
158 lines (134 loc) · 4.8 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
156
157
158
name: Cleanup GHCR Images
on:
schedule:
- cron: "30 4 * * *" # Daily at 04:30 UTC
workflow_dispatch:
inputs:
dry_run:
description: "Log what would be deleted without making changes"
required: false
default: true
type: boolean
retention_days:
description: "Delete versions older than this many days"
required: false
default: 14
type: number
min_keep:
description: "Always keep at least this many versions per package"
required: false
default: 10
type: number
permissions:
packages: write
env:
ORG: TryGhost
RETENTION_DAYS: ${{ inputs.retention_days || 14 }}
MIN_KEEP: ${{ inputs.min_keep || 10 }}
jobs:
cleanup:
name: Cleanup
runs-on: ubuntu-latest
strategy:
matrix:
package: [ghost, ghost-core, ghost-development]
steps:
- name: Delete old non-release versions
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
DRY_RUN: ${{ github.event_name == 'schedule' && 'false' || inputs.dry_run }}
PACKAGE: ${{ matrix.package }}
run: |
set -euo pipefail
cutoff=$(date -u -d "-${RETENTION_DAYS} days" +%Y-%m-%dT%H:%M:%SZ 2>/dev/null \
|| date -u -v-${RETENTION_DAYS}d +%Y-%m-%dT%H:%M:%SZ)
echo "Package: ${ORG}/${PACKAGE}"
echo "Cutoff: ${cutoff} (${RETENTION_DAYS} days ago)"
echo "Dry run: ${DRY_RUN}"
echo ""
# Pagination — collect all versions
page=1
all_versions="[]"
while true; do
if ! batch=$(gh api \
"/orgs/${ORG}/packages/container/${PACKAGE}/versions?per_page=100&page=${page}" \
--jq '.' 2>&1); then
if [ "$page" = "1" ]; then
echo "::error::API request failed: ${batch}"
exit 1
fi
echo "::warning::API request failed (page ${page}): ${batch}"
break
fi
count=$(echo "$batch" | jq 'length')
if [ "$count" = "0" ]; then
break
fi
all_versions=$(echo "$all_versions $batch" | jq -s 'add')
page=$((page + 1))
done
total=$(echo "$all_versions" | jq 'length')
echo "Total versions: ${total}"
# Classify versions
keep=0
delete=0
delete_ids=""
for row in $(echo "$all_versions" | jq -r '.[] | @base64'); do
_jq() { echo "$row" | base64 -d | jq -r "$1"; }
id=$(_jq '.id')
updated=$(_jq '.updated_at')
tags=$(_jq '[.metadata.container.tags[]] | join(",")')
# Keep versions with semver tags (v1.2.3, 1.2.3, 1.2)
if echo "$tags" | grep -qE '(^|,)v?[0-9]+\.[0-9]+\.[0-9]+(,|$)' || \
echo "$tags" | grep -qE '(^|,)[0-9]+\.[0-9]+(,|$)'; then
keep=$((keep + 1))
continue
fi
# Keep versions with 'latest' or 'main' or cache-main tags
if echo "$tags" | grep -qE '(^|,)(latest|main|cache-main)(,|$)'; then
keep=$((keep + 1))
continue
fi
# Keep versions newer than cutoff
if [[ "$updated" > "$cutoff" ]]; then
keep=$((keep + 1))
continue
fi
# This version is eligible for deletion
delete=$((delete + 1))
delete_ids="${delete_ids} ${id}"
tag_display="${tags:-<untagged>}"
if [ "$DRY_RUN" = "true" ]; then
echo "[dry-run] Would delete version ${id} (tags: ${tag_display}, updated: ${updated})"
fi
done
echo ""
echo "Summary: ${keep} kept, ${delete} to delete (of ${total} total)"
if [ "$delete" = "0" ]; then
echo "Nothing to delete."
exit 0
fi
# Safety check — run before dry-run exit so users see the warning
if [ "$keep" -lt "$MIN_KEEP" ]; then
echo "::error::Safety check failed — only ${keep} versions would remain (minimum: ${MIN_KEEP}). Aborting."
exit 1
fi
if [ "$DRY_RUN" = "true" ]; then
echo ""
echo "Dry run — no versions deleted."
exit 0
fi
# Delete eligible versions
deleted=0
failed=0
for id in $delete_ids; do
if gh api --method DELETE \
"/orgs/${ORG}/packages/container/${PACKAGE}/versions/${id}" 2>/dev/null; then
deleted=$((deleted + 1))
else
echo "::warning::Failed to delete version ${id}"
failed=$((failed + 1))
fi
done
echo ""
echo "Deleted ${deleted} versions (${failed} failed)"