This commit is contained in:
@@ -45,6 +45,11 @@ on:
|
|||||||
required: false
|
required: false
|
||||||
default: ""
|
default: ""
|
||||||
type: string
|
type: string
|
||||||
|
release_retention_count:
|
||||||
|
description: Number of immutable sha-* release images to retain after a successful promotion
|
||||||
|
required: false
|
||||||
|
default: 2
|
||||||
|
type: number
|
||||||
secrets:
|
secrets:
|
||||||
REGISTRY_USERNAME:
|
REGISTRY_USERNAME:
|
||||||
required: true
|
required: true
|
||||||
@@ -149,3 +154,90 @@ jobs:
|
|||||||
set -eu
|
set -eu
|
||||||
ssh -i ~/.ssh/id_ed25519 cicd-deploy@"${{ inputs.deploy_host }}" \
|
ssh -i ~/.ssh/id_ed25519 cicd-deploy@"${{ inputs.deploy_host }}" \
|
||||||
"promote ${{ inputs.deploy_service }} ${{ inputs.image_name }}:sha-${{ gitea.sha }}"
|
"promote ${{ inputs.deploy_service }} ${{ inputs.image_name }}:sha-${{ gitea.sha }}"
|
||||||
|
- name: Retain current and previous release images
|
||||||
|
if: ${{ startsWith(gitea.ref, 'refs/tags/v') && inputs.deploy_service != '' && inputs.deploy_host != '' }}
|
||||||
|
env:
|
||||||
|
REGISTRY_USERNAME: ${{ secrets.REGISTRY_USERNAME }}
|
||||||
|
REGISTRY_PASSWORD: ${{ secrets.REGISTRY_PASSWORD }}
|
||||||
|
run: |
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
image_name='${{ inputs.image_name }}'
|
||||||
|
keep_count='${{ inputs.release_retention_count }}'
|
||||||
|
current_version='sha-${{ gitea.sha }}'
|
||||||
|
|
||||||
|
case "$keep_count" in
|
||||||
|
''|*[!0-9]*|0)
|
||||||
|
echo "release_retention_count must be a positive integer"
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
registry="${image_name%%/*}"
|
||||||
|
image_path="${image_name#*/}"
|
||||||
|
owner="${image_path%%/*}"
|
||||||
|
package_name="${image_path#*/}"
|
||||||
|
|
||||||
|
if [ "$registry" = "$image_name" ] || [ "$owner" = "$image_path" ]; then
|
||||||
|
echo "image_name must include registry, owner, and package name"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
case "$owner/$package_name" in
|
||||||
|
*[!A-Za-z0-9._/-]*|*/*/*)
|
||||||
|
echo "image owner or package name contains unsupported characters"
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
api_base="https://${registry}/api/v1/packages"
|
||||||
|
versions_file="$(mktemp)"
|
||||||
|
keep_file="$(mktemp)"
|
||||||
|
trap 'rm -f "$versions_file" "$keep_file"' EXIT
|
||||||
|
|
||||||
|
page=1
|
||||||
|
while [ "$page" -le 100 ]; do
|
||||||
|
response="$(curl --fail --silent --show-error --retry 3 --retry-all-errors \
|
||||||
|
--user "$REGISTRY_USERNAME:$REGISTRY_PASSWORD" \
|
||||||
|
"${api_base}/${owner}?type=container&q=${package_name}&limit=50&page=${page}")"
|
||||||
|
item_count="$(printf '%s' "$response" | jq 'length')"
|
||||||
|
[ "$item_count" -gt 0 ] || break
|
||||||
|
printf '%s' "$response" | jq -r --arg package_name "$package_name" '
|
||||||
|
.[]
|
||||||
|
| select((.name | ascii_downcase) == ($package_name | ascii_downcase))
|
||||||
|
| select(.version | test("^sha-[0-9a-f]{40}$"))
|
||||||
|
| [.created_at, .version]
|
||||||
|
| @tsv
|
||||||
|
' >> "$versions_file"
|
||||||
|
page=$((page + 1))
|
||||||
|
done
|
||||||
|
|
||||||
|
if ! awk -F '\t' -v current="$current_version" '$2 == current { found = 1 } END { exit(found ? 0 : 1) }' "$versions_file"; then
|
||||||
|
echo "Current release version was not returned by the Gitea Packages API: $current_version"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
printf '%s\n' "$current_version" > "$keep_file"
|
||||||
|
previous_count=$((keep_count - 1))
|
||||||
|
if [ "$previous_count" -gt 0 ]; then
|
||||||
|
sort -r "$versions_file" | awk -F '\t' \
|
||||||
|
-v current="$current_version" \
|
||||||
|
-v limit="$previous_count" \
|
||||||
|
'$2 != current && kept < limit { print $2; kept++ }' >> "$keep_file"
|
||||||
|
fi
|
||||||
|
|
||||||
|
deleted=0
|
||||||
|
while IFS=$'\t' read -r _created_at version; do
|
||||||
|
[ -n "$version" ] || continue
|
||||||
|
if grep -Fqx "$version" "$keep_file"; then
|
||||||
|
echo "Keeping release image: $package_name:$version"
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
curl --fail --silent --show-error --retry 3 --retry-all-errors \
|
||||||
|
--user "$REGISTRY_USERNAME:$REGISTRY_PASSWORD" \
|
||||||
|
--request DELETE \
|
||||||
|
"${api_base}/${owner}/container/${package_name}/${version}"
|
||||||
|
echo "Deleted superseded release image: $package_name:$version"
|
||||||
|
deleted=$((deleted + 1))
|
||||||
|
done < <(sort -r "$versions_file")
|
||||||
|
|
||||||
|
echo "Release retention complete: package=$package_name retained=$keep_count deleted=$deleted"
|
||||||
|
|||||||
@@ -1,3 +1,14 @@
|
|||||||
# ci-templates
|
# ci-templates
|
||||||
|
|
||||||
Reusable offline-safe CI/CD workflows
|
Reusable offline-safe CI/CD workflows.
|
||||||
|
|
||||||
|
## Container release retention
|
||||||
|
|
||||||
|
After a tagged release passes candidate verification and is promoted to Dev, the
|
||||||
|
container workflow keeps the current immutable `sha-<commit>` image and the most
|
||||||
|
recent previous release by default. Set `release_retention_count` on the caller
|
||||||
|
to retain a different positive number.
|
||||||
|
|
||||||
|
Retention only matches versions in `sha-` followed by a 40-character commit
|
||||||
|
hash. Registry versions named `sha256:*`, `ci-cache`, or anything else are never
|
||||||
|
selected for deletion by this step.
|
||||||
|
|||||||
Reference in New Issue
Block a user