chore(ci): black+isort format acr_cleanup.py
CI/CD Pipeline / Check if frontend-only change (pull_request) Successful in 23s
CI/CD Pipeline / Frontend Lint (pull_request) Successful in 43s
CI/CD Pipeline / Validate - Type Check (mypy) (pull_request) Successful in 1m15s
CI/CD Pipeline / PR Build Web Image (pull_request) Successful in 30s
CI/CD Pipeline / Validate - Migration (alembic) (pull_request) Successful in 1m18s
CI/CD Pipeline / PR Build Worker Image (pull_request) Successful in 16s
Preview Deploy / Deploy Preview Environment (pull_request) Failing after 35s
CI/CD Pipeline / Validate - Code Quality (pull_request) Successful in 3m19s
CI/CD Pipeline / PR Build API Image (pull_request) Successful in 3m19s
PR Automation / Auto Approve on CI Green (pull_request) Successful in 3m18s
CI/CD Pipeline / Unit Tests (pull_request) Failing after 5m1s
PR Automation / Auto Merge on CI Green + Approved (pull_request) Successful in 6m57s
Preview Cleanup / Cleanup Preview Environment (pull_request) Successful in 17s
AI Code Review / AI Code Review (pull_request) Successful in 7m49s
ACR Cleanup / ACR Image Cleanup (pull_request_target) Failing after 1m31s
CI/CD Pipeline / Integration Tests (pull_request) Successful in 2m17s
CI/CD Pipeline / Production Browser E2E (pull_request) Failing after 1275h11m47s
CI/CD Pipeline / ACR Image Cleanup (pull_request) Failing after 1275h11m49s
CI/CD Pipeline / Staging E2E Tests (pull_request) Failing after 1275h11m53s
CI/CD Pipeline / Deploy Production (pull_request) Failing after 1275h17m43s
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (pull_request) Failing after 1275h17m59s
CI/CD Pipeline / Build Production Worker Image (pull_request) Failing after 1275h21m49s
CI/CD Pipeline / Build Staging Worker Image (pull_request) Failing after 1275h24m14s
CI/CD Pipeline / Build Production Web Image (pull_request) Failing after 1275h21m49s
CI/CD Pipeline / Build Staging Web Image (pull_request) Failing after 1275h24m16s
CI/CD Pipeline / Build Production API Image (pull_request) Failing after 1275h21m51s
CI/CD Pipeline / Build Staging API Image (pull_request) Failing after 1275h24m18s
CI/CD Pipeline / Staging API Integration Tests (pull_request) Failing after 1275h44m25s
CI/CD Pipeline / Frontend Unit Tests (pull_request) Failing after 1275h54m24s

This commit is contained in:
2026-07-25 12:03:06 +08:00
parent 903ff357fc
commit 6f31d2673f
+15 -15
View File
@@ -72,6 +72,7 @@ ACCEPT_MANIFEST_V2 = "application/vnd.docker.distribution.manifest.v2+json"
# ========== Registry API ==========
def get_token(repo, action="pull"):
"""获取仓库访问token"""
scope = "repository:" + NAMESPACE + "/" + repo + ":" + action
@@ -201,6 +202,7 @@ def delete_manifest(repo, digest, token):
# ========== Gitea API ==========
def gitea_get_open_prs():
"""获取所有打开的PR编号列表"""
if not GITEA_TOKEN:
@@ -249,6 +251,7 @@ def gitea_get_pr_commits(pr_number):
# ========== 工具函数 ==========
def parse_time(created_str):
"""解析ISO时间字符串"""
if not created_str:
@@ -299,6 +302,7 @@ def is_in_protected_list(tag, protected_set):
# ========== 核心清理逻辑 ==========
def cleanup_repo(repo, keep_count, dry_run, protected_tags, pr_sha=None, pr_open_set=None):
"""
清理单个仓库
@@ -335,7 +339,11 @@ def cleanup_repo(repo, keep_count, dry_run, protected_tags, pr_sha=None, pr_open
# ========== PR-SHA模式:只删除指定commit的PR镜像 ==========
if pr_sha:
pr_tags_to_del = [t for t in tags if t.startswith("pr-" + pr_sha) or t == "pr-" + pr_sha or pr_sha.startswith(extract_sha_from_pr_tag(t))]
pr_tags_to_del = [
t
for t in tags
if t.startswith("pr-" + pr_sha) or t == "pr-" + pr_sha or pr_sha.startswith(extract_sha_from_pr_tag(t))
]
if not pr_tags_to_del:
print(f" 未找到PR镜像: pr-{pr_sha[:12]}")
return len(tags), 0
@@ -472,10 +480,7 @@ def cleanup_repo(repo, keep_count, dry_run, protected_tags, pr_sha=None, pr_open
# 白名单过滤:受保护的tag不删除
if protected_tags:
before = len(commit_to_delete)
commit_to_delete = [
t for t in commit_to_delete
if not is_in_protected_list(t["tag"], protected_tags)
]
commit_to_delete = [t for t in commit_to_delete if not is_in_protected_list(t["tag"], protected_tags)]
removed = before - len(commit_to_delete)
if removed > 0:
print(f" 白名单保护: 跳过{removed}个运行中镜像")
@@ -492,10 +497,7 @@ def cleanup_repo(repo, keep_count, dry_run, protected_tags, pr_sha=None, pr_open
# 再次过滤白名单(PR镜像也受白名单保护)
if protected_tags:
before = len(all_to_delete)
all_to_delete = [
t for t in all_to_delete
if not is_in_protected_list(t["tag"], protected_tags)
]
all_to_delete = [t for t in all_to_delete if not is_in_protected_list(t["tag"], protected_tags)]
removed = before - len(all_to_delete)
if removed > 0:
print(f" 白名单保护(PR镜像): 跳过{removed}个")
@@ -549,6 +551,7 @@ def _execute_delete(repo, to_delete, dry_run, total_tags):
# ========== 主函数 ==========
def main():
parser = argparse.ArgumentParser(description="ACR镜像清理工具(增强版)")
parser.add_argument("--keep", type=int, default=20, help="保留最近N个commit hash tag(默认20)")
@@ -556,10 +559,8 @@ def main():
parser.add_argument("--execute", action="store_true", help="实际执行删除")
parser.add_argument("--repo", type=str, default="", help="只清理指定仓库")
parser.add_argument("--pr-sha", type=str, default="", help="PR关闭模式:删除指定commit sha的PR镜像")
parser.add_argument("--protected-tags", type=str, default="",
help="受保护tag列表,逗号分隔(运行中镜像白名单)")
parser.add_argument("--skip-pr-check", action="store_true",
help="跳过Gitea PR状态检查(纯按时间清理PR镜像)")
parser.add_argument("--protected-tags", type=str, default="", help="受保护tag列表,逗号分隔(运行中镜像白名单)")
parser.add_argument("--skip-pr-check", action="store_true", help="跳过Gitea PR状态检查(纯按时间清理PR镜像)")
args = parser.parse_args()
# 必须指定 --dry-run 或 --execute
@@ -632,8 +633,7 @@ def main():
total_tags = 0
for repo in repos_to_clean:
count, deleted = cleanup_repo(
repo, args.keep, dry_run, protected_tags,
pr_sha=args.pr_sha, pr_open_set=pr_open_set
repo, args.keep, dry_run, protected_tags, pr_sha=args.pr_sha, pr_open_set=pr_open_set
)
total_tags += count
total_deleted += deleted