Skip to content

Commit 242823d

Browse files
authored
Chore: [AEA-0000] - delete old images fix (#19)
## Summary - Routine Change ### Details - fix delete old images - tag ci images with prefix ci-
1 parent c73f412 commit 242823d

File tree

5 files changed

+140
-15
lines changed

5 files changed

+140
-15
lines changed

.github/scripts/delete_unused_images.sh

Lines changed: 109 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,39 @@
11
#!/usr/bin/env bash
22

3+
DRY_RUN=false
4+
DELETE_PR=false
5+
DELETE_CI=false
6+
7+
while [[ $# -gt 0 ]]; do
8+
case "$1" in
9+
--dry-run|-n)
10+
DRY_RUN=true
11+
shift
12+
;;
13+
--delete-pr)
14+
DELETE_PR=true
15+
shift
16+
;;
17+
--delete-ci)
18+
DELETE_CI=true
19+
shift
20+
;;
21+
--help|-h)
22+
echo "Usage: $0 [--dry-run] [--delete-pr] [--delete-ci]"
23+
exit 0
24+
;;
25+
*)
26+
echo "Unknown option: $1" >&2
27+
echo "Usage: $0 [--dry-run] [--delete-pr] [--delete-ci]" >&2
28+
exit 1
29+
;;
30+
esac
31+
done
32+
33+
if [[ "${DELETE_PR}" == "false" && "${DELETE_CI}" == "false" ]]; then
34+
DELETE_PR=true
35+
fi
36+
337
get_container_package_name() {
438
local container_name=$1
539

@@ -40,16 +74,18 @@ delete_pr_images() {
4074
tags=$(jq -r '[.[].metadata.container.tags[]?] | unique | .[]' <<<"${versions_json}")
4175

4276
if [[ -z "${tags}" ]]; then
77+
echo "No tags found for container ${container_name}, skipping."
4378
return 0
4479
fi
4580

4681
while IFS= read -r tag; do
4782
local pull_request
48-
if [[ "${tag}" =~ ^pr-([0-9]+)- ]]; then
83+
if [[ "${tag}" =~ ^pr-([0-9]+)(-.+)?$ ]]; then
4984
pull_request=${BASH_REMATCH[1]}
50-
elif [[ "${tag}" =~ ^githubactions-pr-([0-9]+)$ ]]; then
85+
elif [[ "${tag}" =~ ^githubactions-pr-([0-9]+)(-.+)?$ ]]; then
5186
pull_request=${BASH_REMATCH[1]}
5287
else
88+
echo "Tag ${tag} does not match expected PR tag format for container ${container_name}, skipping."
5389
continue
5490
fi
5591

@@ -72,26 +108,89 @@ delete_pr_images() {
72108
<<<"${versions_json}" \
73109
| while IFS= read -r version_id; do
74110
if [[ -n "${version_id}" ]]; then
75-
echo "Deleting image with tag ${tag} (version ID: ${version_id}) from container ${container_name}..."
76-
gh api \
77-
-H "Accept: application/vnd.github+json" \
78-
-X DELETE \
79-
"/orgs/nhsdigital/packages/container/${package_name}/versions/${version_id}"
111+
if [[ "${DRY_RUN}" == "true" ]]; then
112+
echo "[DRY RUN] Would delete image with tag ${tag} (version ID: ${version_id}) from container ${container_name}."
113+
else
114+
echo "Deleting image with tag ${tag} (version ID: ${version_id}) from container ${container_name}..."
115+
gh api \
116+
-H "Accept: application/vnd.github+json" \
117+
-X DELETE \
118+
"/orgs/nhsdigital/packages/container/${package_name}/versions/${version_id}"
119+
fi
80120
fi
81121
done
82122
done <<<"${tags}"
83123
}
84124

125+
delete_ci_images() {
126+
local container_name=$1
127+
local package_name
128+
local versions_json
129+
local tags
130+
131+
if [[ -z "${container_name}" ]]; then
132+
echo "Container name is required" >&2
133+
return 1
134+
fi
135+
136+
package_name=$(get_container_package_name "${container_name}")
137+
versions_json=$(get_container_versions_json "${container_name}")
138+
tags=$(jq -r '[.[].metadata.container.tags[]?] | unique | .[]' <<<"${versions_json}")
139+
140+
if [[ -z "${tags}" ]]; then
141+
echo "No tags found for container ${container_name}, skipping."
142+
return 0
143+
fi
144+
145+
while IFS= read -r tag; do
146+
if [[ ! "${tag}" =~ ^ci-[0-9a-fA-F]{8}.*$ ]] && [[ ! "${tag}" =~ ^githubactions-ci-[0-9a-fA-F]{8}.*$ ]]; then
147+
echo "Tag ${tag} does not match expected CI tag format for container ${container_name}, skipping."
148+
continue
149+
fi
150+
151+
jq -r --arg tag "${tag}" '.[] | select(.metadata.container.tags[]? == $tag) | .id' \
152+
<<<"${versions_json}" \
153+
| while IFS= read -r version_id; do
154+
if [[ -n "${version_id}" ]]; then
155+
if [[ "${DRY_RUN}" == "true" ]]; then
156+
echo "[DRY RUN] Would delete CI image with tag ${tag} (version ID: ${version_id}) from container ${container_name}."
157+
else
158+
echo "Deleting CI image with tag ${tag} (version ID: ${version_id}) from container ${container_name}..."
159+
gh api \
160+
-H "Accept: application/vnd.github+json" \
161+
-X DELETE \
162+
"/orgs/nhsdigital/packages/container/${package_name}/versions/${version_id}"
163+
fi
164+
fi
165+
done
166+
done <<<"${tags}"
167+
}
168+
85169

86170
language_folders=$(find src/languages -mindepth 1 -maxdepth 1 -type d -printf '%f\n' | jq -R -s -c 'split("\n")[:-1]')
87171
project_folders=$(find src/projects -mindepth 1 -maxdepth 1 -type d -printf '%f\n' | jq -R -s -c 'split("\n")[:-1]')
88172

89173
for container_name in $(jq -r '.[]' <<<"${project_folders}"); do
90-
delete_pr_images "${container_name}"
174+
if [[ "${DELETE_PR}" == "true" ]]; then
175+
delete_pr_images "${container_name}"
176+
fi
177+
if [[ "${DELETE_CI}" == "true" ]]; then
178+
delete_ci_images "${container_name}"
179+
fi
91180
done
92181

93182
for container_name in $(jq -r '.[]' <<<"${language_folders}"); do
94-
delete_pr_images "${container_name}"
183+
if [[ "${DELETE_PR}" == "true" ]]; then
184+
delete_pr_images "${container_name}"
185+
fi
186+
if [[ "${DELETE_CI}" == "true" ]]; then
187+
delete_ci_images "${container_name}"
188+
fi
95189
done
96190

97-
delete_pr_images "base"
191+
if [[ "${DELETE_PR}" == "true" ]]; then
192+
delete_pr_images "base"
193+
fi
194+
if [[ "${DELETE_CI}" == "true" ]]; then
195+
delete_ci_images "base"
196+
fi

.github/workflows/ci.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,6 @@ jobs:
4343
needs: tag_release
4444
uses: ./.github/workflows/build_all_images.yml
4545
with:
46-
docker_tag: '${{ needs.tag_release.outputs.version_tag }}'
46+
docker_tag: 'ci-${{ needs.tag_release.outputs.version_tag }}'
4747
tag_latest: false
4848
NO_CACHE: false

.github/workflows/delete_old_images.yml

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,13 @@ jobs:
2626

2727
- name: delete unused images
2828
shell: bash
29-
run: .github/scripts/delete_unused_images.sh
29+
run: |
30+
if [[ "${{ github.event_name }}" == "workflow_dispatch" ]]; then
31+
.github/scripts/delete_unused_images.sh --delete-pr
32+
elif [[ "${{ github.event_name }}" == "schedule" ]]; then
33+
.github/scripts/delete_unused_images.sh --delete-ci
34+
else
35+
.github/scripts/delete_unused_images.sh
36+
fi
3037
env:
3138
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}

README.md

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -130,8 +130,8 @@ The base image is built first, and then language images, and finally project ima
130130
Docker images are scanned for vulnerabilities using trivy as part of a build step, and the build fails if vulnerabilities are found not in .trivyignore file.
131131

132132
For pull requests, images are tagged with the pr-{pull request id}-{short commit sha}.
133-
For merges to main, images are tagged with the {short commit sha}.
134-
Github actions images are tagged with githubactions-{tag}
133+
For merges to main, images are tagged with the ci-{short commit sha}.
134+
Github actions images are tagged with githubactions-{full tag}
135135
Amd64 images are tagged with {tag}-amd64
136136
Arm64 images are tagged with {tag}-arm64
137137
Combined image manifest image is just tagged with {tag} so can be included in devcontainer.json and the correct image is pulled based on the host architecture.
@@ -274,6 +274,25 @@ poetry run python \
274274
--output src/projects/fhir_facade_api/.trivyignore.new.yaml
275275
```
276276

277+
## Cleaning up unused container images
278+
279+
There is a script to delete unused container images. This runs on every merge to main, and deletes pull request images, and on a weekly schedule which deletes images created by ci.
280+
You can run it manually using the following. Using the `dry-run` flag just shows what would be deleted
281+
282+
```
283+
make github-login
284+
bash .github/scripts/delete_unused_images.sh --delete-pr --dry-run
285+
bash .github/scripts/delete_unused_images.sh --delete-ci --dry-run
286+
bash .github/scripts/delete_unused_images.sh --delete-pr --delete-ci
287+
```
288+
289+
Flags:
290+
- `--dry-run` (`-n`) shows what would be deleted without deleting anything.
291+
- `--delete-pr` deletes images tagged with `pr-...` or `githubactions-pr-...` only when the PR is closed.
292+
- `--delete-ci` deletes images tagged with `ci-<8 hex sha>...` or `githubactions-ci-<8 hex sha>...`.
293+
294+
If neither `--delete-pr` nor `--delete-ci` is set, the script defaults to `--delete-pr`.
295+
277296
## Common makefile targets
278297
There are a set of common Makefiles that are defined in `src/base/.devcontainer/Mk` and are included from `common.mk`. These are installed to /usr/local/share/eps/Mk on the base image so are available for all containers.
279298

src/base/.devcontainer/Mk/trivy.mk

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ trivy-scan-java:
7979
--output .trivy_out/dependency_results_java.txt \
8080
--format table
8181

82-
trivy-scan-docker:
82+
trivy-scan-docker: guard-DOCKER_IMAGE
8383
mkdir -p .trivy_out/
8484
trivy image $${DOCKER_IMAGE} \
8585
--scanners vuln \

0 commit comments

Comments
 (0)