Files
cleanup-nuget/entrypoint.sh
T

251 lines
6.3 KiB
Bash
Executable File

#!/bin/bash
set -euo pipefail
log() {
printf '%s\n' "$*" >&2
}
fail() {
log "ERROR: $*"
exit 1
}
resolve_token() {
local source_name env_value
local sources=(
"inputs.RUNNER_TOKEN:INPUT_RUNNER_TOKEN"
"secrets.GITEA_TOKEN:GITEA_TOKEN"
"secrets.RUNNER_TOKEN:RUNNER_TOKEN_SECRET"
)
for source in "${sources[@]}"; do
source_name="${source%%:*}"
env_value="${source#*:}"
log "Trying token from ${source_name}"
if [[ -n "${!env_value:-}" ]]; then
log "Using token from ${source_name}"
printf '%s' "${!env_value}"
return 0
fi
log "Token not found in ${source_name}, trying next source"
done
return 1
}
resolve_keep_versions() {
local raw_value="${INPUT_KEEP_VERSIONS:-2}"
if [[ -z "${raw_value}" ]]; then
raw_value="2"
fi
if [[ ! "${raw_value}" =~ ^[0-9]+$ ]]; then
fail "Invalid keep_versions: ${raw_value}"
fi
printf '%s' "${raw_value}"
}
resolve_dry_run() {
local raw_value="${INPUT_DRY_RUN:-true}"
case "${raw_value,,}" in
true|1|yes|on)
printf 'true'
;;
false|0|no|off)
printf 'false'
;;
*)
fail "Invalid dry_run: ${raw_value}"
;;
esac
}
init_repo_context() {
local repository="${GITEA_REPOSITORY:-}"
if [[ -z "${repository}" || "${repository}" != */* ]]; then
fail "Invalid GITEA_REPOSITORY: ${repository:-<empty>}"
fi
REPO_OWNER="${repository%%/*}"
REPO_NAME="${repository#*/}"
}
api_request() {
local method="$1"
local path="$2"
local url body_file headers_file
url="${GITEA_SERVER_URL%/}${path}"
body_file="$(mktemp)"
headers_file="$(mktemp)"
if ! API_HTTP_CODE="$(
curl -sS \
-H "Accept: application/json" \
-H "Authorization: token ${RESOLVED_GITEA_TOKEN}" \
-X "${method}" \
-D "${headers_file}" \
-o "${body_file}" \
-w '%{http_code}' \
"${url}"
)"; then
rm -f "${body_file}" "${headers_file}"
fail "Request failed: ${method} ${path}"
fi
API_RESPONSE_BODY="$(cat "${body_file}")"
API_RESPONSE_HEADERS="$(cat "${headers_file}")"
rm -f "${body_file}" "${headers_file}"
log "${method} ${path} -> ${API_HTTP_CODE}"
[[ "${API_HTTP_CODE}" =~ ^2 ]]
}
fetch_all_pages() {
local base_path="$1"
local page=1
local limit=100
local aggregate_file page_file tmp_file page_path page_length
aggregate_file="$(mktemp)"
printf '[]' > "${aggregate_file}"
while :; do
page_path="${base_path}"
if [[ "${page_path}" == *\?* ]]; then
page_path="${page_path}&page=${page}&limit=${limit}"
else
page_path="${page_path}?page=${page}&limit=${limit}"
fi
if ! api_request GET "${page_path}"; then
rm -f "${aggregate_file}"
fail "Unexpected response for ${page_path}"
fi
page_file="$(mktemp)"
printf '%s' "${API_RESPONSE_BODY}" > "${page_file}"
page_length="$(jq 'length' "${page_file}")"
tmp_file="$(mktemp)"
jq -s '.[0] + .[1]' "${aggregate_file}" "${page_file}" > "${tmp_file}"
mv "${tmp_file}" "${aggregate_file}"
rm -f "${page_file}"
if (( page_length < limit )); then
break
fi
page=$((page + 1))
done
cat "${aggregate_file}"
rm -f "${aggregate_file}"
}
collect_package_candidates() {
local packages_json group_json package_name total_versions candidates_json
packages_json="$(
fetch_all_pages "/api/v1/packages/${REPO_OWNER}?type=nuget"
)"
if [[ "$(jq 'length' <<<"${packages_json}")" -eq 0 ]]; then
log "No nuget packages found for owner ${REPO_OWNER}"
return 0
fi
while IFS= read -r group_json; do
package_name="$(jq -r '.[0].name' <<<"${group_json}")"
total_versions="$(jq 'length' <<<"${group_json}")"
log "Package ${package_name} versions (oldest -> newest):"
while IFS=$'\t' read -r version created_at; do
[[ -z "${version}" ]] && continue
log " - ${version} (${created_at})"
done < <(jq -r 'sort_by(.created_at)[] | [.version, .created_at] | @tsv' <<<"${group_json}")
if (( total_versions <= keep_versions )); then
log " keep all ${total_versions} versions"
continue
fi
candidates_json="$(
jq -c --argjson keep "${keep_versions}" \
'sort_by(.created_at) | .[0:(length - $keep)]' <<<"${group_json}"
)"
while IFS=$'\t' read -r name version created_at; do
[[ -z "${name}" ]] && continue
log "Candidate to delete: package ${name} version ${version} (created: ${created_at})"
printf '%s\t%s\t%s\n' "${name}" "${version}" "${created_at}" >> "${CANDIDATES_FILE}"
done < <(jq -r '.[] | [.name, .version, .created_at] | @tsv' <<<"${candidates_json}")
done < <(jq -c 'group_by(.name)[]' <<<"${packages_json}")
}
process_candidates() {
local dry_run="$1"
local name version created_at
local deleted_count=0
local skipped_count=0
local error_count=0
if [[ ! -s "${CANDIDATES_FILE}" ]]; then
log "No delete candidates found"
return 0
fi
while IFS=$'\t' read -r name version created_at; do
[[ -z "${name}" ]] && continue
if [[ "${dry_run}" == "true" ]]; then
log "[DRY-RUN] Would delete package ${name} version ${version} (created: ${created_at})"
skipped_count=$((skipped_count + 1))
continue
fi
if api_request DELETE "/api/v1/packages/${REPO_OWNER}/nuget/${name}/${version}"; then
log "Deleted package ${name} version ${version} -> ${API_HTTP_CODE}"
deleted_count=$((deleted_count + 1))
else
log "ERROR: DELETE package ${name} version ${version} -> ${API_HTTP_CODE}"
error_count=$((error_count + 1))
fi
done < "${CANDIDATES_FILE}"
log "Summary: deleted=${deleted_count} kept=${keep_versions} candidates=$(wc -l < "${CANDIDATES_FILE}") skipped=${skipped_count} errors=${error_count} dry_run=${dry_run}"
}
main() {
local token keep_versions dry_run
log "Gitea Server Url: ${GITEA_SERVER_URL:-}"
log "Gitea Repository: ${GITEA_REPOSITORY:-}"
if ! token="$(resolve_token)"; then
fail "No Gitea token available, exiting"
fi
export RESOLVED_GITEA_TOKEN="$token"
init_repo_context
keep_versions="$(resolve_keep_versions)"
dry_run="$(resolve_dry_run)"
log "keep_versions=${keep_versions}"
log "dry_run=${dry_run}"
log "Token source resolved successfully"
CANDIDATES_FILE="$(mktemp)"
export CANDIDATES_FILE
trap 'rm -f "${CANDIDATES_FILE}"' EXIT
collect_package_candidates
process_candidates "${dry_run}"
log "Stage 4 complete"
}
main "$@"