feat(ci): ACR镜像自动清理增强版(#524) #872

Merged
xiaoxia merged 2 commits from chore/ci-acr-cleanup-enhance into develop 2026-07-25 12:07:17 +08:00
Owner

背景

P3-2 ACR镜像自动清理增强,防止镜像越积越多占满存储。

改动内容

1. acr_cleanup.py 升级为增强版(319行新增)

清理策略:

  • 生产镜像保护:版本tag(v*)、固定tag(latest/main/develop)、缓存镜像永久保留
  • develop镜像:保留最近20个commit hash tag,超出自动清理
  • Preview镜像(pr-*)
    • PR关闭/合并时自动清理(PR模式)
    • Cron模式下通过Gitea API检查PR状态,已关闭的清理
    • 无Gitea token时降级为7天保留
  • 安全兜底:staging运行中镜像白名单(SSH获取docker ps),正在跑的镜像不删

三种执行模式:

  1. --pr-sha <sha> — PR关闭模式,只删指定commit的pr-*镜像
  2. 无参数 — Cron全量清理模式
  3. --dry-run — 预览模式,只看不删

2. 新增 acr-cleanup.yml workflow

触发方式:

  • Cron定时:每天凌晨3:00(UTC 19:00)全量清理
  • 🔄 PR关闭:PR合并/关闭时自动清理对应镜像
  • 🖐️ 手动触发:支持指定PR SHA清理,默认dry-run

安全机制:

  • Cron模式自动获取staging运行中镜像做白名单
  • 默认dry-run预览,确认无误再切执行模式
  • 按digest去重,避免重复删除

关联工单

#524 ACR镜像自动清理

验证方式

  • Code Quality通过
  • Type Check通过
  • workflow语法正确(Gitea能识别)
## 背景 P3-2 ACR镜像自动清理增强,防止镜像越积越多占满存储。 ## 改动内容 ### 1. acr_cleanup.py 升级为增强版(319行新增) **清理策略:** - ✅ **生产镜像保护**:版本tag(v*)、固定tag(latest/main/develop)、缓存镜像永久保留 - ✅ **develop镜像**:保留最近20个commit hash tag,超出自动清理 - ✅ **Preview镜像(pr-*)**: - PR关闭/合并时自动清理(PR模式) - Cron模式下通过Gitea API检查PR状态,已关闭的清理 - 无Gitea token时降级为7天保留 - ✅ **安全兜底**:staging运行中镜像白名单(SSH获取docker ps),正在跑的镜像不删 **三种执行模式:** 1. `--pr-sha <sha>` — PR关闭模式,只删指定commit的pr-*镜像 2. 无参数 — Cron全量清理模式 3. `--dry-run` — 预览模式,只看不删 ### 2. 新增 acr-cleanup.yml workflow **触发方式:** - ⏰ **Cron定时**:每天凌晨3:00(UTC 19:00)全量清理 - 🔄 **PR关闭**:PR合并/关闭时自动清理对应镜像 - 🖐️ **手动触发**:支持指定PR SHA清理,默认dry-run **安全机制:** - Cron模式自动获取staging运行中镜像做白名单 - 默认dry-run预览,确认无误再切执行模式 - 按digest去重,避免重复删除 ## 关联工单 #524 ACR镜像自动清理 ## 验证方式 - [ ] Code Quality通过 - [ ] Type Check通过 - [ ] workflow语法正确(Gitea能识别)
xiaoxia added 1 commit 2026-07-25 10:51:11 +08:00
feat(ci): ACR镜像自动清理增强版(#524)
CI/CD Pipeline / Check if frontend-only change (pull_request) Successful in 31s
CI/CD Pipeline / PR Build Web Image (pull_request) Successful in 45s
CI/CD Pipeline / Build Staging API Image (pull_request) Has been skipped
CI/CD Pipeline / Build Staging Web Image (pull_request) Has been skipped
CI/CD Pipeline / Frontend Lint (pull_request) Successful in 49s
CI/CD Pipeline / Build Staging Worker Image (pull_request) Has been skipped
CI/CD Pipeline / PR Build Worker Image (pull_request) Successful in 29s
CI/CD Pipeline / Validate - Type Check (mypy) (pull_request) Successful in 1m7s
CI/CD Pipeline / Validate - Migration (alembic) (pull_request) Successful in 1m16s
CI/CD Pipeline / Validate - Code Quality (pull_request) Failing after 1m40s
CI/CD Pipeline / Frontend Unit Tests (pull_request) Has been skipped
Preview Deploy / Deploy Preview Environment (pull_request) Failing after 34s
CI/CD Pipeline / Build Production API Image (pull_request) Has been skipped
CI/CD Pipeline / Build Production Web Image (pull_request) Has been skipped
CI/CD Pipeline / Build Production Worker Image (pull_request) Has been skipped
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (pull_request) Has been skipped
CI/CD Pipeline / Deploy Production (pull_request) Has been skipped
CI/CD Pipeline / Staging E2E Tests (pull_request) Has been skipped
CI/CD Pipeline / Staging API Integration Tests (pull_request) Has been skipped
CI/CD Pipeline / ACR Image Cleanup (pull_request) Has been skipped
CI/CD Pipeline / Production Browser E2E (pull_request) Has been skipped
PR Automation / Auto Merge on CI Green + Approved (pull_request) Successful in 1m12s
CI/CD Pipeline / PR Build API Image (pull_request) Successful in 3m16s
CI/CD Pipeline / Integration Tests (pull_request) Successful in 2m8s
AI Code Review / AI Code Review (pull_request) Successful in 3m16s
PR Automation / Auto Approve on CI Green (pull_request) Successful in 3m18s
CI/CD Pipeline / Unit Tests (pull_request) Failing after 4m29s
903ff357fc
- acr_cleanup.py升级为增强版,支持3种清理模式:
  1. cron全量清理:版本tag/固定tag/缓存镜像永久保留
  2. PR关闭清理:PR合并/关闭后自动删除对应pr-*镜像
  3. 手动清理:指定PR commit SHA清理

- 安全兜底:
  - staging运行中镜像白名单(SSH获取docker ps)
  - Gitea PR状态检查,打开的PR镜像不删
  - dry-run预览模式

- 新增acr-cleanup.yml workflow:
  - 每天凌晨3点cron自动执行
  - PR关闭时自动触发
  - 支持手动触发(dry-run或执行)
xiaoxia added 1 commit 2026-07-25 12:03:07 +08:00
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 / Build Staging API Image (pull_request) Has been skipped
CI/CD Pipeline / Build Staging Web Image (pull_request) Has been skipped
CI/CD Pipeline / Build Staging Worker Image (pull_request) Has been skipped
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
CI/CD Pipeline / Frontend Unit Tests (pull_request) Has been skipped
CI/CD Pipeline / Build Production API Image (pull_request) Has been skipped
CI/CD Pipeline / Build Production Web Image (pull_request) Has been skipped
CI/CD Pipeline / Build Production Worker Image (pull_request) Has been skipped
PR Automation / Auto Approve on CI Green (pull_request) Successful in 3m18s
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (pull_request) Has been skipped
CI/CD Pipeline / Deploy Production (pull_request) Has been skipped
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 / Staging E2E Tests (pull_request) Has been skipped
CI/CD Pipeline / Staging API Integration Tests (pull_request) Has been skipped
CI/CD Pipeline / ACR Image Cleanup (pull_request) Has been skipped
CI/CD Pipeline / Production Browser E2E (pull_request) Has been skipped
6f31d2673f
xiaoxia merged commit e9bc340612 into develop 2026-07-25 12:07:17 +08:00

🗑️ 预览环境已清理

PR #872 已关闭或合并,对应的预览环境已被清理。

如有需要,可以重新打开 PR 来重新生成预览环境。

🗑️ **预览环境已清理** PR #872 已关闭或合并,对应的预览环境已被清理。 > 如有需要,可以重新打开 PR 来重新生成预览环境。
Collaborator

代码审查结果 - PR #872

⚠️ 问题(3个需要修改)

  1. .gitea/workflows/acr-cleanup.yml 第60行:SSH连接失败被静默忽略,导致白名单丢失风险

    • 问题描述:使用了 set +e,后续SSH命令(如 ssh-keyscandocker ps)如果失败(例如网络抖动、Staging服务器宕机),脚本不会退出,而是继续执行。这会导致 PROTECTED_TAGS 为空,Python脚本在没有任何白名单保护的情况下运行,极有可能误删正在Staging环境运行的镜像,导致服务中断。
    • 修改建议:移除 set +e,或者在SSH命令后显式检查 $?,如果获取白名单失败,应该终止Workflow或设置一个标志位阻止后续的清理步骤。
  2. scripts/ci/acr_cleanup.py 第410行:API请求失败导致全量误删PR镜像

    • 问题描述:在 cleanup_repo 函数中,当尝试通过Gitea API获取打开的PR的Head SHA时,使用了 try...except Exception: break。如果在请求过程中发生网络超时或5xx错误,循环会直接 break 退出,导致 open_head_shas 列表为空或不完整。随后的逻辑会认为所有PR镜像都不在“打开”列表中,从而将所有PR镜像标记为删除。
    • 修改建议:API请求失败时应回退到安全策略(例如按时间保留7天),而不是继续执行可能导致误删的逻辑。建议捕获异常后设置一个标志位,若获取失败则跳过该分支逻辑,进入 else 分支的时间保留策略。
  3. scripts/ci/acr_cleanup.py 第314行:空字符串SHA匹配逻辑导致垃圾数据无法清理

    • 问题描述extract_sha_from_pr_tag 函数仅简单切片 tag[3:]。如果存在名为 pr- 的异常标签,提取的SHA为空字符串。在第425行的匹配逻辑中,ohs.startswith(sha)ohs.startswith("") 永远为 True。这会导致名为 pr- 的标签被判定为“属于打开的PR”,从而永远不会被清理,造成垃圾数据残留。
    • 修改建议:在 extract_sha_from_pr_tag 中增加对提取结果的长度校验,或者在使用处判断SHA是否为空。

💡 建议(3个可选)

  1. 性能优化:避免重复的Gitea API调用

    • 具体内容:在 main 函数中调用了 gitea_get_open_prs 仅用于检测API连通性,而在 cleanup_repo 中又重新调用API获取详细信息。这导致每次清理都会对Gitea发起双倍请求(且在多仓库模式下成倍增加)。建议重构逻辑,将获取Open PR详情(包含Head SHA)的逻辑统一在 main 或一个独立函数中完成,并将结果直接传递给 cleanup_repo
  2. 配置管理:移除硬编码的IP地址

    • 具体内容:在 .gitea/workflows/acr-cleanup.yml 第70行,Staging服务器的IP地址 47.98.113.167 被硬编码。如果服务器迁移,Workflow将失效。建议将IP地址配置为Secret或Repository Variable。
  3. 匹配逻辑优化:过于宽松的前缀匹配

    • 具体内容:在 is_in_protected_list 和PR清理逻辑中,使用了 tag.startswith(p) or p.startswith(tag)。这种“互为前缀即匹配”的逻辑在某些边缘情况下可能过于激进。例如,受保护列表中有一个短SHA abc,它将保护所有以 abc 开头的镜像(如 abc123, abc999)。虽然对于SHA来说碰撞概率极低,但建议明确是“精确匹配”还是“短SHA匹配长SHA”,避免歧义。

格式检查通过 | 逻辑审查需修改 | ⚠️ 建议关注性能


🤖 由 AI 代码审查机器人自动生成 | 2026-07-25 04:12:14 | 模型:

## 代码审查结果 - PR #872 ### ⚠️ 问题(3个需要修改) 1. **.gitea/workflows/acr-cleanup.yml 第60行**:SSH连接失败被静默忽略,导致白名单丢失风险 - **问题描述**:使用了 `set +e`,后续SSH命令(如 `ssh-keyscan` 或 `docker ps`)如果失败(例如网络抖动、Staging服务器宕机),脚本不会退出,而是继续执行。这会导致 `PROTECTED_TAGS` 为空,Python脚本在没有任何白名单保护的情况下运行,极有可能误删正在Staging环境运行的镜像,导致服务中断。 - **修改建议**:移除 `set +e`,或者在SSH命令后显式检查 `$?`,如果获取白名单失败,应该终止Workflow或设置一个标志位阻止后续的清理步骤。 2. **scripts/ci/acr_cleanup.py 第410行**:API请求失败导致全量误删PR镜像 - **问题描述**:在 `cleanup_repo` 函数中,当尝试通过Gitea API获取打开的PR的Head SHA时,使用了 `try...except Exception: break`。如果在请求过程中发生网络超时或5xx错误,循环会直接 `break` 退出,导致 `open_head_shas` 列表为空或不完整。随后的逻辑会认为所有PR镜像都不在“打开”列表中,从而将**所有**PR镜像标记为删除。 - **修改建议**:API请求失败时应回退到安全策略(例如按时间保留7天),而不是继续执行可能导致误删的逻辑。建议捕获异常后设置一个标志位,若获取失败则跳过该分支逻辑,进入 `else` 分支的时间保留策略。 3. **scripts/ci/acr_cleanup.py 第314行**:空字符串SHA匹配逻辑导致垃圾数据无法清理 - **问题描述**:`extract_sha_from_pr_tag` 函数仅简单切片 `tag[3:]`。如果存在名为 `pr-` 的异常标签,提取的SHA为空字符串。在第425行的匹配逻辑中,`ohs.startswith(sha)` 即 `ohs.startswith("")` 永远为 `True`。这会导致名为 `pr-` 的标签被判定为“属于打开的PR”,从而永远不会被清理,造成垃圾数据残留。 - **修改建议**:在 `extract_sha_from_pr_tag` 中增加对提取结果的长度校验,或者在使用处判断SHA是否为空。 ### 💡 建议(3个可选) 1. **性能优化:避免重复的Gitea API调用** - **具体内容**:在 `main` 函数中调用了 `gitea_get_open_prs` 仅用于检测API连通性,而在 `cleanup_repo` 中又重新调用API获取详细信息。这导致每次清理都会对Gitea发起双倍请求(且在多仓库模式下成倍增加)。建议重构逻辑,将获取Open PR详情(包含Head SHA)的逻辑统一在 `main` 或一个独立函数中完成,并将结果直接传递给 `cleanup_repo`。 2. **配置管理:移除硬编码的IP地址** - **具体内容**:在 `.gitea/workflows/acr-cleanup.yml` 第70行,Staging服务器的IP地址 `47.98.113.167` 被硬编码。如果服务器迁移,Workflow将失效。建议将IP地址配置为Secret或Repository Variable。 3. **匹配逻辑优化:过于宽松的前缀匹配** - **具体内容**:在 `is_in_protected_list` 和PR清理逻辑中,使用了 `tag.startswith(p) or p.startswith(tag)`。这种“互为前缀即匹配”的逻辑在某些边缘情况下可能过于激进。例如,受保护列表中有一个短SHA `abc`,它将保护所有以 `abc` 开头的镜像(如 `abc123`, `abc999`)。虽然对于SHA来说碰撞概率极低,但建议明确是“精确匹配”还是“短SHA匹配长SHA”,避免歧义。 --- ✅ 格式检查通过 | ❌ 逻辑审查需修改 | ⚠️ 建议关注性能 --- <sub>🤖 由 AI 代码审查机器人自动生成 | 2026-07-25 04:12:14 | 模型: </sub> <!-- AI_CODE_REVIEW_AUTO_COMMENT -->
Sign in to join this conversation.