3353865f5b
CI/CD Pipeline / Staging E2E Tests (pull_request) Blocked by required conditions
CI/CD Pipeline / Staging API Integration Tests (pull_request) Blocked by required conditions
CI/CD Pipeline / Deploy Production (pull_request) Blocked by required conditions
CI/CD Pipeline / Production Browser E2E (pull_request) Blocked by required conditions
CI/CD Pipeline / ACR Image Cleanup (pull_request) Blocked by required conditions
CI/CD Pipeline / Canary Release to Production (pull_request) Blocked by required conditions
CI/CD Pipeline / CI Gate (pull_request) Blocked by required conditions
CI/CD Pipeline / Check if frontend-only change (pull_request) Successful in 9s
CI/CD Pipeline / Unit Tests (pull_request) Waiting to run
CI/CD Pipeline / Frontend Unit Tests (pull_request) Waiting to run
CI/CD Pipeline / Build Production API Image (pull_request) Waiting to run
CI/CD Pipeline / Build Production Web Image (pull_request) Waiting to run
CI/CD Pipeline / Build Production Worker Image (pull_request) Waiting to run
CI/CD Pipeline / Frontend Lint (pull_request) Successful in 39s
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 / Deploy Staging (Watchtower auto-deploy) (pull_request) Waiting to run
CI/CD Pipeline / PR Build Web Image (pull_request) Successful in 56s
CI/CD Pipeline / PR Build Worker Image (pull_request) Successful in 49s
CI/CD Pipeline / PR Build API Image (pull_request) Successful in 59s
PR Automation / Auto Merge on CI Green + Approved (pull_request) Has been skipped
Preview Deploy / Deploy Preview Environment (pull_request) Failing after 18s
Preview Cleanup / Cleanup Preview Environment (pull_request) Successful in 6s
ACR Cleanup / ACR Image Cleanup (pull_request_target) Successful in 6s
CI/CD Pipeline / Validate - Type Check (mypy) (pull_request) Successful in 3m9s
CI/CD Pipeline / Validate - Migration (alembic) (pull_request) Successful in 3m8s
PR Automation / Auto Approve on CI Green (pull_request) Successful in 2m49s
AI Code Review / AI Code Review (pull_request) Failing after 3m49s
CI/CD Pipeline / Validate - Code Quality (pull_request) Successful in 7m19s
CI/CD Pipeline / Integration Tests (pull_request) Waiting to run
P0-1: acr-cleanup.yml 完全不可用 - $GITEA_OUTPUT → $GITHUB_OUTPUT(outputs写入完全失效) - PREVIEW_SSH_KEY → STAGING_SSH_KEY(用错了密钥) - 增加 STAGING_SSH_HOST/PORT/USER 从secret读取 - SSH连接用户从写死root改为变量 P0-2: production-e2e DooD模式下必然失败 - -v "$PWD:/workspace" 改为 docker create + docker cp 模式 - 与 staging-e2e 保持一致
173 lines
6.3 KiB
YAML
173 lines
6.3 KiB
YAML
name: ACR Cleanup
|
||
|
||
on:
|
||
schedule:
|
||
- cron: '0 19 * * *' # UTC 19:00 = 北京时间凌晨3:00
|
||
workflow_dispatch:
|
||
inputs:
|
||
pr_sha:
|
||
description: "PR commit SHA(仅清理指定PR镜像,留空则全量清理)"
|
||
required: false
|
||
default: ""
|
||
dry_run:
|
||
description: "预览模式(dry-run),不实际删除"
|
||
required: false
|
||
default: "true"
|
||
pull_request_target:
|
||
types: [closed]
|
||
branches: [develop, main]
|
||
|
||
concurrency:
|
||
group: acr-cleanup-${{ gitea.ref }}
|
||
cancel-in-progress: false
|
||
|
||
jobs:
|
||
cleanup:
|
||
name: ACR Image Cleanup
|
||
runs-on: ci-l2
|
||
timeout-minutes: 20
|
||
permissions:
|
||
contents: read
|
||
env:
|
||
ACR_REGISTRY: xiaoxia-registry.cn-hangzhou.cr.aliyuncs.com
|
||
ACR_NAMESPACE: xiaoxiakeji
|
||
ACR_SERVICE: registry.aliyuncs.com:cn-hangzhou:china:cri-fvec8o9q4mmxrkaa
|
||
GITEA_URL: https://git.xiaoxiajianji.com
|
||
GITEA_REPO: xiaoxia/xiaoxia-saas
|
||
steps:
|
||
- name: Checkout code
|
||
shell: sh
|
||
env:
|
||
GITHUB_TOKEN: ${{ github.token }}
|
||
run: |
|
||
curl -sH "Authorization: token $GITHUB_TOKEN" "${GITHUB_API_URL}/repos/${GITHUB_REPOSITORY}/raw/scripts/ci/step_checkout.sh?ref=${GITHUB_SHA}" | bash
|
||
|
||
# ====== Cron模式:获取staging运行中镜像作为白名单 ======
|
||
- name: Get staging running images (whitelist)
|
||
id: protected_images
|
||
if: gitea.event_name != 'pull_request_target' && !gitea.event.inputs.pr_sha
|
||
env:
|
||
STAGING_SSH_HOST: ${{ secrets.STAGING_SSH_HOST }}
|
||
STAGING_SSH_PORT: ${{ secrets.STAGING_SSH_PORT }}
|
||
STAGING_SSH_USER: ${{ secrets.STAGING_SSH_USER }}
|
||
STAGING_SSH_KEY: ${{ secrets.STAGING_SSH_KEY }}
|
||
run: |
|
||
set +e
|
||
echo "获取staging服务器运行中镜像作为白名单..."
|
||
staging_host="${STAGING_SSH_HOST:-47.98.113.167}"
|
||
staging_port="${STAGING_SSH_PORT:-22222}"
|
||
staging_user="${STAGING_SSH_USER:-root}"
|
||
|
||
key_path=~/.ssh/id_rsa
|
||
if [ -n "${STAGING_SSH_KEY:-}" ]; then
|
||
printf '%s\n' "$STAGING_SSH_KEY" > "$key_path"
|
||
chmod 600 "$key_path"
|
||
echo "Using key from STAGING_SSH_KEY secret"
|
||
else
|
||
echo "⚠️ STAGING_SSH_KEY not set, skipping whitelist"
|
||
echo "protected_tags=" >> $GITHUB_OUTPUT
|
||
exit 0
|
||
fi
|
||
|
||
ssh-keyscan -p "$staging_port" -H "$staging_host" >> ~/.ssh/known_hosts 2>/dev/null
|
||
|
||
# 获取所有运行容器的镜像,提取tag部分
|
||
IMAGES=$(ssh -p "$staging_port" -i "$key_path" -o StrictHostKeyChecking=no \
|
||
"$staging_user@$staging_host" "docker ps --format '{{.Image}}' 2>/dev/null" 2>/dev/null | grep -v "^$" | sort -u)
|
||
|
||
PROTECTED_TAGS=""
|
||
if [ -n "$IMAGES" ]; then
|
||
while IFS= read -r img; do
|
||
# 从完整镜像名中提取tag(最后一个冒号后)
|
||
tag=$(echo "$img" | rev | cut -d: -f1 | rev)
|
||
if [ -n "$tag" ] && [ "$tag" != "latest" ] && [ ${#tag} -gt 5 ]; then
|
||
if [ -z "$PROTECTED_TAGS" ]; then
|
||
PROTECTED_TAGS="$tag"
|
||
else
|
||
PROTECTED_TAGS="$PROTECTED_TAGS,$tag"
|
||
fi
|
||
fi
|
||
done <<< "$IMAGES"
|
||
fi
|
||
|
||
echo "staging运行中镜像tag: ${PROTECTED_TAGS:-(无)}"
|
||
echo "protected_tags=$PROTECTED_TAGS" >> $GITHUB_OUTPUT
|
||
|
||
# ====== Docker登录 ======
|
||
- name: Docker login to ACR
|
||
env:
|
||
ACR_USERNAME: ${{ secrets.ACR_USERNAME }}
|
||
ACR_PASSWORD: ${{ secrets.ACR_PASSWORD }}
|
||
run: |
|
||
printf '%s' "$ACR_PASSWORD" | docker login "$ACR_REGISTRY" -u "$ACR_USERNAME" --password-stdin
|
||
|
||
# ====== 模式1:PR关闭时清理 ======
|
||
- name: Cleanup PR images (PR closed)
|
||
if: gitea.event_name == 'pull_request_target'
|
||
env:
|
||
ACR_USERNAME: ${{ secrets.ACR_USERNAME }}
|
||
ACR_PASSWORD: ${{ secrets.ACR_PASSWORD }}
|
||
GITEA_TOKEN: ${{ secrets.GITEA_TOKEN }}
|
||
PR_SHA: ${{ gitea.event.pull_request.head.sha }}
|
||
PR_NUMBER: ${{ gitea.event.pull_request.number }}
|
||
run: |
|
||
echo "============================================"
|
||
echo " PR #$PR_NUMBER 已关闭,清理对应镜像"
|
||
echo " Head SHA: ${PR_SHA::12}"
|
||
echo "============================================"
|
||
echo ""
|
||
python3 scripts/ci/acr_cleanup.py \
|
||
--pr-sha "$PR_SHA" \
|
||
--execute
|
||
|
||
# ====== 模式2:Cron全量清理 ======
|
||
- name: Full cleanup (cron / manual)
|
||
if: gitea.event_name != 'pull_request_target' && !gitea.event.inputs.pr_sha
|
||
env:
|
||
ACR_USERNAME: ${{ secrets.ACR_USERNAME }}
|
||
ACR_PASSWORD: ${{ secrets.ACR_PASSWORD }}
|
||
GITEA_TOKEN: ${{ secrets.GITEA_TOKEN }}
|
||
PROTECTED_TAGS: ${{ steps.protected_images.outputs.protected_tags }}
|
||
DRY_RUN_INPUT: ${{ gitea.event.inputs.dry_run }}
|
||
run: |
|
||
echo "============================================"
|
||
echo " ACR 全量清理(${{ gitea.event_name }})"
|
||
echo "============================================"
|
||
echo ""
|
||
|
||
# 决定是否dry-run
|
||
DRY_RUN_FLAG=""
|
||
if [ "$DRY_RUN_INPUT" = "true" ]; then
|
||
DRY_RUN_FLAG="--dry-run"
|
||
echo "模式: 预览模式 (dry-run)"
|
||
else
|
||
echo "模式: 执行模式"
|
||
fi
|
||
echo ""
|
||
|
||
python3 scripts/ci/acr_cleanup.py \
|
||
--keep 20 \
|
||
--protected-tags "$PROTECTED_TAGS" \
|
||
$DRY_RUN_FLAG
|
||
|
||
# ====== 模式3:手动指定PR SHA清理 ======
|
||
- name: Cleanup specific PR image (manual)
|
||
if: gitea.event_name == 'workflow_dispatch' && gitea.event.inputs.pr_sha
|
||
env:
|
||
ACR_USERNAME: ${{ secrets.ACR_USERNAME }}
|
||
ACR_PASSWORD: ${{ secrets.ACR_PASSWORD }}
|
||
PR_SHA: ${{ gitea.event.inputs.pr_sha }}
|
||
DRY_RUN_INPUT: ${{ gitea.event.inputs.dry_run }}
|
||
run: |
|
||
echo "手动清理PR镜像: ${PR_SHA::12}"
|
||
echo ""
|
||
|
||
DRY_RUN_FLAG=""
|
||
if [ "$DRY_RUN_INPUT" = "true" ]; then
|
||
DRY_RUN_FLAG="--dry-run"
|
||
fi
|
||
|
||
python3 scripts/ci/acr_cleanup.py \
|
||
--pr-sha "$PR_SHA" \
|
||
$DRY_RUN_FLAG
|