From 90e4c633632c6fb192f4397101440e0180090306 Mon Sep 17 00:00:00 2001 From: sunrisepeak Date: Sun, 9 Aug 2026 03:16:59 +0800 Subject: [PATCH] =?UTF-8?q?ci:=20=E5=90=88=E5=85=A5=20main=20=E5=8F=AA?= =?UTF-8?q?=E8=B7=91=E5=AF=B9=E5=BA=94=E7=9A=84=E6=88=90=E5=91=98,?= =?UTF-8?q?=E5=85=A8=E9=87=8F=E9=80=80=E5=88=B0=E6=AF=8F=E5=91=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 每次合并都在重跑全部 67 个成员 × 三平台 —— 约 11 小时 runner 时间,用来复核 几分钟前那个 PR 已经绿过的同一批东西。 原因是 select 的第一行 `event != pull_request -> full`。它的前提是"push 没有 可 diff 的 base",而这个前提不成立:本仓的合并都是 squash,`github.event.before` 就是上一个 main,`before..HEAD` 复现出来的文件列表和 PR 当时看到的一模一样。 拿 698b95ee 实测,两边都选出 `redis-plus-plus redis-plus-plus-v133`,十个改动 文件逐个对得上。 ## 事件分派 pull_request 三点 diff(自己的提交对 merge base) push 两点 diff(这条分支上真正落下的东西) 其他 全量 schedule 和 workflow_dispatch 保持全量:它们表达的不是"有改动落地",而是 "把所有东西查一遍",那正是下面那张网的意义。 push 的两个兜底都倒向全量:before 是全零(建分支)或那个对象不在历史里 (force-push 把它丢了)时,没有可 diff 的东西,重跑好过猜。 ## 定时从每天改成每周 选择性测试结构上看不到的只有一件事:两个改动各自绿、落在一起互相踩 —— 两个 PR 的 diff 都没提到那个坏掉的成员,所以两个都选不中它。全量是这件事的网。 这张网每天撒一次和每七天撒一次,抓到的是同一批交互;而全量已经不便宜了(加 了第二条 linux 工具链腿之后约 11 小时)。改成周日 06:00 UTC。手动触发本来就 在,想立刻验一遍不用等。 五条路径都验过:schedule / dispatch / before 全零 / before 不在历史 → 全量; before 正常 → 选择性。 --- .github/workflows/validate.yml | 68 ++++++++++++++++++++++++++++------ 1 file changed, 57 insertions(+), 11 deletions(-) diff --git a/.github/workflows/validate.yml b/.github/workflows/validate.yml index 10bef6e..c6d2ddf 100644 --- a/.github/workflows/validate.yml +++ b/.github/workflows/validate.yml @@ -9,8 +9,18 @@ on: push: branches: [main] schedule: - # nightly full regression — exercises every workspace member regardless of diff - - cron: "0 6 * * *" + # WEEKLY full regression — every workspace member, regardless of diff. + # + # This is the safety net for the one thing selective testing structurally + # cannot see: two changes that are each green on their own and break each + # other once both have landed. Neither PR's diff names the member that + # breaks, so neither PR selects it. + # + # Weekly rather than nightly because a full run is no longer cheap — with + # the second linux toolchain leg it is ~11 hours of runner time — and the + # net catches the same interactions whether it is cast every day or every + # seven. Sunday 06:00 UTC. + - cron: "0 6 * * 0" workflow_dispatch: inputs: cache: @@ -326,21 +336,57 @@ jobs: # package. Map changed files → affected members and test only those: # pkgs//.lua → members whose mcpp.toml references # tests/examples//** → member - # Run the FULL workspace when the change can affect everything: - # non-PR events (push to main, the nightly cron, dispatch), this - # workflow file (it carries the mcpp version pins, so a version bump - # always re-validates every package), a non-member edit to the - # workspace manifest, or shared test scripts. Docs-only and tools/-only - # changes select nothing. + # A push to main is mapped the same way — the merge's own diff — so a + # merge costs what its PR cost. Run the FULL workspace when the change + # can affect everything: the weekly cron and manual dispatch (which mean + # "check everything" by definition), this workflow file (it carries the + # mcpp version pins, so a version bump always re-validates every + # package), a non-member edit to the workspace manifest, or shared test + # scripts. Docs-only and tools/-only changes select nothing. # Note: bash 3.2 on macOS runners — no associative arrays here. - name: Select affected workspace members id: plan shell: bash run: | full() { echo "MEMBERS=__ALL__" >> "$GITHUB_ENV"; echo "full run: $1"; exit 0; } - [ "${{ github.event_name }}" = "pull_request" ] || full "event=${{ github.event_name }}" - base="origin/${{ github.base_ref }}" - changed=$(git diff --name-only "$base"...HEAD) + + # A push to main has a diff too — it was just never asked for. + # + # This used to be `event != pull_request -> full`, so every merge + # re-tested all 67 members on all three platforms: ~11 hours of + # runner time to re-confirm what the PR had already gone green on + # minutes earlier. The premise was that a push has no base to diff + # against, and that is not true: merges here are squashes, so + # `github.event.before` is the previous main and + # `before..HEAD` reproduces exactly the file list the PR saw + # (verified on 698b95ee — same ten paths). + # + # schedule and workflow_dispatch stay full. They are not "a change + # landed", they are "check everything", which is the whole point of + # the weekly net above. + # + # Two-dot for push, three-dot for pull_request, deliberately: a PR + # wants its own commits against the merge base, while a push wants + # what actually landed on this branch. + case "${{ github.event_name }}" in + pull_request) + base="origin/${{ github.base_ref }}"; range="$base...HEAD" ;; + push) + base="${{ github.event.before }}" + # All-zero on branch creation; absent object after a + # force-push that dropped it. Either way there is nothing to + # diff against, and guessing is worse than re-testing. + case "$base" in + ""|0000000000000000000000000000000000000000) + full "push with no predecessor" ;; + esac + git cat-file -e "$base^{commit}" 2>/dev/null \ + || full "push predecessor $base not in history" + range="$base..HEAD" ;; + *) + full "event=${{ github.event_name }}" ;; + esac + changed=$(git diff --name-only $range) printf 'changed files vs %s:\n%s\n' "$base" "$changed" sel="" add() { case " $sel " in *" $1 "*) ;; *) sel="$sel $1" ;; esac; }