From ee6fa3e1cf640bd5abe9ff542e75f379907d0abf Mon Sep 17 00:00:00 2001 From: xiaoxia Date: Mon, 31 Aug 2026 17:41:43 +0800 Subject: [PATCH] =?UTF-8?q?fix(ci):=20push=20=E8=B7=AF=E5=BE=84=E6=A3=80?= =?UTF-8?q?=E6=B5=8B=E5=AE=89=E5=85=A8=E5=9B=9E=E6=BA=AF=E2=80=94=E2=80=94?= =?UTF-8?q?=E9=98=B2=E6=AD=A2=20concurrency=20=E5=8F=96=E6=B6=88=E5=AF=BC?= =?UTF-8?q?=E8=87=B4=E5=89=8D=E7=AB=AF=E6=94=B9=E5=8A=A8=E4=B8=A2=E5=A4=B1?= =?UTF-8?q?=20(#1578)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- scripts/ci/ci_push_paths.sh | 71 ++++++++++++++++++++++++++++++++++++- 1 file changed, 70 insertions(+), 1 deletion(-) diff --git a/scripts/ci/ci_push_paths.sh b/scripts/ci/ci_push_paths.sh index 09e99cdf9..6cefd0553 100755 --- a/scripts/ci/ci_push_paths.sh +++ b/scripts/ci/ci_push_paths.sh @@ -13,6 +13,7 @@ before="${GITHUB_EVENT_BEFORE:-}" after="${GITHUB_SHA:-}" repo="${GITHUB_REPOSITORY:-}" base="${GITHUB_API_URL:-}" +ZERO="0000000000000000000000000000000000000000" # Gitea Actions 中 push 事件的前一个 SHA 在 event payload 的 before 字段 if [ -z "$before" ] && [ -n "${GITHUB_EVENT_PATH:-}" ] && [ -f "$GITHUB_EVENT_PATH" ]; then @@ -28,8 +29,76 @@ fi echo "改动范围检测: before=${before:-} after=${after}" +# ── 安全回溯:确保 diff 基准覆盖所有未构建的改动 ── +# 问题:concurrency 取消机制会导致前端改动被跳过。被取消的 push 的改动不会被 +# 后续 push 的 diff 覆盖到,因为 GITHUB_EVENT_BEFORE 只指向上一次 push 的 SHA。 +# 修复:查询最近一次**实际构建了 web 镜像**的成功 push run,用其 head_sha 作为 +# diff 基准。这样被取消/跳过的 run 的改动都会被包含在当前 diff 中。 +if [ -n "$before" ] && [ "$before" != "$ZERO" ] && [ -n "${GITHUB_TOKEN:-}" ]; then + BRANCH="${GITHUB_REF_NAME:-}" + if [ -n "$BRANCH" ]; then + SAFE_BASE=$(python3 -c " +import json, subprocess, sys + +base = '${base}' +repo = '${repo}' +token = '${GITHUB_TOKEN}' +branch = '${BRANCH}' +cur_sha = '${after}' + +def check_run(run_id): + \"\"\"Check if this run actually built the web image.\"\"\" + try: + r = subprocess.run( + ['curl', '-sf', '--max-time', '10', + '-H', f'Authorization: token {token}', + f'{base}/repos/{repo}/actions/runs/{run_id}/jobs'], + capture_output=True, text=True, timeout=15) + if r.returncode != 0: + return False + jobs = json.loads(r.stdout).get('jobs', []) + return any( + 'Build Staging Web' in j.get('name', '') + and j.get('conclusion') == 'success' + for j in jobs + ) + except Exception: + return False + +try: + r = subprocess.run( + ['curl', '-sf', '--max-time', '15', + '-H', f'Authorization: token {token}', + f'{base}/repos/{repo}/actions/runs?status=success&event=push&branch={branch}&per_page=30'], + capture_output=True, text=True, timeout=20) + if r.returncode != 0: + sys.exit(0) + d = json.loads(r.stdout) + runs = d.get('workflow_runs', []) if isinstance(d, dict) else d + for run in runs: + sha = run.get('head_sha', '') + if sha and sha != cur_sha: + if check_run(run['id']): + print(sha) + break +except Exception: + pass +" 2>/dev/null || true) + + if [ -n "$SAFE_BASE" ] && [ "$SAFE_BASE" != "$before" ]; then + echo "🔒 安全回溯: 使用最近实际构建 web 的 commit ${SAFE_BASE:0:8} 替代 before=${before:0:8}" + before="$SAFE_BASE" + elif [ -z "$SAFE_BASE" ]; then + echo "⚠️ 未找到历史成功构建 web 的 push run,保守走全量构建" + echo "skip_backend=false" >> "$OUTPUT" + echo "skip_frontend=false" >> "$OUTPUT" + exit 0 + fi + fi +fi + FILES="" -if [ -n "$before" ] && [ "$before" != "0000000000000000000000000000000000000000" ]; then +if [ -n "$before" ] && [ "$before" != "$ZERO" ]; then # Gitea 1.26.x compare API 的顶层 files 字段不填充(始终为空), # 但响应里每个 commit 条目自带的 files 完整可用;聚合区间内所有提交的 files 即可。 API_URL="${base}/repos/${repo}/compare/${before}...${after}?per_page=300"