feat: support package name filtering and refactor cleanup flow
This commit is contained in:
@@ -0,0 +1,17 @@
|
|||||||
|
name: CI
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- feat/解決問題
|
||||||
|
pull_request:
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
test:
|
||||||
|
runs-on: ubuntu
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- name: Install jq
|
||||||
|
run: sudo apt-get update && sudo apt-get install -y jq
|
||||||
|
- name: Run shell tests
|
||||||
|
run: bash tests/entrypoint.sh
|
||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
FROM alpine:latest
|
FROM alpine:latest
|
||||||
|
|
||||||
RUN apk add --no-cache --no-check-certificate bash curl jq ca-certificates
|
RUN apk add --no-cache bash curl jq ca-certificates
|
||||||
|
|
||||||
COPY entrypoint.sh /entrypoint.sh
|
COPY entrypoint.sh /entrypoint.sh
|
||||||
|
|
||||||
|
|||||||
@@ -59,3 +59,5 @@ jobs:
|
|||||||
|
|
||||||
- Action 定義: [`action.yaml`](action.yaml)
|
- Action 定義: [`action.yaml`](action.yaml)
|
||||||
- 執行腳本: [`entrypoint.sh`](entrypoint.sh)
|
- 執行腳本: [`entrypoint.sh`](entrypoint.sh)
|
||||||
|
- Shell 測試: [`tests/entrypoint.sh`](tests/entrypoint.sh)
|
||||||
|
- Gitea workflow: [`.gitea/workflows/ci.yaml`](.gitea/workflows/ci.yaml)
|
||||||
|
|||||||
+160
-106
@@ -10,7 +10,12 @@ fail() {
|
|||||||
exit 1
|
exit 1
|
||||||
}
|
}
|
||||||
|
|
||||||
declare -A TARGET_PACKAGES=()
|
trim() {
|
||||||
|
local value="$1"
|
||||||
|
value="${value#"${value%%[![:space:]]*}"}"
|
||||||
|
value="${value%"${value##*[![:space:]]}"}"
|
||||||
|
printf '%s' "$value"
|
||||||
|
}
|
||||||
|
|
||||||
resolve_token() {
|
resolve_token() {
|
||||||
log "Trying token from RUNNER_TOKEN"
|
log "Trying token from RUNNER_TOKEN"
|
||||||
@@ -24,7 +29,7 @@ resolve_token() {
|
|||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
|
|
||||||
resolve_keep_versions() {
|
resolve_keep_count() {
|
||||||
local raw_value="${INPUT_KEEP_COUNT:-2}"
|
local raw_value="${INPUT_KEEP_COUNT:-2}"
|
||||||
|
|
||||||
if [[ -z "${raw_value}" ]]; then
|
if [[ -z "${raw_value}" ]]; then
|
||||||
@@ -39,50 +44,68 @@ resolve_keep_versions() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
resolve_package_names() {
|
resolve_package_names() {
|
||||||
|
# Normalize PACKAGE_NAMES into a unique, newline-separated list.
|
||||||
local raw_value="${INPUT_PACKAGE_NAMES:-}"
|
local raw_value="${INPUT_PACKAGE_NAMES:-}"
|
||||||
local normalized package_name
|
local normalized token
|
||||||
local -a package_name_list
|
local -A seen=()
|
||||||
|
local -a package_names=()
|
||||||
|
|
||||||
if [[ -z "${raw_value}" ]]; then
|
if [[ -z "$(trim "${raw_value}")" ]]; then
|
||||||
fail "Missing PACKAGE_NAMES"
|
fail "Missing PACKAGE_NAMES"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
normalized="${raw_value//$'\n'/,}"
|
normalized="${raw_value//$'\n'/,}"
|
||||||
|
IFS=',' read -r -a tokens <<< "${normalized}"
|
||||||
|
|
||||||
IFS=',' read -r -a package_name_list <<< "${normalized}"
|
for token in "${tokens[@]}"; do
|
||||||
for package_name in "${package_name_list[@]}"; do
|
token="$(trim "${token}")"
|
||||||
package_name="${package_name#"${package_name%%[![:space:]]*}"}"
|
[[ -n "${token}" ]] || continue
|
||||||
package_name="${package_name%"${package_name##*[![:space:]]}"}"
|
|
||||||
[[ -n "${package_name}" ]] || continue
|
if [[ -z "${seen["${token}"]+x}" ]]; then
|
||||||
TARGET_PACKAGES["${package_name}"]=1
|
seen["${token}"]=1
|
||||||
|
package_names+=("${token}")
|
||||||
|
fi
|
||||||
done
|
done
|
||||||
|
|
||||||
if (( ${#TARGET_PACKAGES[@]} == 0 )); then
|
if (( ${#package_names[@]} == 0 )); then
|
||||||
fail "Missing PACKAGE_NAMES"
|
fail "Missing PACKAGE_NAMES"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
printf '%s\n' "${package_names[@]}"
|
||||||
}
|
}
|
||||||
|
|
||||||
init_repo_context() {
|
parse_repo_context() {
|
||||||
local repository="${GITEA_REPOSITORY:-}"
|
# Convert the repository slug into owner/repo parts.
|
||||||
|
local repository="$1"
|
||||||
|
local owner repo
|
||||||
|
|
||||||
|
repository="$(trim "${repository}")"
|
||||||
if [[ -z "${repository}" || "${repository}" != */* ]]; then
|
if [[ -z "${repository}" || "${repository}" != */* ]]; then
|
||||||
fail "Invalid GITEA_REPOSITORY: ${repository:-<empty>}"
|
fail "Invalid GITEA_REPOSITORY: ${repository:-<empty>}"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
REPO_OWNER="${repository%%/*}"
|
owner="${repository%%/*}"
|
||||||
REPO_NAME="${repository#*/}"
|
repo="${repository#*/}"
|
||||||
|
|
||||||
|
if [[ -z "${owner}" || -z "${repo}" || "${repo}" == */* ]]; then
|
||||||
|
fail "Invalid GITEA_REPOSITORY: ${repository}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
printf '%s\t%s\n' "${owner}" "${repo}"
|
||||||
}
|
}
|
||||||
|
|
||||||
api_request() {
|
api_request() {
|
||||||
|
# Perform one HTTP request and return status metadata as TSV.
|
||||||
|
# stdout format: http_code<TAB>status_text<TAB>request_id
|
||||||
local method="$1"
|
local method="$1"
|
||||||
local path="$2"
|
local path="$2"
|
||||||
local url body_file headers_file
|
local body_file="$3"
|
||||||
|
local headers_file="$4"
|
||||||
|
local url http_code status_text request_id status_line
|
||||||
|
|
||||||
url="${GITEA_SERVER_URL%/}${path}"
|
url="${GITEA_SERVER_URL%/}${path}"
|
||||||
body_file="$(mktemp)"
|
|
||||||
headers_file="$(mktemp)"
|
|
||||||
|
|
||||||
if ! API_HTTP_CODE="$(
|
if ! http_code="$(
|
||||||
curl -sS \
|
curl -sS \
|
||||||
-H "Accept: application/json" \
|
-H "Accept: application/json" \
|
||||||
-H "Authorization: token ${RESOLVED_GITEA_TOKEN}" \
|
-H "Authorization: token ${RESOLVED_GITEA_TOKEN}" \
|
||||||
@@ -92,50 +115,55 @@ api_request() {
|
|||||||
-w '%{http_code}' \
|
-w '%{http_code}' \
|
||||||
"${url}"
|
"${url}"
|
||||||
)"; then
|
)"; then
|
||||||
rm -f "${body_file}" "${headers_file}"
|
|
||||||
fail "Request failed: ${method} ${path}"
|
fail "Request failed: ${method} ${path}"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
API_RESPONSE_BODY="$(cat "${body_file}")"
|
status_line="$(head -n 1 "${headers_file}" | tr -d '\r')"
|
||||||
API_RESPONSE_HEADERS="$(cat "${headers_file}")"
|
status_text="$(printf '%s' "${status_line}" | cut -d' ' -f2-)"
|
||||||
API_STATUS_TEXT="$(head -n 1 "${headers_file}" | tr -d '\r' | cut -d' ' -f2-)"
|
request_id="$(
|
||||||
API_REQUEST_ID="$(
|
awk -F': *' 'tolower($1)=="x-gitea-request-id" || tolower($1)=="x-request-id" {value=$2} END {print value}' "${headers_file}" | tr -d '\r'
|
||||||
grep -iE '^(x-gitea-request-id|x-request-id):' "${headers_file}" | tail -n 1 | cut -d':' -f2- | tr -d '\r' | sed 's/^ *//'
|
)"
|
||||||
)" || true
|
|
||||||
rm -f "${body_file}" "${headers_file}"
|
|
||||||
|
|
||||||
if [[ -n "${API_REQUEST_ID}" ]]; then
|
if [[ -n "${request_id}" ]]; then
|
||||||
log "${method} ${path} -> ${API_STATUS_TEXT} request_id=${API_REQUEST_ID}"
|
log "${method} ${path} -> ${status_text} request_id=${request_id}"
|
||||||
else
|
else
|
||||||
log "${method} ${path} -> ${API_STATUS_TEXT}"
|
log "${method} ${path} -> ${status_text}"
|
||||||
fi
|
fi
|
||||||
[[ "${API_HTTP_CODE}" =~ ^2 ]]
|
|
||||||
|
printf '%s\t%s\t%s\n' "${http_code}" "${status_text}" "${request_id}"
|
||||||
}
|
}
|
||||||
|
|
||||||
fetch_all_pages() {
|
fetch_package_versions() {
|
||||||
local base_path="$1"
|
# Fetch and aggregate all package versions for a single package name.
|
||||||
|
# stdout: JSON array of version objects sorted by page order, later sorted by created_at by the caller.
|
||||||
|
local owner="$1"
|
||||||
|
local package_name="$2"
|
||||||
local page=1
|
local page=1
|
||||||
local limit=100
|
local limit=100
|
||||||
local aggregate_file page_file tmp_file page_path page_length
|
local aggregate_file page_file headers_file meta http_code status_text request_id page_length path
|
||||||
|
|
||||||
aggregate_file="$(mktemp)"
|
aggregate_file="$(mktemp)"
|
||||||
printf '[]' > "${aggregate_file}"
|
printf '[]' > "${aggregate_file}"
|
||||||
|
|
||||||
while :; do
|
while :; do
|
||||||
page_path="${base_path}"
|
path="/api/v1/packages/${owner}/nuget/${package_name}?page=${page}&limit=${limit}"
|
||||||
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)"
|
page_file="$(mktemp)"
|
||||||
printf '%s' "${API_RESPONSE_BODY}" > "${page_file}"
|
headers_file="$(mktemp)"
|
||||||
|
meta="$(api_request GET "${path}" "${page_file}" "${headers_file}")"
|
||||||
|
IFS=$'\t' read -r http_code status_text request_id <<< "${meta}"
|
||||||
|
rm -f "${headers_file}"
|
||||||
|
|
||||||
|
if [[ "${http_code}" == "404" ]]; then
|
||||||
|
rm -f "${page_file}" "${aggregate_file}"
|
||||||
|
printf '[]'
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ ! "${http_code}" =~ ^2 ]]; then
|
||||||
|
rm -f "${page_file}" "${aggregate_file}"
|
||||||
|
fail "Unexpected response for package ${package_name}: ${status_text}"
|
||||||
|
fi
|
||||||
|
|
||||||
page_length="$(jq 'length' "${page_file}")"
|
page_length="$(jq 'length' "${page_file}")"
|
||||||
|
|
||||||
tmp_file="$(mktemp)"
|
tmp_file="$(mktemp)"
|
||||||
@@ -155,89 +183,110 @@ fetch_all_pages() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
collect_package_candidates() {
|
collect_package_candidates() {
|
||||||
local packages_json group_json package_name total_versions candidates_json
|
# Build the delete candidate file for the requested package names.
|
||||||
|
# stdout: package_count<TAB>total_version_count<TAB>kept_count<TAB>candidate_count
|
||||||
|
local owner="$1"
|
||||||
|
local keep_count="$2"
|
||||||
|
local candidate_file="$3"
|
||||||
|
shift 3
|
||||||
|
local -a package_names=("$@")
|
||||||
|
local package_name versions_json total_versions candidates_json
|
||||||
|
local package_count=0
|
||||||
|
local total_version_count=0
|
||||||
|
local kept_count=0
|
||||||
|
local candidate_count=0
|
||||||
|
|
||||||
packages_json="$(
|
: > "${candidate_file}"
|
||||||
fetch_all_pages "/api/v1/packages/${REPO_OWNER}?type=nuget"
|
|
||||||
)"
|
|
||||||
|
|
||||||
if [[ "$(jq 'length' <<<"${packages_json}")" -eq 0 ]]; then
|
for package_name in "${package_names[@]}"; do
|
||||||
log "No nuget packages found for owner ${REPO_OWNER}"
|
versions_json="$(fetch_package_versions "${owner}" "${package_name}")"
|
||||||
return 0
|
|
||||||
fi
|
|
||||||
|
|
||||||
while IFS= read -r group_json; do
|
if [[ "$(jq 'length' <<<"${versions_json}")" -eq 0 ]]; then
|
||||||
package_name="$(jq -r '.[0].name' <<<"${group_json}")"
|
log "No versions found for package ${package_name}"
|
||||||
|
|
||||||
if [[ -z "${TARGET_PACKAGES["$package_name"]+x}" ]]; then
|
|
||||||
continue
|
continue
|
||||||
fi
|
fi
|
||||||
|
|
||||||
total_versions="$(jq 'length' <<<"${group_json}")"
|
package_count=$((package_count + 1))
|
||||||
|
total_versions="$(jq 'length' <<<"${versions_json}")"
|
||||||
|
total_version_count=$((total_version_count + total_versions))
|
||||||
|
|
||||||
PACKAGE_COUNT=$((PACKAGE_COUNT + 1))
|
log "Package ${package_name}: total_versions=${total_versions} keep_count=${keep_count}"
|
||||||
TOTAL_VERSION_COUNT=$((TOTAL_VERSION_COUNT + total_versions))
|
|
||||||
|
|
||||||
log "Package ${package_name}: total_versions=${total_versions} keep_count=${keep_versions}"
|
|
||||||
log "Package ${package_name} versions (oldest -> newest):"
|
log "Package ${package_name} versions (oldest -> newest):"
|
||||||
while IFS=$'\t' read -r version created_at; do
|
while IFS=$'\t' read -r version created_at; do
|
||||||
[[ -z "${version}" ]] && continue
|
[[ -z "${version}" ]] && continue
|
||||||
log " - ${version} (${created_at})"
|
log " - ${version} (${created_at})"
|
||||||
done < <(jq -r 'sort_by(.created_at)[] | [.version, .created_at] | @tsv' <<<"${group_json}")
|
done < <(jq -r 'sort_by(.created_at)[] | [.version, .created_at] | @tsv' <<<"${versions_json}")
|
||||||
|
|
||||||
if (( total_versions <= keep_versions )); then
|
if (( total_versions <= keep_count )); then
|
||||||
log " keep all ${total_versions} versions"
|
log " keep all ${total_versions} versions"
|
||||||
KEPT_COUNT=$((KEPT_COUNT + total_versions))
|
kept_count=$((kept_count + total_versions))
|
||||||
continue
|
continue
|
||||||
fi
|
fi
|
||||||
|
|
||||||
KEPT_COUNT=$((KEPT_COUNT + keep_versions))
|
kept_count=$((kept_count + keep_count))
|
||||||
candidates_json="$(
|
candidates_json="$(
|
||||||
jq -c --argjson keep "${keep_versions}" \
|
jq -c --argjson keep "${keep_count}" \
|
||||||
'sort_by(.created_at) | .[0:(length - $keep)]' <<<"${group_json}"
|
'sort_by(.created_at) | .[0:(length - $keep)]' <<<"${versions_json}"
|
||||||
)"
|
)"
|
||||||
|
|
||||||
while IFS=$'\t' read -r name version created_at; do
|
while IFS=$'\t' read -r package version created_at; do
|
||||||
[[ -z "${name}" ]] && continue
|
[[ -z "${package}" ]] && continue
|
||||||
log "Candidate to delete: package ${name} version ${version} (created: ${created_at})"
|
log "Candidate to delete: package ${package} version ${version} (created: ${created_at})"
|
||||||
printf '%s\t%s\t%s\n' "${name}" "${version}" "${created_at}" >> "${CANDIDATES_FILE}"
|
printf '%s\t%s\t%s\n' "${package}" "${version}" "${created_at}" >> "${candidate_file}"
|
||||||
CANDIDATE_COUNT=$((CANDIDATE_COUNT + 1))
|
candidate_count=$((candidate_count + 1))
|
||||||
done < <(jq -r '.[] | [.name, .version, .created_at] | @tsv' <<<"${candidates_json}")
|
done < <(jq -r '.[] | [.name, .version, .created_at] | @tsv' <<<"${candidates_json}")
|
||||||
done < <(jq -c 'group_by(.name)[]' <<<"${packages_json}")
|
done
|
||||||
|
|
||||||
|
printf '%s\t%s\t%s\t%s\n' "${package_count}" "${total_version_count}" "${kept_count}" "${candidate_count}"
|
||||||
}
|
}
|
||||||
|
|
||||||
process_candidates() {
|
process_candidates() {
|
||||||
local name version created_at
|
# Delete each queued version and summarize the result.
|
||||||
|
local owner="$1"
|
||||||
|
local candidate_file="$2"
|
||||||
|
local package_count="$3"
|
||||||
|
local total_version_count="$4"
|
||||||
|
local kept_count="$5"
|
||||||
|
local candidate_count="$6"
|
||||||
local deleted_count=0
|
local deleted_count=0
|
||||||
local error_count=0
|
local error_count=0
|
||||||
|
local package_name version created_at
|
||||||
|
local body_file headers_file meta http_code status_text request_id
|
||||||
|
|
||||||
if [[ ! -s "${CANDIDATES_FILE}" ]]; then
|
if [[ ! -s "${candidate_file}" ]]; then
|
||||||
log "No delete candidates found"
|
log "No delete candidates found"
|
||||||
log "Summary: packages=${PACKAGE_COUNT} versions=${TOTAL_VERSION_COUNT} kept=${KEPT_COUNT} candidates=0 deleted=0 errors=0"
|
log "Summary: packages=${package_count} versions=${total_version_count} kept=${kept_count} candidates=0 deleted=0 errors=0"
|
||||||
return 0
|
return 0
|
||||||
fi
|
fi
|
||||||
|
|
||||||
while IFS=$'\t' read -r name version created_at; do
|
while IFS=$'\t' read -r package_name version created_at; do
|
||||||
[[ -z "${name}" ]] && continue
|
[[ -z "${package_name}" ]] && continue
|
||||||
|
|
||||||
if api_request DELETE "/api/v1/packages/${REPO_OWNER}/nuget/${name}/${version}"; then
|
body_file="$(mktemp)"
|
||||||
log "Deleted package ${name} version ${version} -> ${API_STATUS_TEXT}"
|
headers_file="$(mktemp)"
|
||||||
|
meta="$(api_request DELETE "/api/v1/packages/${owner}/nuget/${package_name}/${version}" "${body_file}" "${headers_file}")"
|
||||||
|
IFS=$'\t' read -r http_code status_text request_id <<< "${meta}"
|
||||||
|
rm -f "${body_file}" "${headers_file}"
|
||||||
|
|
||||||
|
if [[ "${http_code}" =~ ^2 ]]; then
|
||||||
|
log "Deleted package ${package_name} version ${version} -> ${status_text}"
|
||||||
deleted_count=$((deleted_count + 1))
|
deleted_count=$((deleted_count + 1))
|
||||||
else
|
else
|
||||||
if [[ -n "${API_REQUEST_ID}" ]]; then
|
if [[ -n "${request_id}" ]]; then
|
||||||
log "ERROR: DELETE package ${name} version ${version} -> ${API_STATUS_TEXT} request_id=${API_REQUEST_ID}"
|
log "ERROR: DELETE package ${package_name} version ${version} -> ${status_text} request_id=${request_id}"
|
||||||
else
|
else
|
||||||
log "ERROR: DELETE package ${name} version ${version} -> ${API_STATUS_TEXT}"
|
log "ERROR: DELETE package ${package_name} version ${version} -> ${status_text}"
|
||||||
fi
|
fi
|
||||||
error_count=$((error_count + 1))
|
error_count=$((error_count + 1))
|
||||||
fi
|
fi
|
||||||
done < "${CANDIDATES_FILE}"
|
done < "${candidate_file}"
|
||||||
|
|
||||||
log "Summary: packages=${PACKAGE_COUNT} versions=${TOTAL_VERSION_COUNT} kept=${KEPT_COUNT} candidates=${CANDIDATE_COUNT} deleted=${deleted_count} errors=${error_count}"
|
log "Summary: packages=${package_count} versions=${total_version_count} kept=${kept_count} candidates=${candidate_count} deleted=${deleted_count} errors=${error_count}"
|
||||||
}
|
}
|
||||||
|
|
||||||
main() {
|
main() {
|
||||||
local token keep_versions
|
local token keep_count repository owner repo package_names_csv
|
||||||
|
local candidate_file summary package_count total_version_count kept_count candidate_count
|
||||||
|
|
||||||
log "Gitea Server Url: ${GITEA_SERVER_URL:-}"
|
log "Gitea Server Url: ${GITEA_SERVER_URL:-}"
|
||||||
log "Gitea Repository: ${GITEA_REPOSITORY:-}"
|
log "Gitea Repository: ${GITEA_REPOSITORY:-}"
|
||||||
@@ -247,27 +296,32 @@ main() {
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
export RESOLVED_GITEA_TOKEN="$token"
|
export RESOLVED_GITEA_TOKEN="$token"
|
||||||
init_repo_context
|
|
||||||
keep_versions="$(resolve_keep_versions)"
|
repository="${GITEA_REPOSITORY:-}"
|
||||||
log "keep_count=${keep_versions}"
|
IFS=$'\t' read -r owner repo <<< "$(parse_repo_context "${repository}")"
|
||||||
resolve_package_names
|
keep_count="$(resolve_keep_count)"
|
||||||
log "package_names=${INPUT_PACKAGE_NAMES}"
|
|
||||||
|
mapfile -t package_names < <(resolve_package_names)
|
||||||
|
package_names_csv="$(IFS=,; echo "${package_names[*]}")"
|
||||||
|
|
||||||
|
log "keep_count=${keep_count}"
|
||||||
|
log "package_names=${package_names_csv}"
|
||||||
log "Token source resolved successfully"
|
log "Token source resolved successfully"
|
||||||
|
|
||||||
CANDIDATES_FILE="$(mktemp)"
|
candidate_file="$(mktemp)"
|
||||||
export CANDIDATES_FILE
|
trap "rm -f -- '${candidate_file}'" EXIT
|
||||||
PACKAGE_COUNT=0
|
|
||||||
TOTAL_VERSION_COUNT=0
|
|
||||||
KEPT_COUNT=0
|
|
||||||
CANDIDATE_COUNT=0
|
|
||||||
trap 'rm -f "${CANDIDATES_FILE}"' EXIT
|
|
||||||
|
|
||||||
collect_package_candidates
|
summary="$(collect_package_candidates "${owner}" "${keep_count}" "${candidate_file}" "${package_names[@]}")"
|
||||||
if (( PACKAGE_COUNT == 0 )); then
|
IFS=$'\t' read -r package_count total_version_count kept_count candidate_count <<< "${summary}"
|
||||||
|
|
||||||
|
if (( package_count == 0 )); then
|
||||||
log "No matching packages found for requested package_names"
|
log "No matching packages found for requested package_names"
|
||||||
fi
|
fi
|
||||||
process_candidates
|
|
||||||
|
process_candidates "${owner}" "${candidate_file}" "${package_count}" "${total_version_count}" "${kept_count}" "${candidate_count}"
|
||||||
log "Stage 4 complete"
|
log "Stage 4 complete"
|
||||||
}
|
}
|
||||||
|
|
||||||
main "$@"
|
if [[ "${BASH_SOURCE[0]}" == "$0" ]]; then
|
||||||
|
main "$@"
|
||||||
|
fi
|
||||||
|
|||||||
@@ -0,0 +1,223 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||||
|
TMP_DIR="$(mktemp -d)"
|
||||||
|
trap 'rm -rf "${TMP_DIR}"' EXIT
|
||||||
|
|
||||||
|
assert_eq() {
|
||||||
|
local expected="$1"
|
||||||
|
local actual="$2"
|
||||||
|
|
||||||
|
if [[ "${expected}" != "${actual}" ]]; then
|
||||||
|
printf 'Expected:\n%s\nActual:\n%s\n' "${expected}" "${actual}" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
expect_fail() {
|
||||||
|
if ( "$@" ) >/dev/null 2>&1; then
|
||||||
|
printf 'Expected failure: %s\n' "$*" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
write_mock_curl_pagination() {
|
||||||
|
cat > "${TMP_DIR}/curl" <<'EOF'
|
||||||
|
#!/bin/sh
|
||||||
|
make_versions_json() {
|
||||||
|
prefix="$1"
|
||||||
|
start="$2"
|
||||||
|
count="$3"
|
||||||
|
i=0
|
||||||
|
printf '['
|
||||||
|
while [ "$i" -lt "$count" ]; do
|
||||||
|
idx=$((start + i))
|
||||||
|
[ "$i" -gt 0 ] && printf ','
|
||||||
|
printf '{"name":"%s","version":"%s.%s.0","created_at":"2023-01-%02dT00:00:00Z"}' \
|
||||||
|
"$prefix" "$idx" "$idx" "$((idx + 1))"
|
||||||
|
i=$((i + 1))
|
||||||
|
done
|
||||||
|
printf ']'
|
||||||
|
}
|
||||||
|
|
||||||
|
method=GET
|
||||||
|
out_file=
|
||||||
|
headers_file=
|
||||||
|
url=
|
||||||
|
while [ "$#" -gt 0 ]; do
|
||||||
|
case "$1" in
|
||||||
|
-X) method=$2; shift 2 ;;
|
||||||
|
-D) headers_file=$2; shift 2 ;;
|
||||||
|
-o) out_file=$2; shift 2 ;;
|
||||||
|
-w) shift 2 ;;
|
||||||
|
-H|-sS) shift 1 ;;
|
||||||
|
*) url=$1; shift ;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
body='[]'
|
||||||
|
status='HTTP/1.1 200 OK'
|
||||||
|
case "$url" in
|
||||||
|
*'/api/v1/packages/test-owner/nuget/pkg-a?page=1&limit=100')
|
||||||
|
body="$(make_versions_json pkg-a 0 100)"
|
||||||
|
;;
|
||||||
|
*'/api/v1/packages/test-owner/nuget/pkg-a?page=2&limit=100')
|
||||||
|
body="$(make_versions_json pkg-a 100 1)"
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
printf '%s' "$body" > "$out_file"
|
||||||
|
{
|
||||||
|
printf '%s\n' "$status"
|
||||||
|
printf 'X-Request-Id: req-page\n'
|
||||||
|
} > "$headers_file"
|
||||||
|
|
||||||
|
printf '%s' 200
|
||||||
|
EOF
|
||||||
|
chmod +x "${TMP_DIR}/curl"
|
||||||
|
}
|
||||||
|
|
||||||
|
write_mock_curl_collect() {
|
||||||
|
cat > "${TMP_DIR}/curl" <<'EOF'
|
||||||
|
#!/bin/sh
|
||||||
|
method=GET
|
||||||
|
out_file=
|
||||||
|
headers_file=
|
||||||
|
url=
|
||||||
|
while [ "$#" -gt 0 ]; do
|
||||||
|
case "$1" in
|
||||||
|
-X) method=$2; shift 2 ;;
|
||||||
|
-D) headers_file=$2; shift 2 ;;
|
||||||
|
-o) out_file=$2; shift 2 ;;
|
||||||
|
-w) shift 2 ;;
|
||||||
|
-H|-sS) shift 1 ;;
|
||||||
|
*) url=$1; shift ;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
body='[]'
|
||||||
|
status='HTTP/1.1 200 OK'
|
||||||
|
code='200'
|
||||||
|
case "$url" in
|
||||||
|
*'/api/v1/packages/test-owner/nuget/pkg-a?page=1&limit=100')
|
||||||
|
body='[{"name":"pkg-a","version":"1.0.0","created_at":"2023-01-01T00:00:00Z"},{"name":"pkg-a","version":"1.1.0","created_at":"2023-01-02T00:00:00Z"},{"name":"pkg-a","version":"1.2.0","created_at":"2023-01-03T00:00:00Z"}]'
|
||||||
|
;;
|
||||||
|
*'/api/v1/packages/test-owner/nuget/pkg-b?page=1&limit=100')
|
||||||
|
code='404'
|
||||||
|
status='HTTP/1.1 404 Not Found'
|
||||||
|
body=''
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
printf '%s' "$body" > "$out_file"
|
||||||
|
{
|
||||||
|
printf '%s\n' "$status"
|
||||||
|
printf 'X-Request-Id: req-collect\n'
|
||||||
|
} > "$headers_file"
|
||||||
|
|
||||||
|
printf '%s' "$code"
|
||||||
|
EOF
|
||||||
|
chmod +x "${TMP_DIR}/curl"
|
||||||
|
}
|
||||||
|
|
||||||
|
write_mock_curl_delete() {
|
||||||
|
cat > "${TMP_DIR}/curl" <<'EOF'
|
||||||
|
#!/bin/sh
|
||||||
|
method=GET
|
||||||
|
out_file=
|
||||||
|
headers_file=
|
||||||
|
url=
|
||||||
|
while [ "$#" -gt 0 ]; do
|
||||||
|
case "$1" in
|
||||||
|
-X) method=$2; shift 2 ;;
|
||||||
|
-D) headers_file=$2; shift 2 ;;
|
||||||
|
-o) out_file=$2; shift 2 ;;
|
||||||
|
-w) shift 2 ;;
|
||||||
|
-H|-sS) shift 1 ;;
|
||||||
|
*) url=$1; shift ;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
code='200'
|
||||||
|
status='HTTP/1.1 200 OK'
|
||||||
|
body='[]'
|
||||||
|
case "$url" in
|
||||||
|
*'/api/v1/packages/test-owner/nuget/pkg-a?page=1&limit=100')
|
||||||
|
body='[{"name":"pkg-a","version":"1.0.0","created_at":"2023-01-01T00:00:00Z"},{"name":"pkg-a","version":"0.9.0","created_at":"2022-12-31T00:00:00Z"}]'
|
||||||
|
;;
|
||||||
|
*'/api/v1/packages/test-owner/nuget/pkg-a/1.0.0')
|
||||||
|
if [ "$method" = "DELETE" ]; then
|
||||||
|
code='204'
|
||||||
|
status='HTTP/1.1 204 No Content'
|
||||||
|
body=''
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
*'/api/v1/packages/test-owner/nuget/pkg-a/0.9.0')
|
||||||
|
if [ "$method" = "DELETE" ]; then
|
||||||
|
code='404'
|
||||||
|
status='HTTP/1.1 404 Not Found'
|
||||||
|
body=''
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
printf '%s' "$body" > "$out_file"
|
||||||
|
{
|
||||||
|
printf '%s\n' "$status"
|
||||||
|
printf 'X-Request-Id: req-delete\n'
|
||||||
|
} > "$headers_file"
|
||||||
|
|
||||||
|
if [ "$method" = "DELETE" ] && [ -n "${MOCK_DELETE_LOG:-}" ]; then
|
||||||
|
printf '%s %s\n' "$method" "$url" >> "$MOCK_DELETE_LOG"
|
||||||
|
fi
|
||||||
|
|
||||||
|
printf '%s' "$code"
|
||||||
|
EOF
|
||||||
|
chmod +x "${TMP_DIR}/curl"
|
||||||
|
}
|
||||||
|
|
||||||
|
source "${ROOT_DIR}/entrypoint.sh"
|
||||||
|
|
||||||
|
assert_eq "abc" "$(RUNNER_TOKEN=abc resolve_token)"
|
||||||
|
expect_fail env -u RUNNER_TOKEN bash -lc "source '${ROOT_DIR}/entrypoint.sh'; resolve_token"
|
||||||
|
|
||||||
|
assert_eq "5" "$(INPUT_KEEP_COUNT=5 resolve_keep_count)"
|
||||||
|
expect_fail env INPUT_KEEP_COUNT=abc bash -lc "source '${ROOT_DIR}/entrypoint.sh'; resolve_keep_count"
|
||||||
|
|
||||||
|
output="$(INPUT_PACKAGE_NAMES=$' pkg-a, pkg-b\npkg-a , pkg-c ' resolve_package_names)"
|
||||||
|
assert_eq $'pkg-a\npkg-b\npkg-c' "${output}"
|
||||||
|
expect_fail env INPUT_PACKAGE_NAMES=' , ' bash -lc "source '${ROOT_DIR}/entrypoint.sh'; resolve_package_names"
|
||||||
|
|
||||||
|
IFS=$'\t' read -r owner repo <<< "$(parse_repo_context "owner/repo")"
|
||||||
|
assert_eq "owner" "${owner}"
|
||||||
|
assert_eq "repo" "${repo}"
|
||||||
|
expect_fail bash -lc "source '${ROOT_DIR}/entrypoint.sh'; parse_repo_context 'repo'"
|
||||||
|
|
||||||
|
write_mock_curl_pagination
|
||||||
|
output="$(PATH="${TMP_DIR}:$PATH" GITEA_SERVER_URL="https://gitea.example.com" RESOLVED_GITEA_TOKEN="token" \
|
||||||
|
fetch_package_versions "test-owner" "pkg-a")"
|
||||||
|
assert_eq "101" "$(jq 'length' <<<"${output}")"
|
||||||
|
assert_eq "0.0.0" "$(jq -r '.[0].version' <<<"${output}")"
|
||||||
|
assert_eq "100.100.0" "$(jq -r '.[100].version' <<<"${output}")"
|
||||||
|
|
||||||
|
write_mock_curl_collect
|
||||||
|
summary="$(PATH="${TMP_DIR}:$PATH" GITEA_SERVER_URL="https://gitea.example.com" RESOLVED_GITEA_TOKEN="token" \
|
||||||
|
collect_package_candidates "test-owner" "1" "${TMP_DIR}/candidates.tsv" "pkg-a" "pkg-b")"
|
||||||
|
IFS=$'\t' read -r package_count total_version_count kept_count candidate_count <<< "${summary}"
|
||||||
|
assert_eq "1" "${package_count}"
|
||||||
|
assert_eq "3" "${total_version_count}"
|
||||||
|
assert_eq "1" "${kept_count}"
|
||||||
|
assert_eq "2" "${candidate_count}"
|
||||||
|
assert_eq "2" "$(wc -l < "${TMP_DIR}/candidates.tsv" | tr -d ' ')"
|
||||||
|
|
||||||
|
write_mock_curl_delete
|
||||||
|
candidate_file="${TMP_DIR}/delete.tsv"
|
||||||
|
cat > "${candidate_file}" <<'EOF'
|
||||||
|
pkg-a 1.0.0 2023-01-01T00:00:00Z
|
||||||
|
pkg-a 0.9.0 2022-12-31T00:00:00Z
|
||||||
|
EOF
|
||||||
|
output="$(MOCK_DELETE_LOG="${TMP_DIR}/delete.log" PATH="${TMP_DIR}:$PATH" GITEA_SERVER_URL="https://gitea.example.com" RESOLVED_GITEA_TOKEN="token" \
|
||||||
|
process_candidates "test-owner" "${candidate_file}" "1" "2" "1" "2" 2>&1)"
|
||||||
|
assert_eq "1" "$(grep -c '^DELETE ' "${TMP_DIR}/delete.log")"
|
||||||
|
assert_eq "Summary: packages=1 versions=2 kept=1 candidates=2 deleted=1 errors=1" "$(printf '%s' "${output}" | tail -n 1)"
|
||||||
Reference in New Issue
Block a user