From d07b711640031f00862ce5f769d931a0df5b3379 Mon Sep 17 00:00:00 2001 From: Christopher Albert Date: Thu, 16 Jul 2026 16:07:16 +0200 Subject: [PATCH 1/2] CI: cover stacked flux-pumping PR bases and base retargets --- .github/workflows/test-on-pr.yml | 41 ++++++++++++++++++++++++++------ .github/workflows/unit-tests.yml | 14 +++++++++-- 2 files changed, 46 insertions(+), 9 deletions(-) diff --git a/.github/workflows/test-on-pr.yml b/.github/workflows/test-on-pr.yml index f7ec32dc..c4356c0b 100644 --- a/.github/workflows/test-on-pr.yml +++ b/.github/workflows/test-on-pr.yml @@ -5,20 +5,47 @@ on: branches: - main - master + # 'edited' covers base-branch retargeting (guarded below so title/body + # edits do not rerun); 'labeled' lets the run-golden-record label start + # the job on stacked flux-pumping PRs, where it is label-gated because + # it builds NEO-2 three times and runs the full golden-record suites. pull_request: - types: [opened, synchronize, reopened, ready_for_review] + types: [opened, synchronize, reopened, ready_for_review, edited, labeled] branches: - main - master + - fix/flux-pumping/** + - diag/flux-pumping/** + - feat/flux-pumping/** + - integration/flux-pumping/** + workflow_dispatch: + inputs: + compare_against_main: + description: Also run the exact golden-record comparison against main + required: false + default: true + type: boolean concurrency: - group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} + group: ${{ github.workflow }}-${{ github.event.pull_request.number || (github.event_name == 'workflow_dispatch' && github.run_id) || github.ref }} cancel-in-progress: true jobs: run-golden-record: runs-on: ubuntu-24.04 - if: github.event_name == 'push' || (github.event_name == 'pull_request' && github.event.pull_request.draft == false) + # Push and manual dispatch always run. Pull requests run when non-draft + # and either based on main/master or carrying the run-golden-record + # label (stacked flux-pumping bases). 'edited' events run only on base + # retargets; 'labeled' events only for the run-golden-record label. + if: >- + github.event_name == 'push' || + github.event_name == 'workflow_dispatch' || + (github.event_name == 'pull_request' && + github.event.pull_request.draft == false && + (github.event.action != 'edited' || github.event.changes.base != null) && + (github.event.action != 'labeled' || github.event.label.name == 'run-golden-record') && + (github.base_ref == 'main' || github.base_ref == 'master' || + contains(github.event.pull_request.labels.*.name, 'run-golden-record'))) env: CC: gcc @@ -192,7 +219,7 @@ jobs: ${{ steps.run_test_stable.outputs.test_dir }}/ql/reference/out - name: Build NEO-2 (reference version - main branch) - if: github.event_name == 'pull_request' + if: github.event_name == 'pull_request' || (github.event_name == 'workflow_dispatch' && inputs.compare_against_main) id: build_reference_main run: | # Build reference version from main branch for PR comparison @@ -210,7 +237,7 @@ jobs: echo "neo2_par_main=$(pwd)/build/NEO-2-PAR/neo_2_par.x" >> $GITHUB_OUTPUT - name: Run golden record test against main version (exact check) - if: github.event_name == 'pull_request' + if: github.event_name == 'pull_request' || (github.event_name == 'workflow_dispatch' && inputs.compare_against_main) env: CURRENT_NEO2_PAR: ${{ steps.build.outputs.neo2_par }} REFERENCE_NEO2_PAR: ${{ steps.build_reference_main.outputs.neo2_par_main }} @@ -247,7 +274,7 @@ jobs: ${{ steps.run_test_stable.outputs.test_dir }}/ql/reference/out - name: Run QL performance test - if: github.event_name == 'pull_request' + if: github.event_name == 'pull_request' || (github.event_name == 'workflow_dispatch' && inputs.compare_against_main) env: CURRENT_NEO2_QL: ${{ steps.build.outputs.neo2_ql }} REFERENCE_NEO2_QL: ${{ steps.build_reference_main.outputs.neo2_ql_main }} @@ -275,7 +302,7 @@ jobs: ${{ steps.run_test_stable.outputs.test_dir }}/performance_ql/performance_report.txt - name: Run (slow) par golden record test on pull request - if: github.event_name == 'pull_request' + if: github.event_name == 'pull_request' || (github.event_name == 'workflow_dispatch' && inputs.compare_against_main) env: CURRENT_NEO2_PAR: ${{ steps.build.outputs.neo2_par }} id: run_par_test diff --git a/.github/workflows/unit-tests.yml b/.github/workflows/unit-tests.yml index a6ae5cbc..e74f1728 100644 --- a/.github/workflows/unit-tests.yml +++ b/.github/workflows/unit-tests.yml @@ -5,11 +5,18 @@ on: branches: - main - master + # 'edited' covers base-branch retargeting; the job condition below skips + # plain title/body edits. The flux-pumping patterns give stacked PRs + # (bases like fix/flux-pumping/*) unit-test coverage on every push. pull_request: - types: [opened, synchronize, reopened, ready_for_review] + types: [opened, synchronize, reopened, ready_for_review, edited] branches: - main - master + - fix/flux-pumping/** + - diag/flux-pumping/** + - feat/flux-pumping/** + - integration/flux-pumping/** workflow_dispatch: inputs: libneo_ref: @@ -24,7 +31,10 @@ concurrency: jobs: unit-tests: runs-on: ubuntu-24.04 - if: github.event_name != 'pull_request' || github.event.pull_request.draft == false + if: >- + github.event_name != 'pull_request' || + (github.event.pull_request.draft == false && + (github.event.action != 'edited' || github.event.changes.base != null)) env: CC: gcc From ca354dec3844a04f23ef36f7712bb181db5d7fe0 Mon Sep 17 00:00:00 2001 From: Christopher Albert Date: Thu, 16 Jul 2026 20:04:20 +0200 Subject: [PATCH 2/2] CI: keep required golden-record check unskippable via dispatch workflow run-golden-record is the sole required status check on main (ruleset 7026456, strict, zero required reviews). A job skipped by its 'if' reports Success, and the newest same-named check run on the head SHA is what the merge gate evaluates, so the previous commit's edited/labeled types plus job-level skip let a PR title edit or any unrelated label overwrite (or cancel-and-overwrite, via the PR-keyed cancel-in-progress group) a failing golden record with skipped=Success, unblocking merges into main. Restore test-on-pr.yml pull_request types and branches exactly to the main state so every run-golden-record check run is real; keep workflow_dispatch. Drop 'edited' from unit-tests.yml types and revert its job condition so title edits stop shadowing real unit-test results; keep the stacked base patterns, which always produce real runs. Move label additions, base retargets, and pushes to labeled stacked PRs into stacked-pr-dispatch.yml, whose differently named job re-triggers the real workflows via workflow_dispatch and can never create a skipped run-golden-record. This also removes the skipped-check noise on unlabeled stacked PRs. actionlint: same 21 pre-existing findings before and after, zero in the new file. --- .github/workflows/stacked-pr-dispatch.yml | 73 +++++++++++++++++++++++ .github/workflows/test-on-pr.yml | 31 ++++------ .github/workflows/unit-tests.yml | 15 +++-- 3 files changed, 92 insertions(+), 27 deletions(-) create mode 100644 .github/workflows/stacked-pr-dispatch.yml diff --git a/.github/workflows/stacked-pr-dispatch.yml b/.github/workflows/stacked-pr-dispatch.yml new file mode 100644 index 00000000..1a8dcce1 --- /dev/null +++ b/.github/workflows/stacked-pr-dispatch.yml @@ -0,0 +1,73 @@ +name: Stacked PR Dispatch + +# Companion to test-on-pr.yml and unit-tests.yml for the stacked +# flux-pumping PR chains. Those workflows keep their pull_request types +# untouched so the required run-golden-record check is never created in a +# skipped state: a job skipped by its 'if' reports Success, and the newest +# check run with the same name on the head SHA is what the merge gate on +# main evaluates, so a skipped run of the required job would launder a red +# golden record into a mergeable PR. This workflow instead reacts to the +# events the real workflows must not see (run-golden-record label +# additions, base retargets, pushes to labeled stacked PRs) with a +# differently named job and re-triggers them via workflow_dispatch, which +# always produces real runs. +# +# workflow_dispatch uses the workflow definition on the target ref: +# dispatching test-on-pr.yml on a stacked head works only once that head +# contains the commit that added its workflow_dispatch trigger (rebase the +# stack onto an updated main first). + +on: + pull_request: + types: [opened, synchronize, reopened, ready_for_review, labeled, edited] + branches: + - main + - master + - fix/flux-pumping/** + - diag/flux-pumping/** + - feat/flux-pumping/** + - integration/flux-pumping/** + +permissions: + actions: write + +jobs: + # NOT named run-golden-record or unit-tests: skipped runs of this job + # must never shadow the real checks. + dispatch-stacked-checks: + runs-on: ubuntu-24.04 + # React only to: base retargets ('edited' with a base change), the + # run-golden-record label being added, and events on labeled stacked + # PRs. Everything else is already covered by the pull_request triggers + # of the real workflows. Fork heads are excluded because + # workflow_dispatch can only target refs in this repository. + if: >- + github.event.pull_request.draft == false && + github.event.pull_request.head.repo.full_name == github.repository && + ((github.event.action == 'edited' && github.event.changes.base != null) || + (github.event.action == 'labeled' && github.event.label.name == 'run-golden-record') || + (github.event.action != 'edited' && github.event.action != 'labeled' && + github.base_ref != 'main' && github.base_ref != 'master' && + contains(github.event.pull_request.labels.*.name, 'run-golden-record'))) + + env: + GH_TOKEN: ${{ github.token }} + HEAD_REF: ${{ github.event.pull_request.head.ref }} + + steps: + - name: Dispatch unit tests on base retarget + # unit-tests.yml does not listen to 'edited', so a retargeted PR + # gets its unit-test run from here. + if: github.event.action == 'edited' + run: gh workflow run unit-tests.yml --repo "$GITHUB_REPOSITORY" --ref "$HEAD_REF" + + - name: Dispatch golden record + # Label events and pushes to labeled stacked PRs always dispatch; + # a base retarget dispatches when the new base is main/master + # (where run-golden-record is required) or the PR carries the + # run-golden-record label. + if: >- + github.event.action != 'edited' || + github.base_ref == 'main' || github.base_ref == 'master' || + contains(github.event.pull_request.labels.*.name, 'run-golden-record') + run: gh workflow run test-on-pr.yml --repo "$GITHUB_REPOSITORY" --ref "$HEAD_REF" diff --git a/.github/workflows/test-on-pr.yml b/.github/workflows/test-on-pr.yml index c4356c0b..cc7395f9 100644 --- a/.github/workflows/test-on-pr.yml +++ b/.github/workflows/test-on-pr.yml @@ -5,19 +5,18 @@ on: branches: - main - master - # 'edited' covers base-branch retargeting (guarded below so title/body - # edits do not rerun); 'labeled' lets the run-golden-record label start - # the job on stacked flux-pumping PRs, where it is label-gated because - # it builds NEO-2 three times and runs the full golden-record suites. + # run-golden-record is the required status check on main. Its types and + # branches stay exactly as they are so that every check run named + # run-golden-record comes from a real run: a job skipped by its 'if' + # reports Success and would satisfy the branch protection gate. Label + # additions, base retargets, and pushes to labeled stacked flux-pumping + # PRs are handled by stacked-pr-dispatch.yml, which re-triggers this + # workflow via workflow_dispatch under a differently named job. pull_request: - types: [opened, synchronize, reopened, ready_for_review, edited, labeled] + types: [opened, synchronize, reopened, ready_for_review] branches: - main - master - - fix/flux-pumping/** - - diag/flux-pumping/** - - feat/flux-pumping/** - - integration/flux-pumping/** workflow_dispatch: inputs: compare_against_main: @@ -33,19 +32,13 @@ concurrency: jobs: run-golden-record: runs-on: ubuntu-24.04 - # Push and manual dispatch always run. Pull requests run when non-draft - # and either based on main/master or carrying the run-golden-record - # label (stacked flux-pumping bases). 'edited' events run only on base - # retargets; 'labeled' events only for the run-golden-record label. + # Do not add event- or label-based skip conditions here: a skipped + # run-golden-record counts as Success for the required status check + # on main. Gating for stacked PRs lives in stacked-pr-dispatch.yml. if: >- github.event_name == 'push' || github.event_name == 'workflow_dispatch' || - (github.event_name == 'pull_request' && - github.event.pull_request.draft == false && - (github.event.action != 'edited' || github.event.changes.base != null) && - (github.event.action != 'labeled' || github.event.label.name == 'run-golden-record') && - (github.base_ref == 'main' || github.base_ref == 'master' || - contains(github.event.pull_request.labels.*.name, 'run-golden-record'))) + (github.event_name == 'pull_request' && github.event.pull_request.draft == false) env: CC: gcc diff --git a/.github/workflows/unit-tests.yml b/.github/workflows/unit-tests.yml index e74f1728..1c99f32a 100644 --- a/.github/workflows/unit-tests.yml +++ b/.github/workflows/unit-tests.yml @@ -5,11 +5,13 @@ on: branches: - main - master - # 'edited' covers base-branch retargeting; the job condition below skips - # plain title/body edits. The flux-pumping patterns give stacked PRs - # (bases like fix/flux-pumping/*) unit-test coverage on every push. + # The flux-pumping patterns give stacked PRs (bases like + # fix/flux-pumping/*) unit-test coverage on every push. 'edited' stays + # out of types so a title/body edit never spawns a skipped run that + # shadows the latest real unit-tests result; base retargets are handled + # by stacked-pr-dispatch.yml via workflow_dispatch. pull_request: - types: [opened, synchronize, reopened, ready_for_review, edited] + types: [opened, synchronize, reopened, ready_for_review] branches: - main - master @@ -31,10 +33,7 @@ concurrency: jobs: unit-tests: runs-on: ubuntu-24.04 - if: >- - github.event_name != 'pull_request' || - (github.event.pull_request.draft == false && - (github.event.action != 'edited' || github.event.changes.base != null)) + if: github.event_name != 'pull_request' || github.event.pull_request.draft == false env: CC: gcc