Skip to content

Commit 70fb515

Browse files
authored
Merge pull request #1055 from broadinstitute/fix-quay-tag-cleanup
Fix tag cleanup to prevent cascade deletion of shared-digest images
2 parents a7d8b51 + 12518b4 commit 70fb515

File tree

2 files changed

+110
-18
lines changed

2 files changed

+110
-18
lines changed
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
name: Audit Quay.io Tags
2+
3+
on:
4+
schedule:
5+
- cron: '0 8 * * 1' # Monday 8:00 UTC
6+
workflow_dispatch:
7+
8+
permissions: {}
9+
10+
jobs:
11+
audit-tags:
12+
runs-on: ubuntu-latest
13+
permissions: {}
14+
15+
steps:
16+
- name: Install crane
17+
uses: imjasonh/setup-crane@v0.4
18+
19+
- name: Log in to Quay.io
20+
uses: docker/login-action@v3
21+
with:
22+
registry: quay.io
23+
username: ${{ secrets.QUAY_USERNAME }}
24+
password: ${{ secrets.QUAY_TOKEN }}
25+
26+
- name: Audit version tags
27+
run: |
28+
set -euo pipefail
29+
REPO="quay.io/broadinstitute/viral-ngs"
30+
FLAVORS="baseimage core assemble classify phylo"
31+
FAILED=0
32+
33+
# Check the 5 most recent version tags
34+
VERSIONS=$(crane ls "$REPO" | grep -E '^[0-9]+\.[0-9]+\.[0-9]+$' | sort -V | tail -5)
35+
36+
if [[ -z "$VERSIONS" ]]; then
37+
echo "::error::No version tags found on ${REPO}"
38+
exit 1
39+
fi
40+
41+
check_tag() {
42+
local TAG="$1"
43+
if ! crane manifest "${REPO}:${TAG}" > /dev/null 2>&1; then
44+
echo "::error::MISSING: ${REPO}:${TAG}"
45+
FAILED=1
46+
else
47+
echo "OK: ${REPO}:${TAG}"
48+
fi
49+
}
50+
51+
for VERSION in $VERSIONS; do
52+
MAJOR_MINOR=$(echo "$VERSION" | sed -E 's/^([0-9]+\.[0-9]+).*/\1/')
53+
54+
# Check mega tag (no suffix) — both X.Y.Z and X.Y
55+
check_tag "${VERSION}"
56+
check_tag "${MAJOR_MINOR}"
57+
58+
# Check each flavor — both X.Y.Z-flavor and X.Y-flavor
59+
for FLAVOR in $FLAVORS; do
60+
check_tag "${VERSION}-${FLAVOR}"
61+
check_tag "${MAJOR_MINOR}-${FLAVOR}"
62+
done
63+
done
64+
65+
if [[ $FAILED -ne 0 ]]; then
66+
echo "::error::Some version tags are missing from Quay.io!"
67+
exit 1
68+
fi
69+
echo "All version tags verified."

.github/workflows/cleanup-images.yml

Lines changed: 41 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -11,28 +11,51 @@ jobs:
1111
permissions: {}
1212

1313
steps:
14-
- name: Delete feature branch images from Quay
14+
- name: Compute tag prefix
15+
id: tag-prefix
1516
run: |
16-
# Install skopeo
17-
sudo apt-get update && sudo apt-get install -y skopeo
17+
# Apply the same sanitization as docker.yml's "Compute Docker image tag prefix":
18+
# replace "/" with "-" and strip leading "v"
19+
RAW="${{ github.event.ref }}"
20+
PREFIX=$(echo "$RAW" | sed 's|/|-|g')
21+
PREFIX=${PREFIX#v}
22+
echo "prefix=${PREFIX}" >> $GITHUB_OUTPUT
23+
echo "Tag prefix: ${PREFIX} (from branch: ${RAW})"
1824
19-
BRANCH_TAG="${{ github.event.ref }}"
25+
- name: Safety check - refuse version-like branch names
26+
run: |
27+
PREFIX="${{ steps.tag-prefix.outputs.prefix }}"
28+
if [[ "$PREFIX" =~ ^[0-9]+\.[0-9]+ ]]; then
29+
echo "::error::Refusing to delete tags for version-like branch: $PREFIX"
30+
exit 1
31+
fi
32+
33+
- name: Install crane
34+
uses: imjasonh/setup-crane@v0.4
35+
36+
- name: Log in to Quay.io
37+
uses: docker/login-action@v3
38+
with:
39+
registry: quay.io
40+
username: ${{ secrets.QUAY_USERNAME }}
41+
password: ${{ secrets.QUAY_TOKEN }}
42+
43+
- name: Delete feature branch tags from Quay
44+
run: |
45+
TAG_PREFIX="${{ steps.tag-prefix.outputs.prefix }}"
2046
QUAY_REPO="quay.io/broadinstitute/viral-ngs"
2147
22-
# Image tags to delete - must be kept in sync with deploy-to-quay in docker.yml
48+
# Image tag suffixes - must be kept in sync with deploy-to-quay in docker.yml
2349
# See: .github/workflows/docker.yml deploy-to-quay job matrix
24-
IMAGES=(
25-
"${BRANCH_TAG}-baseimage"
26-
"${BRANCH_TAG}-core"
27-
"${BRANCH_TAG}-assemble"
28-
"${BRANCH_TAG}-classify"
29-
"${BRANCH_TAG}-phylo"
30-
"${BRANCH_TAG}" # mega image (no suffix)
31-
)
32-
33-
for TAG in "${IMAGES[@]}"; do
50+
SUFFIXES=("-baseimage" "-core" "-assemble" "-classify" "-phylo" "")
51+
52+
for SUFFIX in "${SUFFIXES[@]}"; do
53+
TAG="${TAG_PREFIX}${SUFFIX}"
3454
echo "Deleting ${QUAY_REPO}:${TAG}..."
35-
skopeo delete \
36-
--creds "${{ secrets.QUAY_USERNAME }}:${{ secrets.QUAY_TOKEN }}" \
37-
"docker://${QUAY_REPO}:${TAG}" || echo "Tag ${TAG} not found or already deleted"
55+
# Use crane delete instead of skopeo delete. crane removes only the tag
56+
# reference, not the underlying manifest. This prevents cascade deletion
57+
# of other tags sharing the same digest (which caused the 3.0.10-baseimage
58+
# incident: skopeo deleted the manifest by digest, expiring all tags
59+
# pointing to it).
60+
crane delete "${QUAY_REPO}:${TAG}" 2>&1 || echo " Tag ${TAG} not found or already deleted"
3861
done

0 commit comments

Comments
 (0)