From 7e4a30aac9e82a457ec1d43b2d29ef723dfacf41 Mon Sep 17 00:00:00 2001 From: ReSerendipity Date: Thu, 24 Sep 2026 20:43:18 +0800 Subject: [PATCH] =?UTF-8?q?ci(gpu-smoke):=20=E6=8A=8A"=E6=B2=A1=E6=9C=89?= =?UTF-8?q?=E7=9C=9F=E6=8E=A8=E7=90=86"=E4=BB=8E=20success+warning=20?= =?UTF-8?q?=E6=94=B9=E6=88=90=E8=87=AA=E8=AF=81=E7=BB=93=E8=AE=BA=EF=BC=88?= =?UTF-8?q?neutral=20check=20run=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 现状(2026-09-24 审计实测):REPO_ADMIN_TOKEN 从未配置 + 注册 runner 数 = 0, 于是 3 次 schedule run 全是 gpu-smoke=skipped 而 run 结论 success。docs/DOD.md:225 记的上一轮处理是"改成 ::warning + job summary",但 warning 仍然读作通过。 改法(不硬失败,保留接入点): - runner-precheck 只产出机器可读 verdict:no-repo-admin-token-secret / no-registered-gpu-runner / runner-query-denied / available; 另加两个按 verdict 条件执行的步骤,步骤名直接自证原因。 - gpu-smoke 的 job if 改判 verdict == 'available'(真机 job 依旧 skipped)。 - 新增 smoke-status 后置作业(if: always()):把结论写成一条独立 check run, 名字自带原因码,conclusion 用 neutral —— 既不是红灯也不算通过, 同时把结论表格落到 run 页面摘要。check 名用 ASCII(本仓在 cp1252/bsdtar 非 ASCII 上栽过,不引入新变量)。 - 自证链路本身坏掉(check-run 写入失败)才 exit 1:这跟"没有真推理"是两回事。 - 需要 permissions.checks: write,已在本文件内显式声明,未碰仓库 settings。 选型说明:不用 issue skip-record(写后回读)—— 它每周产生用户可见的对象、 要 issues:write、且回读是一次额外的失败点;check run 天然绑在 SHA+run 上。 本地验证(抽出工作流里的脚本原文,在 stub gh 下跑):probe 四场景 verdict 全对、 均 rc=0;attestation 九种 (gpu-smoke 结果 × verdict) 组合的 name/conclusion 映射 全对、正常路径 rc=0;POST 失败路径 rc=1 且带 ::error::;verdict/SMOKE 为空时 兜底成 precheck-did-not-report / unknown。 Signed-off-by: ReSerendipity --- .github/workflows/gpu-smoke.yml | 113 ++++++++++++++++++++++++++------ 1 file changed, 93 insertions(+), 20 deletions(-) diff --git a/.github/workflows/gpu-smoke.yml b/.github/workflows/gpu-smoke.yml index f4e4249..8548ef5 100644 --- a/.github/workflows/gpu-smoke.yml +++ b/.github/workflows/gpu-smoke.yml @@ -36,44 +36,63 @@ on: permissions: contents: read + # smoke-status 要把"本次有没有真推理"写成一条独立 check run(neutral 而非红), + # 见 jobs.smoke-status。只需本工作流级别的 checks:write,不动仓库设置。 + checks: write jobs: runner-precheck: runs-on: ubuntu-latest outputs: - available: ${{ steps.check.outputs.available }} + # verdict ∈ {available, no-repo-admin-token-secret, no-registered-gpu-runner, + # runner-query-denied};gpu-smoke 只在 available 时跑。 + verdict: ${{ steps.probe.outputs.verdict }} steps: - - name: Check for an online self-hosted GPU runner - id: check + - name: Probe GPU runner availability + id: probe env: GH_TOKEN: ${{ secrets.REPO_ADMIN_TOKEN }} run: | - if [ -z "$GH_TOKEN" ]; then - # 以前这里是 ::notice::,整个 run 以"success"收场 —— 而真推理 job 一次都没跑过 - # (本仓 2026-09-21 实测:3 次 schedule run 全部 gpu-smoke=skipped, - # 且 REPO_ADMIN_TOKEN 这个 secret 从未配置)。跳过必须显眼,不能长得像通过。 - echo "::warning::REPO_ADMIN_TOKEN 未配置 → 无法查询 runner 在线状态,本次 GPU 真机冒烟**没有跑**。真推理覆盖率为 0,别把本 run 当验收依据。" - echo "### GPU 真机冒烟未执行" >> "$GITHUB_STEP_SUMMARY" - echo "原因:secret \`REPO_ADMIN_TOKEN\` 未配置,无法判断有没有在线的 \`gpu\` self-hosted runner。" >> "$GITHUB_STEP_SUMMARY" - echo "available=false" >> "$GITHUB_OUTPUT" + set -euo pipefail + # 这里不再"发个 warning 然后让整条 run 长得像通过"。 + # 2026-09-24 审计实测:REPO_ADMIN_TOKEN 从未配置 + 注册 runner 数 = 0, + # 于是 3 次 schedule run 全是 gpu-smoke=skipped 而 run 结论 success; + # docs/DOD.md:225 记的"改成 ::warning + job summary"那一步并不够 —— + # warning 还是读作成功。现在 precheck 只负责把原因定成机器可读的 verdict, + # 显式结论由 smoke-status 落成一条 neutral check run。 + if [ -z "${GH_TOKEN:-}" ]; then + echo "verdict=no-repo-admin-token-secret" >> "$GITHUB_OUTPUT" exit 0 fi # runners 列表 API 需要管理员级 token(GITHUB_TOKEN 即使 actions:read 也 403) COUNT=$(gh api "repos/${{ github.repository }}/actions/runners" \ - --jq '[.runners[] | select(.status == "online") | select(any(.labels[]; .name == "gpu"))] | length') - echo "online gpu-labelled runners: ${COUNT}" - if [ "${COUNT}" -gt 0 ]; then - echo "available=true" >> "$GITHUB_OUTPUT" + --jq '[.runners[] | select(.status == "online") | select(any(.labels[]; .name == "gpu"))] | length' 2>/dev/null || echo "") + if [ -z "$COUNT" ]; then + echo "verdict=runner-query-denied" >> "$GITHUB_OUTPUT" + elif [ "${COUNT}" -gt 0 ]; then + echo "online gpu-labelled runners: ${COUNT}" + echo "verdict=available" >> "$GITHUB_OUTPUT" else - echo "::warning::没有在线的 self-hosted \`gpu\` runner → 本次 GPU 真机冒烟**没有跑**,真推理覆盖率为 0(别把本 run 的 green 当验收依据)。" - echo "### GPU 真机冒烟未执行" >> "$GITHUB_STEP_SUMMARY" - echo "原因:仓库里没有在线且带 \`gpu\` 标签的 self-hosted runner(注册数见上一步输出)。" >> "$GITHUB_STEP_SUMMARY" - echo "available=false" >> "$GITHUB_OUTPUT" + echo "verdict=no-registered-gpu-runner" >> "$GITHUB_OUTPUT" fi + - name: "Skip reason: no-repo-admin-token-secret(REPO_ADMIN_TOKEN 未配置)" + if: steps.probe.outputs.verdict == 'no-repo-admin-token-secret' + run: | + echo "::notice::本次没有任何 GPU 真推理:secret REPO_ADMIN_TOKEN 未配置,precheck 无法查询 runner 在线状态。" + echo "### GPU 真机冒烟未执行" >> "$GITHUB_STEP_SUMMARY" + echo "\`verdict=no-repo-admin-token-secret\` —— precheck 拿不到管理员级 token,无法判断有没有在线的 \`gpu\` runner。" >> "$GITHUB_STEP_SUMMARY" + + - name: "Skip reason: no-registered-gpu-runner(在线 gpu runner 数为 0)" + if: steps.probe.outputs.verdict == 'no-registered-gpu-runner' + run: | + echo "::notice::本次没有任何 GPU 真推理:仓库里没有在线且带 gpu 标签的 self-hosted runner。" + echo "### GPU 真机冒烟未执行" >> "$GITHUB_STEP_SUMMARY" + echo "\`verdict=no-registered-gpu-runner\` —— 查询成功,但在线的 \`gpu\` runner 数为 0。" >> "$GITHUB_STEP_SUMMARY" + gpu-smoke: needs: runner-precheck - if: needs.runner-precheck.outputs.available == 'true' + if: needs.runner-precheck.outputs.verdict == 'available' runs-on: [self-hosted, gpu] timeout-minutes: 150 env: @@ -196,3 +215,57 @@ jobs: ${{ runner.temp }}/server.err if-no-files-found: ignore retention-days: 7 + + smoke-status: + # 把"这一轮到底有没有跑成真 GPU 推理"落成一眼可读的结论:一条独立的 check run + # (名字自带 ASCII 原因码)+ run 页面摘要。用 neutral 而不是 exit 1 —— + # 注册 runner 属基础设施决策,每周红一次不是目标;但 success + warning 也不行, + # 因为它读起来就是"过了"。check 名用 ASCII:本仓在 Windows 控制台与文档里 + # 多次栽过非 ASCII 编码(release-gate 的 cp1252、bsdtar 中文名),不再引入新变量。 + name: GPU real-inference attestation + needs: [runner-precheck, gpu-smoke] + if: always() + runs-on: ubuntu-latest + timeout-minutes: 5 + steps: + - name: Publish verdict as a neutral check run + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + VERDICT: ${{ needs.runner-precheck.outputs.verdict }} + SMOKE: ${{ needs.gpu-smoke.result }} + HEAD_SHA: ${{ github.sha }} + RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} + run: | + set -euo pipefail + # precheck 自己挂掉时 verdict 会是空串 —— 别让它落成一个读不出原因的裸名字。 + VERDICT="${VERDICT:-precheck-did-not-report}" + SMOKE="${SMOKE:-unknown}" + case "${SMOKE}/${VERDICT}" in + success/*) CONCL=success; NAME="[gpu-smoke] REAL inference executed and passed" ;; + failure/*) CONCL=failure; NAME="[gpu-smoke] REAL inference executed and FAILED" ;; + cancelled/*) CONCL=cancelled; NAME="[gpu-smoke] real inference cancelled" ;; + skipped/no-repo-admin-token-secret) CONCL=neutral; NAME="[gpu-smoke] NO REAL INFERENCE - no-repo-admin-token-secret" ;; + skipped/no-registered-gpu-runner) CONCL=neutral; NAME="[gpu-smoke] NO REAL INFERENCE - no-registered-gpu-runner" ;; + skipped/runner-query-denied) CONCL=neutral; NAME="[gpu-smoke] NO REAL INFERENCE - runner-query-denied (token lacks rights)" ;; + skipped/*) CONCL=neutral; NAME="[gpu-smoke] NO REAL INFERENCE - ${VERDICT}" ;; + *) CONCL=neutral; NAME="[gpu-smoke] status unknown (${SMOKE}/${VERDICT})" ;; + esac + + { + echo "## GPU 真机推理结论:${NAME}" + echo "" + echo "| 项 | 值 |" + echo "|---|---|" + echo "| gpu-smoke job 结果 | ${SMOKE} |" + echo "| precheck verdict | ${VERDICT} |" + echo "| check conclusion | ${CONCL} |" + echo "" + echo "整条 run 的 Success 不代表真推理跑过 —— 本条 check 才是结论。" + } >> "$GITHUB_STEP_SUMMARY" + + # 写不进去就是自证机制本身坏了,这种情况要红(它和"没有真推理"是两回事)。 + if ! gh api -X POST "repos/${{ github.repository }}/check-runs" -f "name=${NAME}" -f "head_sha=${HEAD_SHA}" -f status=completed -f "conclusion=${CONCL}" -f "html_url=${RUN_URL}" -f "external_id=gpu-smoke-${{ github.run_id }}-${{ github.run_attempt }}" -f "title=GPU smoke - real inference attestation" -f "summary=${NAME} | verdict=${VERDICT} | gpu-smoke=${SMOKE} | run=${RUN_URL}" >/dev/null; then + echo "::error::check run 写入失败:需要 permissions.checks=write。自证链路断了,必须显眼。" + exit 1 + fi + echo "已发布 check run:${NAME} -> conclusion=${CONCL}"