From 789d08a0a638fcebd2ec9f32e9a1cfe2d60e4f8a Mon Sep 17 00:00:00 2001 From: Jeffery Date: Mon, 1 Dec 2025 14:32:01 +0800 Subject: [PATCH 1/2] =?UTF-8?q?feat:=20=E5=84=AA=E5=8C=96=E7=A8=8B?= =?UTF-8?q?=E5=BA=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- cleanup-images.sh | 95 ++++++++++++++++++++++++++++--------------- cleanup-releases.sh | 98 ++++++++++++++++++++++++++++----------------- 2 files changed, 123 insertions(+), 70 deletions(-) diff --git a/cleanup-images.sh b/cleanup-images.sh index dfa5e78..69cc2eb 100644 --- a/cleanup-images.sh +++ b/cleanup-images.sh @@ -1,13 +1,20 @@ #!/bin/bash -set -e +set -euo pipefail GITEA_SERVER="$1" REPOSITORY="$2" PACKAGE_NAME="$3" TOKEN="$4" KEEP_COUNT="$5" -DRY_RUN="$6" +DRY_RUN="${6:-false}" + +# 參數驗證 +if [[ -z "$GITEA_SERVER" ]] || [[ -z "$REPOSITORY" ]] || [[ -z "$PACKAGE_NAME" ]] || [[ -z "$TOKEN" ]] || [[ -z "$KEEP_COUNT" ]]; then + echo "❌ 錯誤:缺少必要參數" + echo "使用方式: $0 [dry-run]" + exit 1 +fi echo "開始清理舊映像,保留最新 $KEEP_COUNT 個版本" @@ -18,26 +25,35 @@ echo "Owner: $OWNER" echo "Package: $PACKAGE_NAME" # 獲取所有 container 版本並按創建時間排序 -VERSIONS_JSON=$(curl -s "$GITEA_SERVER/api/v1/packages/$OWNER/container/$PACKAGE_NAME?token=$TOKEN" \ +echo "正在獲取映像版本列表..." +VERSIONS_JSON=$(curl -s -w "\nHTTP_STATUS:%{http_code}" \ + "$GITEA_SERVER/api/v1/packages/$OWNER/container/$PACKAGE_NAME?token=$TOKEN" \ -H "Accept: application/json") +HTTP_STATUS=$(echo "$VERSIONS_JSON" | grep -o "HTTP_STATUS:[0-9]*" | cut -d: -f2) +VERSIONS_JSON=$(echo "$VERSIONS_JSON" | sed '/HTTP_STATUS:/d') + # 檢查是否成功獲取版本列表 -if [ $? -ne 0 ] || [ -z "$VERSIONS_JSON" ]; then - echo "錯誤:無法獲取 container 版本列表" +if [[ "$HTTP_STATUS" != "200" ]] || [[ -z "$VERSIONS_JSON" ]] || [[ "$VERSIONS_JSON" == "null" ]]; then + echo "❌ 錯誤:無法獲取 container 版本列表 (HTTP Status: $HTTP_STATUS)" exit 1 fi # 獲取並排序所有版本 (按創建時間降序) -# API 直接返回版本陣列,不需要 .versions -SORTED_VERSIONS=$(echo "$VERSIONS_JSON" | jq 'sort_by(.created_at) | reverse') +SORTED_VERSIONS=$(echo "$VERSIONS_JSON" | jq -e 'sort_by(.created_at) | reverse') + +if [[ $? -ne 0 ]]; then + echo "❌ 錯誤:無法解析版本資料" + exit 1 +fi # 計算總數量 TOTAL_COUNT=$(echo "$SORTED_VERSIONS" | jq 'length') echo "目前總共有 $TOTAL_COUNT 個映像版本" # 如果總數量小於等於保留數量,則無需清理 -if [ $TOTAL_COUNT -le $KEEP_COUNT ]; then - echo "映像數量 ($TOTAL_COUNT) 未超過保留數量 ($KEEP_COUNT),無需清理" +if [[ $TOTAL_COUNT -le $KEEP_COUNT ]]; then + echo "✅ 映像數量 ($TOTAL_COUNT) 未超過保留數量 ($KEEP_COUNT),無需清理" exit 0 fi @@ -46,15 +62,15 @@ DELETE_COUNT=$((TOTAL_COUNT - KEEP_COUNT)) echo "需要刪除 $DELETE_COUNT 個舊映像" # 獲取要刪除的版本 (跳過前 keep-count 個) -TO_DELETE=$(echo "$SORTED_VERSIONS" | jq -r ".[$KEEP_COUNT:]") +TO_DELETE=$(echo "$SORTED_VERSIONS" | jq -c ".[$KEEP_COUNT:][]") # 初始化刪除計數器和列表 DELETED_COUNT=0 -DELETED_LIST="[]" +declare -a DELETED_VERSIONS # 處理每個要刪除的版本 -echo "$TO_DELETE" | jq -c '.[]' | while read -r version; do - if [ -z "$version" ] || [ "$version" = "null" ]; then +while IFS= read -r version; do + if [[ -z "$version" ]] || [[ "$version" == "null" ]]; then continue fi @@ -62,45 +78,58 @@ echo "$TO_DELETE" | jq -c '.[]' | while read -r version; do VERSION_NAME=$(echo "$version" | jq -r '.version') CREATED_AT=$(echo "$version" | jq -r '.created_at') - if [ -z "$VERSION_ID" ] || [ "$VERSION_ID" = "null" ]; then + if [[ -z "$VERSION_ID" ]] || [[ "$VERSION_ID" == "null" ]]; then continue fi echo "準備刪除: ID=$VERSION_ID, Version=$VERSION_NAME, Created=$CREATED_AT" - if [ "$DRY_RUN" = "true" ]; then + if [[ "$DRY_RUN" == "true" ]]; then echo "🔍 [模擬執行] 會刪除映像版本: $VERSION_NAME (ID: $VERSION_ID)" DELETED_COUNT=$((DELETED_COUNT + 1)) + DELETED_VERSIONS+=("$VERSION_NAME") else # 實際刪除 - DELETE_RESPONSE=$(curl -s -w "HTTP_STATUS:%{http_code}" \ + DELETE_RESPONSE=$(curl -s -w "\nHTTP_STATUS:%{http_code}" \ -X DELETE "$GITEA_SERVER/api/v1/packages/$OWNER/container/$PACKAGE_NAME/$VERSION_ID?token=$TOKEN" \ -H "Accept: application/json") - HTTP_STATUS=$(echo "$DELETE_RESPONSE" | grep -o "HTTP_STATUS:[0-9]*" | cut -d: -f2) + DELETE_HTTP_STATUS=$(echo "$DELETE_RESPONSE" | grep -o "HTTP_STATUS:[0-9]*" | cut -d: -f2) - if [ "$HTTP_STATUS" = "204" ] || [ "$HTTP_STATUS" = "200" ]; then + if [[ "$DELETE_HTTP_STATUS" == "204" ]] || [[ "$DELETE_HTTP_STATUS" == "200" ]]; then echo "✅ 成功刪除映像版本: $VERSION_NAME (ID: $VERSION_ID)" DELETED_COUNT=$((DELETED_COUNT + 1)) - - # 更新已刪除列表 - DELETED_LIST=$(echo "$DELETED_LIST" | jq ". + [{\"id\": \"$VERSION_ID\", \"version\": \"$VERSION_NAME\", \"created_at\": \"$CREATED_AT\"}]") + DELETED_VERSIONS+=("$VERSION_NAME") else - echo "❌ 刪除失敗映像版本: $VERSION_NAME (ID: $VERSION_ID), HTTP狀態: $HTTP_STATUS" + echo "❌ 刪除失敗映像版本: $VERSION_NAME (ID: $VERSION_ID), HTTP狀態: $DELETE_HTTP_STATUS" fi fi -done +done <<< "$TO_DELETE" -# 由於 while 迴圈在子 shell 中執行,需要重新計算 -if [ "$DRY_RUN" = "true" ]; then - FINAL_DELETE_COUNT=$(echo "$TO_DELETE" | jq 'length') - echo "🔍 [模擬執行] 總共會刪除 $FINAL_DELETE_COUNT 個映像版本" +# 輸出最終結果 +echo "" +echo "=========================================" +if [[ "$DRY_RUN" == "true" ]]; then + echo "🔍 [模擬執行] 總共會刪除 $DELETED_COUNT 個映像版本" + if [[ ${#DELETED_VERSIONS[@]} -gt 0 ]]; then + echo "待刪除版本列表:" + printf ' - %s\n' "${DELETED_VERSIONS[@]}" + fi else - # 重新獲取並計算實際刪除的數量 - NEW_VERSIONS_JSON=$(curl -s "$GITEA_SERVER/api/v1/packages/$OWNER/container/$PACKAGE_NAME?token=$TOKEN" \ - -H "Accept: application/json") - NEW_TOTAL_COUNT=$(echo "$NEW_VERSIONS_JSON" | jq 'length') - ACTUAL_DELETED=$((TOTAL_COUNT - NEW_TOTAL_COUNT)) + echo "✅ 清理完成,成功刪除了 $DELETED_COUNT 個映像版本" + if [[ ${#DELETED_VERSIONS[@]} -gt 0 ]]; then + echo "已刪除版本列表:" + printf ' - %s\n' "${DELETED_VERSIONS[@]}" + fi - echo "✅ 清理完成,實際刪除了 $ACTUAL_DELETED 個映像版本" + # 驗證結果 + if [[ $DELETED_COUNT -gt 0 ]]; then + echo "" + echo "正在驗證剩餘映像數量..." + VERIFY_JSON=$(curl -s "$GITEA_SERVER/api/v1/packages/$OWNER/container/$PACKAGE_NAME?token=$TOKEN" \ + -H "Accept: application/json") + NEW_TOTAL_COUNT=$(echo "$VERIFY_JSON" | jq 'length') + echo "目前剩餘 $NEW_TOTAL_COUNT 個映像版本(預期: $KEEP_COUNT)" + fi fi +echo "=========================================" diff --git a/cleanup-releases.sh b/cleanup-releases.sh index b44e768..7cd8434 100644 --- a/cleanup-releases.sh +++ b/cleanup-releases.sh @@ -1,33 +1,52 @@ #!/bin/bash -set -e +set -euo pipefail GITEA_SERVER="$1" REPOSITORY="$2" TOKEN="$3" KEEP_COUNT="$4" -DRY_RUN="$5" +DRY_RUN="${5:-false}" + +# 參數驗證 +if [[ -z "$GITEA_SERVER" ]] || [[ -z "$REPOSITORY" ]] || [[ -z "$TOKEN" ]] || [[ -z "$KEEP_COUNT" ]]; then + echo "❌ 錯誤:缺少必要參數" + echo "使用方式: $0 [dry-run]" + exit 1 +fi echo "開始清理舊版本,保留最新 $KEEP_COUNT 個版本" # 獲取所有 releases 並按創建時間排序 -RELEASES_JSON=$(curl -s "$GITEA_SERVER/api/v1/repos/$REPOSITORY/releases" \ +echo "正在獲取 releases 列表..." +RELEASES_RESPONSE=$(curl -s -w "\nHTTP_STATUS:%{http_code}" \ + "$GITEA_SERVER/api/v1/repos/$REPOSITORY/releases" \ -H "Authorization: token $TOKEN" \ -H "Accept: application/json") +HTTP_STATUS=$(echo "$RELEASES_RESPONSE" | grep -o "HTTP_STATUS:[0-9]*" | cut -d: -f2) +RELEASES_JSON=$(echo "$RELEASES_RESPONSE" | sed '/HTTP_STATUS:/d') + # 檢查是否成功獲取 releases -if [ $? -ne 0 ] || [ -z "$RELEASES_JSON" ]; then - echo "錯誤:無法獲取 releases 列表" +if [[ "$HTTP_STATUS" != "200" ]] || [[ -z "$RELEASES_JSON" ]] || [[ "$RELEASES_JSON" == "null" ]]; then + echo "❌ 錯誤:無法獲取 releases 列表 (HTTP Status: $HTTP_STATUS)" exit 1 fi -# 計算總數量 -TOTAL_COUNT=$(echo "$RELEASES_JSON" | jq 'length') +# 計算總數量並排序(按創建時間降序,最新的在前) +SORTED_RELEASES=$(echo "$RELEASES_JSON" | jq -e 'sort_by(.created_at) | reverse') + +if [[ $? -ne 0 ]]; then + echo "❌ 錯誤:無法解析 releases 資料" + exit 1 +fi + +TOTAL_COUNT=$(echo "$SORTED_RELEASES" | jq 'length') echo "目前總共有 $TOTAL_COUNT 個 releases" # 如果總數量小於等於保留數量,則無需清理 -if [ $TOTAL_COUNT -le $KEEP_COUNT ]; then - echo "releases 數量 ($TOTAL_COUNT) 未超過保留數量 ($KEEP_COUNT),無需清理" +if [[ $TOTAL_COUNT -le $KEEP_COUNT ]]; then + echo "✅ releases 數量 ($TOTAL_COUNT) 未超過保留數量 ($KEEP_COUNT),無需清理" exit 0 fi @@ -35,63 +54,68 @@ fi DELETE_COUNT=$((TOTAL_COUNT - KEEP_COUNT)) echo "需要刪除 $DELETE_COUNT 個舊版本" -# 獲取要刪除的 releases (跳過前 keep-count 個) -TO_DELETE=$(echo "$RELEASES_JSON" | jq -r ".[$KEEP_COUNT:] | .[] | {id: .id, tag: .tag_name, name: .name}") +# 獲取要刪除的 releases(跳過前 keep-count 個) +TO_DELETE=$(echo "$SORTED_RELEASES" | jq -c ".[$KEEP_COUNT:]") -# 初始化刪除計數器和列表 +# 初始化刪除計數器 DELETED_COUNT=0 -DELETED_LIST="[]" +FAILED_COUNT=0 # 處理每個要刪除的 release -echo "$TO_DELETE" | jq -c '.' | while read -r release; do - if [ -z "$release" ] || [ "$release" = "null" ]; then +echo "$TO_DELETE" | jq -c '.[]' | while IFS= read -r release; do + if [[ -z "$release" ]] || [[ "$release" == "null" ]]; then continue fi RELEASE_ID=$(echo "$release" | jq -r '.id') - RELEASE_TAG=$(echo "$release" | jq -r '.tag') - RELEASE_NAME=$(echo "$release" | jq -r '.name') + RELEASE_TAG=$(echo "$release" | jq -r '.tag_name') + RELEASE_NAME=$(echo "$release" | jq -r '.name // "未命名"') - if [ -z "$RELEASE_ID" ] || [ "$RELEASE_ID" = "null" ]; then + if [[ -z "$RELEASE_ID" ]] || [[ "$RELEASE_ID" == "null" ]]; then continue fi - echo "準備刪除: ID=$RELEASE_ID, Tag=$RELEASE_TAG, Name=$RELEASE_NAME" - - if [ "$DRY_RUN" = "true" ]; then + if [[ "$DRY_RUN" == "true" ]]; then echo "🔍 [模擬執行] 會刪除 release: $RELEASE_TAG ($RELEASE_NAME)" - DELETED_COUNT=$((DELETED_COUNT + 1)) else # 實際刪除 - DELETE_RESPONSE=$(curl -s -w "HTTP_STATUS:%{http_code}" \ + DELETE_RESPONSE=$(curl -s -w "\nHTTP_STATUS:%{http_code}" \ -X DELETE "$GITEA_SERVER/api/v1/repos/$REPOSITORY/releases/$RELEASE_ID" \ -H "Authorization: token $TOKEN") HTTP_STATUS=$(echo "$DELETE_RESPONSE" | grep -o "HTTP_STATUS:[0-9]*" | cut -d: -f2) - if [ "$HTTP_STATUS" = "204" ] || [ "$HTTP_STATUS" = "200" ]; then + if [[ "$HTTP_STATUS" == "204" ]] || [[ "$HTTP_STATUS" == "200" ]]; then echo "✅ 成功刪除 release: $RELEASE_TAG ($RELEASE_NAME)" - DELETED_COUNT=$((DELETED_COUNT + 1)) - - # 更新已刪除列表 - DELETED_LIST=$(echo "$DELETED_LIST" | jq ". + [{\"id\": \"$RELEASE_ID\", \"tag\": \"$RELEASE_TAG\", \"name\": \"$RELEASE_NAME\"}]") else - echo "❌ 刪除失敗 release: $RELEASE_TAG ($RELEASE_NAME), HTTP狀態: $HTTP_STATUS" + echo "❌ 刪除失敗 release: $RELEASE_TAG ($RELEASE_NAME), HTTP 狀態: $HTTP_STATUS" fi fi done -# 由於 while 迴圈在子 shell 中執行,需要重新計算 -if [ "$DRY_RUN" = "true" ]; then - FINAL_DELETE_COUNT=$(echo "$TO_DELETE" | jq -s 'length') - echo "🔍 [模擬執行] 總共會刪除 $FINAL_DELETE_COUNT 個版本" +# 輸出最終結果 +if [[ "$DRY_RUN" == "true" ]]; then + echo "" + echo "🔍 [模擬執行] 總共會刪除 $DELETE_COUNT 個版本" + echo "✅ 模擬執行完成" else # 重新獲取並計算實際刪除的數量 - NEW_RELEASES_JSON=$(curl -s "$GITEA_SERVER/api/v1/repos/$REPOSITORY/releases" \ + echo "" + echo "正在驗證刪除結果..." + NEW_RELEASES_RESPONSE=$(curl -s -w "\nHTTP_STATUS:%{http_code}" \ + "$GITEA_SERVER/api/v1/repos/$REPOSITORY/releases" \ -H "Authorization: token $TOKEN" \ -H "Accept: application/json") - NEW_TOTAL_COUNT=$(echo "$NEW_RELEASES_JSON" | jq 'length') - ACTUAL_DELETED=$((TOTAL_COUNT - NEW_TOTAL_COUNT)) - echo "✅ 清理完成,實際刪除了 $ACTUAL_DELETED 個版本" + NEW_HTTP_STATUS=$(echo "$NEW_RELEASES_RESPONSE" | grep -o "HTTP_STATUS:[0-9]*" | cut -d: -f2) + NEW_RELEASES_JSON=$(echo "$NEW_RELEASES_RESPONSE" | sed '/HTTP_STATUS:/d') + + if [[ "$NEW_HTTP_STATUS" == "200" ]] && [[ -n "$NEW_RELEASES_JSON" ]]; then + NEW_TOTAL_COUNT=$(echo "$NEW_RELEASES_JSON" | jq 'length') + ACTUAL_DELETED=$((TOTAL_COUNT - NEW_TOTAL_COUNT)) + echo "✅ 清理完成!實際刪除了 $ACTUAL_DELETED 個版本" + echo "📊 目前剩餘 $NEW_TOTAL_COUNT 個 releases" + else + echo "✅ 清理完成(無法驗證最終數量)" + fi fi -- 2.48.1 From b4c9edcd695952efa6ac0367bbe022fa6ee1b234 Mon Sep 17 00:00:00 2001 From: Jeffery Date: Mon, 1 Dec 2025 14:33:36 +0800 Subject: [PATCH 2/2] =?UTF-8?q?feat:=20=E4=BF=AE=E6=94=B9=E8=BC=B8?= =?UTF-8?q?=E5=87=BA=E8=88=87=E8=A8=BB=E8=A7=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- cleanup-releases.sh | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/cleanup-releases.sh b/cleanup-releases.sh index 7cd8434..50b4729 100644 --- a/cleanup-releases.sh +++ b/cleanup-releases.sh @@ -17,8 +17,8 @@ fi echo "開始清理舊版本,保留最新 $KEEP_COUNT 個版本" -# 獲取所有 releases 並按創建時間排序 -echo "正在獲取 releases 列表..." +# 獲取所有發佈並按創建時間排序 +echo "正在獲取發佈列表..." RELEASES_RESPONSE=$(curl -s -w "\nHTTP_STATUS:%{http_code}" \ "$GITEA_SERVER/api/v1/repos/$REPOSITORY/releases" \ -H "Authorization: token $TOKEN" \ @@ -27,9 +27,9 @@ RELEASES_RESPONSE=$(curl -s -w "\nHTTP_STATUS:%{http_code}" \ HTTP_STATUS=$(echo "$RELEASES_RESPONSE" | grep -o "HTTP_STATUS:[0-9]*" | cut -d: -f2) RELEASES_JSON=$(echo "$RELEASES_RESPONSE" | sed '/HTTP_STATUS:/d') -# 檢查是否成功獲取 releases +# 檢查是否成功獲取發佈 if [[ "$HTTP_STATUS" != "200" ]] || [[ -z "$RELEASES_JSON" ]] || [[ "$RELEASES_JSON" == "null" ]]; then - echo "❌ 錯誤:無法獲取 releases 列表 (HTTP Status: $HTTP_STATUS)" + echo "❌ 錯誤:無法獲取發佈列表 (HTTP Status: $HTTP_STATUS)" exit 1 fi @@ -37,16 +37,16 @@ fi SORTED_RELEASES=$(echo "$RELEASES_JSON" | jq -e 'sort_by(.created_at) | reverse') if [[ $? -ne 0 ]]; then - echo "❌ 錯誤:無法解析 releases 資料" + echo "❌ 錯誤:無法解析發佈資料" exit 1 fi TOTAL_COUNT=$(echo "$SORTED_RELEASES" | jq 'length') -echo "目前總共有 $TOTAL_COUNT 個 releases" +echo "目前總共有 $TOTAL_COUNT 個發佈" # 如果總數量小於等於保留數量,則無需清理 if [[ $TOTAL_COUNT -le $KEEP_COUNT ]]; then - echo "✅ releases 數量 ($TOTAL_COUNT) 未超過保留數量 ($KEEP_COUNT),無需清理" + echo "✅ 發佈數量 ($TOTAL_COUNT) 未超過保留數量 ($KEEP_COUNT),無需清理" exit 0 fi @@ -54,14 +54,14 @@ fi DELETE_COUNT=$((TOTAL_COUNT - KEEP_COUNT)) echo "需要刪除 $DELETE_COUNT 個舊版本" -# 獲取要刪除的 releases(跳過前 keep-count 個) +# 獲取要刪除的發佈(跳過前 keep-count 個) TO_DELETE=$(echo "$SORTED_RELEASES" | jq -c ".[$KEEP_COUNT:]") # 初始化刪除計數器 DELETED_COUNT=0 FAILED_COUNT=0 -# 處理每個要刪除的 release +# 處理每個要刪除的發佈 echo "$TO_DELETE" | jq -c '.[]' | while IFS= read -r release; do if [[ -z "$release" ]] || [[ "$release" == "null" ]]; then continue @@ -76,7 +76,7 @@ echo "$TO_DELETE" | jq -c '.[]' | while IFS= read -r release; do fi if [[ "$DRY_RUN" == "true" ]]; then - echo "🔍 [模擬執行] 會刪除 release: $RELEASE_TAG ($RELEASE_NAME)" + echo "🔍 [模擬執行] 會刪除發佈: $RELEASE_TAG ($RELEASE_NAME)" else # 實際刪除 DELETE_RESPONSE=$(curl -s -w "\nHTTP_STATUS:%{http_code}" \ @@ -86,9 +86,9 @@ echo "$TO_DELETE" | jq -c '.[]' | while IFS= read -r release; do HTTP_STATUS=$(echo "$DELETE_RESPONSE" | grep -o "HTTP_STATUS:[0-9]*" | cut -d: -f2) if [[ "$HTTP_STATUS" == "204" ]] || [[ "$HTTP_STATUS" == "200" ]]; then - echo "✅ 成功刪除 release: $RELEASE_TAG ($RELEASE_NAME)" + echo "✅ 成功刪除發佈: $RELEASE_TAG ($RELEASE_NAME)" else - echo "❌ 刪除失敗 release: $RELEASE_TAG ($RELEASE_NAME), HTTP 狀態: $HTTP_STATUS" + echo "❌ 刪除失敗發佈: $RELEASE_TAG ($RELEASE_NAME), HTTP 狀態: $HTTP_STATUS" fi fi done @@ -114,7 +114,7 @@ else NEW_TOTAL_COUNT=$(echo "$NEW_RELEASES_JSON" | jq 'length') ACTUAL_DELETED=$((TOTAL_COUNT - NEW_TOTAL_COUNT)) echo "✅ 清理完成!實際刪除了 $ACTUAL_DELETED 個版本" - echo "📊 目前剩餘 $NEW_TOTAL_COUNT 個 releases" + echo "📊 目前剩餘 $NEW_TOTAL_COUNT 個發佈" else echo "✅ 清理完成(無法驗證最終數量)" fi -- 2.48.1