66 - cron : ' 0 3 * * 0' # Weekly: Sundays at 03:00 UTC
77 workflow_dispatch :
88 inputs :
9- registries :
10- description : ' Comma-separated registries to prune (ghcr,dockerhub)'
11- required : false
12- default : ' ghcr,dockerhub'
139 keep_days :
1410 description : ' Number of days to retain images (unprotected)'
1511 required : false
@@ -28,55 +24,117 @@ permissions:
2824 contents : read
2925
3026jobs :
31- prune :
27+ prune-ghcr :
3228 runs-on : ubuntu-latest
3329 env :
3430 OWNER : ${{ github.repository_owner }}
3531 IMAGE_NAME : charon
36- REGISTRIES : ${{ github.event.inputs.registries || 'ghcr,dockerhub' }}
3732 KEEP_DAYS : ${{ github.event.inputs.keep_days || '30' }}
3833 KEEP_LAST_N : ${{ github.event.inputs.keep_last_n || '30' }}
39- DRY_RUN : ${{ github.event.inputs.dry_run || 'false' }}
34+ DRY_RUN : ${{ github.event_name == 'pull_request' && 'true' || github. event.inputs.dry_run || 'false' }}
4035 PROTECTED_REGEX : ' ["^v?[0-9]+\\.[0-9]+\\.[0-9]+$","^latest$","^main$","^develop$"]'
36+ PRUNE_UNTAGGED : ' true'
37+ PRUNE_SBOM_TAGS : ' true'
4138 steps :
4239 - name : Checkout
4340 uses : actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
4441
4542 - name : Install tools
4643 run : |
47- sudo apt-get update && sudo apt-get install -y jq curl gh
44+ sudo apt-get update && sudo apt-get install -y jq curl
45+
46+ - name : Run GHCR prune
47+ env :
48+ GITHUB_TOKEN : ${{ secrets.GITHUB_TOKEN }}
49+ run : |
50+ chmod +x scripts/prune-ghcr.sh
51+ ./scripts/prune-ghcr.sh 2>&1 | tee prune-ghcr-${{ github.run_id }}.log
52+
53+ - name : Summarize GHCR results
54+ if : always()
55+ run : |
56+ set -euo pipefail
57+ SUMMARY_FILE=prune-summary-ghcr.env
58+ LOG_FILE=prune-ghcr-${{ github.run_id }}.log
59+
60+ human() {
61+ local bytes=${1:-0}
62+ if [ -z "$bytes" ] || [ "$bytes" -eq 0 ]; then
63+ echo "0 B"
64+ return
65+ fi
66+ awk -v b="$bytes" 'BEGIN { split("B KiB MiB GiB TiB",u," "); i=0; x=b; while(x>1024){x/=1024;i++} printf "%0.2f %s", x, u[i+1] }'
67+ }
68+
69+ if [ -f "$SUMMARY_FILE" ]; then
70+ TOTAL_CANDIDATES=$(grep -E '^TOTAL_CANDIDATES=' "$SUMMARY_FILE" | cut -d= -f2 || echo 0)
71+ TOTAL_CANDIDATES_BYTES=$(grep -E '^TOTAL_CANDIDATES_BYTES=' "$SUMMARY_FILE" | cut -d= -f2 || echo 0)
72+ TOTAL_DELETED=$(grep -E '^TOTAL_DELETED=' "$SUMMARY_FILE" | cut -d= -f2 || echo 0)
73+ TOTAL_DELETED_BYTES=$(grep -E '^TOTAL_DELETED_BYTES=' "$SUMMARY_FILE" | cut -d= -f2 || echo 0)
74+
75+ {
76+ echo "## GHCR prune summary"
77+ echo "- candidates: ${TOTAL_CANDIDATES} (≈ $(human "${TOTAL_CANDIDATES_BYTES}"))"
78+ echo "- deleted: ${TOTAL_DELETED} (≈ $(human "${TOTAL_DELETED_BYTES}"))"
79+ } >> "$GITHUB_STEP_SUMMARY"
80+ else
81+ deleted_bytes=$(grep -oE '\( *approx +[0-9]+ bytes\)' "$LOG_FILE" | sed -E 's/.*approx +([0-9]+) bytes.*/\1/' | awk '{s+=$1} END {print s+0}' || true)
82+ deleted_count=$(grep -cE 'deleting |DRY RUN: would delete' "$LOG_FILE" || true)
83+
84+ {
85+ echo "## GHCR prune summary"
86+ echo "- deleted (approx): ${deleted_count} (≈ $(human "${deleted_bytes}"))"
87+ } >> "$GITHUB_STEP_SUMMARY"
88+ fi
89+
90+ - name : Upload GHCR prune artifacts
91+ if : always()
92+ uses : actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6
93+ with :
94+ name : prune-ghcr-log-${{ github.run_id }}
95+ path : |
96+ prune-ghcr-${{ github.run_id }}.log
97+ prune-summary-ghcr.env
4898
49- - name : Show prune script being executed
99+ prune-dockerhub :
100+ runs-on : ubuntu-latest
101+ env :
102+ OWNER : ${{ github.repository_owner }}
103+ IMAGE_NAME : charon
104+ KEEP_DAYS : ${{ github.event.inputs.keep_days || '30' }}
105+ KEEP_LAST_N : ${{ github.event.inputs.keep_last_n || '30' }}
106+ DRY_RUN : ${{ github.event_name == 'pull_request' && 'true' || github.event.inputs.dry_run || 'false' }}
107+ PROTECTED_REGEX : ' ["^v?[0-9]+\\.[0-9]+\\.[0-9]+$","^latest$","^main$","^develop$"]'
108+ steps :
109+ - name : Checkout
110+ uses : actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
111+
112+ - name : Install tools
50113 run : |
51- echo "===== SCRIPT PATH ====="
52- pwd
53- ls -la scripts
54- echo "===== FIRST 20 LINES ====="
55- head -n 20 scripts/prune-container-images.sh
114+ sudo apt-get update && sudo apt-get install -y jq curl
56115
57- - name : Run container prune
116+ - name : Run Docker Hub prune
58117 env :
59- GITHUB_TOKEN : ${{ secrets.GITHUB_TOKEN }}
60118 DOCKERHUB_USERNAME : ${{ secrets.DOCKERHUB_USERNAME }}
61119 DOCKERHUB_TOKEN : ${{ secrets.DOCKERHUB_TOKEN }}
62120 run : |
63- chmod +x scripts/prune-container-images .sh
64- ./scripts/prune-container-images .sh 2>&1 | tee prune-${{ github.run_id }}.log
121+ chmod +x scripts/prune-dockerhub .sh
122+ ./scripts/prune-dockerhub .sh 2>&1 | tee prune-dockerhub -${{ github.run_id }}.log
65123
66- - name : Summarize prune results (space reclaimed)
67- if : ${{ always() }}
124+ - name : Summarize Docker Hub results
125+ if : always()
68126 run : |
69127 set -euo pipefail
70- SUMMARY_FILE=prune-summary.env
71- LOG_FILE=prune-${{ github.run_id }}.log
128+ SUMMARY_FILE=prune-summary-dockerhub .env
129+ LOG_FILE=prune-dockerhub- ${{ github.run_id }}.log
72130
73131 human() {
74132 local bytes=${1:-0}
75133 if [ -z "$bytes" ] || [ "$bytes" -eq 0 ]; then
76134 echo "0 B"
77135 return
78136 fi
79- awk -v b="$bytes" 'function human(x) { split("B KiB MiB GiB TiB",u," "); i=0; while(x>1024){x/=1024;i++} printf "%0.2f %s", x, u[i+1]} END{human(b) }'
137+ awk -v b="$bytes" 'BEGIN { split("B KiB MiB GiB TiB",u," "); i=0; x=b; while(x>1024){x/=1024;i++} printf "%0.2f %s", x, u[i+1] }'
80138 }
81139
82140 if [ -f "$SUMMARY_FILE" ]; then
@@ -86,34 +144,84 @@ jobs:
86144 TOTAL_DELETED_BYTES=$(grep -E '^TOTAL_DELETED_BYTES=' "$SUMMARY_FILE" | cut -d= -f2 || echo 0)
87145
88146 {
89- echo "## Container prune summary"
147+ echo "## Docker Hub prune summary"
90148 echo "- candidates: ${TOTAL_CANDIDATES} (≈ $(human "${TOTAL_CANDIDATES_BYTES}"))"
91149 echo "- deleted: ${TOTAL_DELETED} (≈ $(human "${TOTAL_DELETED_BYTES}"))"
92150 } >> "$GITHUB_STEP_SUMMARY"
93-
94- printf 'PRUNE_SUMMARY: candidates=%s candidates_bytes=%s deleted=%s deleted_bytes=%s\n' \
95- "${TOTAL_CANDIDATES}" "${TOTAL_CANDIDATES_BYTES}" "${TOTAL_DELETED}" "${TOTAL_DELETED_BYTES}"
96- echo "Deleted approximately: $(human "${TOTAL_DELETED_BYTES}")"
97- echo "space_saved=$(human "${TOTAL_DELETED_BYTES}")" >> "$GITHUB_OUTPUT"
98151 else
99152 deleted_bytes=$(grep -oE '\( *approx +[0-9]+ bytes\)' "$LOG_FILE" | sed -E 's/.*approx +([0-9]+) bytes.*/\1/' | awk '{s+=$1} END {print s+0}' || true)
100153 deleted_count=$(grep -cE 'deleting |DRY RUN: would delete' "$LOG_FILE" || true)
101154
102155 {
103- echo "## Container prune summary"
156+ echo "## Docker Hub prune summary"
104157 echo "- deleted (approx): ${deleted_count} (≈ $(human "${deleted_bytes}"))"
105158 } >> "$GITHUB_STEP_SUMMARY"
106-
107- printf 'PRUNE_SUMMARY: deleted_approx=%s deleted_bytes=%s\n' "${deleted_count}" "${deleted_bytes}"
108- echo "Deleted approximately: $(human "${deleted_bytes}")"
109- echo "space_saved=$(human "${deleted_bytes}")" >> "$GITHUB_OUTPUT"
110159 fi
111160
112- - name : Upload prune artifacts
113- if : ${{ always() }}
161+ - name : Upload Docker Hub prune artifacts
162+ if : always()
114163 uses : actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6
115164 with :
116- name : prune-log-${{ github.run_id }}
165+ name : prune-dockerhub- log-${{ github.run_id }}
117166 path : |
118- prune-${{ github.run_id }}.log
119- prune-summary.env
167+ prune-dockerhub-${{ github.run_id }}.log
168+ prune-summary-dockerhub.env
169+
170+ summarize :
171+ runs-on : ubuntu-latest
172+ needs : [prune-ghcr, prune-dockerhub]
173+ if : always()
174+ steps :
175+ - name : Download all artifacts
176+ uses : actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4
177+ with :
178+ pattern : prune-*-log-${{ github.run_id }}
179+ merge-multiple : true
180+
181+ - name : Combined summary
182+ run : |
183+ set -euo pipefail
184+
185+ human() {
186+ local bytes=${1:-0}
187+ if [ -z "$bytes" ] || [ "$bytes" -eq 0 ]; then
188+ echo "0 B"
189+ return
190+ fi
191+ awk -v b="$bytes" 'BEGIN { split("B KiB MiB GiB TiB",u," "); i=0; x=b; while(x>1024){x/=1024;i++} printf "%0.2f %s", x, u[i+1] }'
192+ }
193+
194+ GHCR_CANDIDATES=0 GHCR_CANDIDATES_BYTES=0 GHCR_DELETED=0 GHCR_DELETED_BYTES=0
195+ if [ -f prune-summary-ghcr.env ]; then
196+ GHCR_CANDIDATES=$(grep -E '^TOTAL_CANDIDATES=' prune-summary-ghcr.env | cut -d= -f2 || echo 0)
197+ GHCR_CANDIDATES_BYTES=$(grep -E '^TOTAL_CANDIDATES_BYTES=' prune-summary-ghcr.env | cut -d= -f2 || echo 0)
198+ GHCR_DELETED=$(grep -E '^TOTAL_DELETED=' prune-summary-ghcr.env | cut -d= -f2 || echo 0)
199+ GHCR_DELETED_BYTES=$(grep -E '^TOTAL_DELETED_BYTES=' prune-summary-ghcr.env | cut -d= -f2 || echo 0)
200+ fi
201+
202+ HUB_CANDIDATES=0 HUB_CANDIDATES_BYTES=0 HUB_DELETED=0 HUB_DELETED_BYTES=0
203+ if [ -f prune-summary-dockerhub.env ]; then
204+ HUB_CANDIDATES=$(grep -E '^TOTAL_CANDIDATES=' prune-summary-dockerhub.env | cut -d= -f2 || echo 0)
205+ HUB_CANDIDATES_BYTES=$(grep -E '^TOTAL_CANDIDATES_BYTES=' prune-summary-dockerhub.env | cut -d= -f2 || echo 0)
206+ HUB_DELETED=$(grep -E '^TOTAL_DELETED=' prune-summary-dockerhub.env | cut -d= -f2 || echo 0)
207+ HUB_DELETED_BYTES=$(grep -E '^TOTAL_DELETED_BYTES=' prune-summary-dockerhub.env | cut -d= -f2 || echo 0)
208+ fi
209+
210+ TOTAL_CANDIDATES=$((GHCR_CANDIDATES + HUB_CANDIDATES))
211+ TOTAL_CANDIDATES_BYTES=$((GHCR_CANDIDATES_BYTES + HUB_CANDIDATES_BYTES))
212+ TOTAL_DELETED=$((GHCR_DELETED + HUB_DELETED))
213+ TOTAL_DELETED_BYTES=$((GHCR_DELETED_BYTES + HUB_DELETED_BYTES))
214+
215+ {
216+ echo "## Combined container prune summary"
217+ echo ""
218+ echo "| Registry | Candidates | Deleted | Space Reclaimed |"
219+ echo "|----------|------------|---------|-----------------|"
220+ echo "| GHCR | ${GHCR_CANDIDATES} | ${GHCR_DELETED} | $(human "${GHCR_DELETED_BYTES}") |"
221+ echo "| Docker Hub | ${HUB_CANDIDATES} | ${HUB_DELETED} | $(human "${HUB_DELETED_BYTES}") |"
222+ echo "| **Total** | **${TOTAL_CANDIDATES}** | **${TOTAL_DELETED}** | **$(human "${TOTAL_DELETED_BYTES}")** |"
223+ } >> "$GITHUB_STEP_SUMMARY"
224+
225+ printf 'PRUNE_SUMMARY: candidates=%s candidates_bytes=%s deleted=%s deleted_bytes=%s\n' \
226+ "${TOTAL_CANDIDATES}" "${TOTAL_CANDIDATES_BYTES}" "${TOTAL_DELETED}" "${TOTAL_DELETED_BYTES}"
227+ echo "Total space reclaimed: $(human "${TOTAL_DELETED_BYTES}")"
0 commit comments