#!/usr/bin/env bash set -euo pipefail KEEP_COUNT="${GITEA_PACKAGE_KEEP_COUNT:-3}" API_BASE_URL="${GITEA_API_BASE_URL:-https://gitea.banlv-ai.com/api/v1}" TOKEN="${GITEA_PACKAGE_TOKEN:-}" REGISTRY_IMAGE="${REGISTRY_IMAGE:-${IMAGE:-}}" DEPLOY_ENV="${DEPLOY_ENV:-}" CURRENT_IMAGE_TAG="${IMAGE_VERSION_TAG:-}" if [ -z "$TOKEN" ] && [ -z "${GITEA_PACKAGE_VERSIONS_FILE:-}" ]; then echo "Missing GITEA_PACKAGE_TOKEN" >&2 exit 1 fi if [ -z "$REGISTRY_IMAGE" ] || [ -z "$DEPLOY_ENV" ]; then echo "Missing REGISTRY_IMAGE/IMAGE or DEPLOY_ENV" >&2 exit 1 fi case "$DEPLOY_ENV" in test|prod) ;; *) echo "Unsupported DEPLOY_ENV for package pruning: $DEPLOY_ENV" >&2 exit 1 ;; esac if ! command -v node >/dev/null 2>&1; then echo "node is required for package pruning" >&2 exit 127 fi if ! command -v curl >/dev/null 2>&1; then echo "curl is required for package pruning" >&2 exit 127 fi image_without_tag="${REGISTRY_IMAGE%%:*}" path_without_registry="${image_without_tag#*/}" owner="${path_without_registry%%/*}" package="${path_without_registry#*/}" if [ "$owner" = "$path_without_registry" ] || [ -z "$owner" ] || [ -z "$package" ]; then echo "Could not parse owner/package from REGISTRY_IMAGE: $REGISTRY_IMAGE" >&2 exit 1 fi current_version="${CURRENT_IMAGE_TAG##*:}" version_prefix="${DEPLOY_ENV}-" latest_version="${DEPLOY_ENV}-latest" api_base="${API_BASE_URL%/}" echo "=== prune Gitea container package versions ===" echo "owner: $owner" echo "package: $package" echo "environment: $DEPLOY_ENV" echo "keep count: $KEEP_COUNT" versions_json="$(mktemp)" delete_list="$(mktemp)" cleanup() { rm -f "$versions_json" "$delete_list" } trap cleanup EXIT if [ -n "${GITEA_PACKAGE_VERSIONS_FILE:-}" ]; then cp "$GITEA_PACKAGE_VERSIONS_FILE" "$versions_json" else curl -fsS \ -H "Authorization: token $TOKEN" \ -H "Accept: application/json" \ "$api_base/packages/$owner/container/$package" \ -o "$versions_json" fi KEEP_COUNT="$KEEP_COUNT" \ VERSION_PREFIX="$version_prefix" \ LATEST_VERSION="$latest_version" \ CURRENT_VERSION="$current_version" \ VERSIONS_JSON_FILE="$versions_json" \ node <<'NODE' > "$delete_list" const fs = require("node:fs"); const file = process.env.VERSIONS_JSON_FILE; const keepCount = Number.parseInt(process.env.KEEP_COUNT || "3", 10); const prefix = process.env.VERSION_PREFIX || ""; const latest = process.env.LATEST_VERSION || ""; const current = process.env.CURRENT_VERSION || ""; const raw = JSON.parse(fs.readFileSync(file, "utf8")); const list = Array.isArray(raw) ? raw : Array.isArray(raw.versions) ? raw.versions : []; function versionOf(item) { return String(item.version || item.name || item.tag_name || "").trim(); } function createdAtOf(item) { return String( item.created_at || item.created || item.createdAt || item.published_at || item.updated_at || "", ); } const candidates = list .map((item) => ({ version: versionOf(item), createdAt: createdAtOf(item), })) .filter(({ version }) => { if (!version.startsWith(prefix)) return false; if (version === latest) return false; return true; }) .sort((a, b) => { const bTime = Date.parse(b.createdAt); const aTime = Date.parse(a.createdAt); if (Number.isFinite(bTime) && Number.isFinite(aTime) && bTime !== aTime) { return bTime - aTime; } return b.version.localeCompare(a.version); }); const kept = new Set(candidates.slice(0, Math.max(0, keepCount)).map((item) => item.version)); const toDelete = candidates .slice(Math.max(0, keepCount)) .map((item) => item.version) .filter((version) => version !== current && !kept.has(version)); process.stdout.write(toDelete.join("\n")); if (toDelete.length > 0) process.stdout.write("\n"); NODE if [ ! -s "$delete_list" ]; then echo "No old $DEPLOY_ENV package versions to delete." exit 0 fi echo "Package versions selected for deletion:" sed 's/^/ - /' "$delete_list" if [ "${PRUNE_DRY_RUN:-}" = "1" ]; then echo "Dry run enabled, skip DELETE requests." exit 0 fi while IFS= read -r version; do [ -n "$version" ] || continue encoded_owner="$(node -e 'process.stdout.write(encodeURIComponent(process.argv[1] || ""))' "$owner")" encoded_package="$(node -e 'process.stdout.write(encodeURIComponent(process.argv[1] || ""))' "$package")" encoded_version="$(node -e 'process.stdout.write(encodeURIComponent(process.argv[1] || ""))' "$version")" curl -fsS \ -X DELETE \ -H "Authorization: token $TOKEN" \ "$api_base/packages/$encoded_owner/container/$encoded_package/$encoded_version" \ >/dev/null echo "Deleted package version: $version" done < "$delete_list" echo "=== package prune done ==="