From c7468fba79bb1fcbc3404adf2adace983d4e551d Mon Sep 17 00:00:00 2001 From: Dmitrii Vasilev Date: Mon, 5 Oct 2026 02:11:28 +0700 Subject: [PATCH 1/3] ci: give the 102 push workflows a dispatch-safe concurrency group Eight merges in 3m22s on 2026-10-04 started 220 push runs on main across 66 workflows, and jobs waited ~25 min for a runner (#869). 102 of the 104 workflows that run on push to main had no concurrency group, so nothing superseded anything. Each of them now has, at top level: concurrency: group: ${{ github.workflow_ref }}-${{ github.event_name == 'workflow_dispatch' && github.run_id || github.event_name }} cancel-in-progress: ${{ github.event_name == 'pull_request' }} - push: one run active and one pending per file and branch; a newer commit replaces the pending run, and the active run is not cancelled. - pull_request: a new commit cancels the old commit's run. - workflow_dispatch: a group per run, so a push never cancels a manual run and two dispatches with different inputs never cancel each other. 85 of the 102 can be dispatched; #647's group (workflow + ref, cancel-in-progress always) would have done both. - The key is workflow_ref (the file), not github.workflow (the name): conformance-golden-selftests.yml and conformance-selftest.yml are both named "Conformance golden self-tests" and would have shared a group. Skipped: 2 workflows that already have a group, 18 with no push trigger, 0 reusable workflows. 102 files, 6 lines each, 0 deletions; every edited file re-parses with the block at top level and no job carrying one. Refs #869 #647 #851 Co-Authored-By: Claude Opus 5.5 --- .github/workflows/artefact-agreement.yml | 6 ++++++ .github/workflows/author-docs-gate.yml | 6 ++++++ .github/workflows/ax7203-blinky-bitstream.yml | 6 ++++++ .github/workflows/ax7203-corona-decode.yml | 6 ++++++ .github/workflows/ax7203-dual-clk-heartbeat.yml | 6 ++++++ .github/workflows/ax7203-format-cost.yml | 6 ++++++ .github/workflows/ax7203-gf10-add.yml | 6 ++++++ .github/workflows/ax7203-gf10-mul.yml | 6 ++++++ .github/workflows/ax7203-gf10-sub.yml | 6 ++++++ .github/workflows/ax7203-gf12-clean.yml | 6 ++++++ .github/workflows/ax7203-gf12-mul-clean.yml | 6 ++++++ .github/workflows/ax7203-gf12-sub.yml | 6 ++++++ .github/workflows/ax7203-gf14-add.yml | 6 ++++++ .github/workflows/ax7203-gf14-mul.yml | 6 ++++++ .github/workflows/ax7203-gf14-sub.yml | 6 ++++++ .github/workflows/ax7203-gf16-clean.yml | 6 ++++++ .github/workflows/ax7203-gf16-conformance.yml | 6 ++++++ .github/workflows/ax7203-gf16-mul-clean.yml | 6 ++++++ .github/workflows/ax7203-gf16-sub.yml | 6 ++++++ .github/workflows/ax7203-gf20-clean.yml | 6 ++++++ .github/workflows/ax7203-gf20-mul-clean.yml | 6 ++++++ .github/workflows/ax7203-gf20-sub.yml | 6 ++++++ .github/workflows/ax7203-gf24-clean.yml | 6 ++++++ .github/workflows/ax7203-gf24-mul-clean.yml | 6 ++++++ .github/workflows/ax7203-gf24-sub.yml | 6 ++++++ .github/workflows/ax7203-gf32-add.yml | 6 ++++++ .github/workflows/ax7203-gf32-mul.yml | 6 ++++++ .github/workflows/ax7203-gf32-sub.yml | 6 ++++++ .github/workflows/ax7203-gf4-clean.yml | 6 ++++++ .github/workflows/ax7203-gf4-mul-clean.yml | 6 ++++++ .github/workflows/ax7203-gf4-sub.yml | 6 ++++++ .github/workflows/ax7203-gf6-clean.yml | 6 ++++++ .github/workflows/ax7203-gf6-mul-clean.yml | 6 ++++++ .github/workflows/ax7203-gf6-sub.yml | 6 ++++++ .github/workflows/ax7203-gf8-clean.yml | 6 ++++++ .github/workflows/ax7203-gf8-mul-clean.yml | 6 ++++++ .github/workflows/ax7203-gf8-sub.yml | 6 ++++++ .github/workflows/ax7203-gfternary-mul.yml | 6 ++++++ .github/workflows/ax7203-identity-echo.yml | 6 ++++++ .github/workflows/ax7203-led-onehot.yml | 6 ++++++ .github/workflows/ax7203-rx-echo-hb.yml | 6 ++++++ .github/workflows/ax7203-trinet-baud.yml | 6 ++++++ .github/workflows/ax7203-trinet-dna-probe.yml | 6 ++++++ .github/workflows/ax7203-trinet-fleet.yml | 6 ++++++ .github/workflows/ax7203-trinet-mac32.yml | 6 ++++++ .github/workflows/ax7203-trinet-node-v2.yml | 6 ++++++ .github/workflows/ax7203-uart-loopback.yml | 6 ++++++ .github/workflows/ax7203-uart-tx-probe.yml | 6 ++++++ .github/workflows/brain-ci.yml | 6 ++++++ .github/workflows/build-paper.yml | 6 ++++++ .github/workflows/ci.yml | 6 ++++++ .github/workflows/claim-consistency.yml | 6 ++++++ .github/workflows/codegen.yml | 6 ++++++ .github/workflows/conformance-frame-alignment.yml | 6 ++++++ .github/workflows/conformance-golden-selftests.yml | 6 ++++++ .github/workflows/conformance-selftest.yml | 6 ++++++ .github/workflows/counted-claim-gate.yml | 6 ++++++ .github/workflows/decode-verify.yml | 6 ++++++ .github/workflows/decoder-conformance.yml | 6 ++++++ .github/workflows/dev-enforcement.yml | 6 ++++++ .github/workflows/doc-refs.yml | 6 ++++++ .github/workflows/exhaustive-claim-gate.yml | 6 ++++++ .github/workflows/figure-names.yml | 6 ++++++ .github/workflows/format-table-gate.yml | 6 ++++++ .github/workflows/fpga-bitstream.yml | 6 ++++++ .github/workflows/fpga-ci.yml | 6 ++++++ .github/workflows/fpga-docker.yml | 6 ++++++ .github/workflows/fpga-formal-comb.yml | 6 ++++++ .github/workflows/fpga-formal.yml | 6 ++++++ .github/workflows/fpga-hslm-bitstream.yml | 6 ++++++ .github/workflows/fpga-regression.yml | 6 ++++++ .github/workflows/gate-ratchet.yml | 6 ++++++ .github/workflows/gates-can-fail.yml | 6 ++++++ .github/workflows/gf-adder-ref-tb.yml | 6 ++++++ .github/workflows/hardware-row-gate.yml | 6 ++++++ .github/workflows/harness.yml | 6 ++++++ .github/workflows/latex-hygiene.yml | 6 ++++++ .github/workflows/lut-report.yml | 6 ++++++ .github/workflows/module-loader-gate.yml | 6 ++++++ .github/workflows/narrow-register-gate.yml | 6 ++++++ .github/workflows/orphan-artefacts.yml | 6 ++++++ .github/workflows/overfull.yml | 6 ++++++ .github/workflows/pack-layout-gate.yml | 6 ++++++ .github/workflows/paper-numbers.yml | 6 ++++++ .github/workflows/reachability-ratchet.yml | 6 ++++++ .github/workflows/ref-kinds.yml | 6 ++++++ .github/workflows/reproducibility-gate.yml | 6 ++++++ .github/workflows/rtl-check.yml | 6 ++++++ .github/workflows/rtl-datapath-gate.yml | 6 ++++++ .github/workflows/run-syntax-gate.yml | 6 ++++++ .github/workflows/script-rot.yml | 6 ++++++ .github/workflows/self-consistency.yml | 6 ++++++ .github/workflows/stale-citation-gate.yml | 6 ++++++ .github/workflows/tee-pipefail-gate.yml | 6 ++++++ .github/workflows/tnf-boundary-gate.yml | 6 ++++++ .github/workflows/tri-build.yml | 6 ++++++ .github/workflows/trinet-portability.yml | 6 ++++++ .github/workflows/undefined-outputs.yml | 6 ++++++ .github/workflows/withdrawn-live.yml | 6 ++++++ .github/workflows/workflow-path-gate.yml | 6 ++++++ .github/workflows/wrapper-conversion-conformance.yml | 6 ++++++ .github/workflows/wrapper-fsm-sim.yml | 6 ++++++ 102 files changed, 612 insertions(+) diff --git a/.github/workflows/artefact-agreement.yml b/.github/workflows/artefact-agreement.yml index 7927484d2f..856a4beee3 100644 --- a/.github/workflows/artefact-agreement.yml +++ b/.github/workflows/artefact-agreement.yml @@ -18,6 +18,12 @@ on: push: branches: [main, master] +# A newer push or PR commit replaces this file's pending run instead of +# queueing behind it; a dispatch is never cancelled (gHashTag/trinity-fpga#869). +concurrency: + group: ${{ github.workflow_ref }}-${{ github.event_name == 'workflow_dispatch' && github.run_id || github.event_name }} + cancel-in-progress: ${{ github.event_name == 'pull_request' }} + jobs: artefact-agreement: runs-on: ubuntu-latest diff --git a/.github/workflows/author-docs-gate.yml b/.github/workflows/author-docs-gate.yml index b2dd4dd0a7..903e108110 100644 --- a/.github/workflows/author-docs-gate.yml +++ b/.github/workflows/author-docs-gate.yml @@ -26,6 +26,12 @@ on: - '.github/workflows/author-docs-gate.yml' workflow_dispatch: +# A newer push or PR commit replaces this file's pending run instead of +# queueing behind it; a dispatch is never cancelled (gHashTag/trinity-fpga#869). +concurrency: + group: ${{ github.workflow_ref }}-${{ github.event_name == 'workflow_dispatch' && github.run_id || github.event_name }} + cancel-in-progress: ${{ github.event_name == 'pull_request' }} + jobs: docs: runs-on: ubuntu-latest diff --git a/.github/workflows/ax7203-blinky-bitstream.yml b/.github/workflows/ax7203-blinky-bitstream.yml index 1d38679a0a..5150211501 100644 --- a/.github/workflows/ax7203-blinky-bitstream.yml +++ b/.github/workflows/ax7203-blinky-bitstream.yml @@ -23,6 +23,12 @@ on: - 'specs/fpga/constraints/ax7203.xdc' - '.github/workflows/ax7203-blinky-bitstream.yml' +# A newer push or PR commit replaces this file's pending run instead of +# queueing behind it; a dispatch is never cancelled (gHashTag/trinity-fpga#869). +concurrency: + group: ${{ github.workflow_ref }}-${{ github.event_name == 'workflow_dispatch' && github.run_id || github.event_name }} + cancel-in-progress: ${{ github.event_name == 'pull_request' }} + jobs: bitstream: name: Generate AX7203 Blinky Bitstream diff --git a/.github/workflows/ax7203-corona-decode.yml b/.github/workflows/ax7203-corona-decode.yml index 94810fb721..ec0871f5b6 100644 --- a/.github/workflows/ax7203-corona-decode.yml +++ b/.github/workflows/ax7203-corona-decode.yml @@ -29,6 +29,12 @@ on: - 'external/tt-trinity-corona' - 'fpga/openxc7-synth/corona_decode_ax7203.xdc' - '.github/workflows/ax7203-corona-decode.yml' +# A newer push or PR commit replaces this file's pending run instead of +# queueing behind it; a dispatch is never cancelled (gHashTag/trinity-fpga#869). +concurrency: + group: ${{ github.workflow_ref }}-${{ github.event_name == 'workflow_dispatch' && github.run_id || github.event_name }} + cancel-in-progress: ${{ github.event_name == 'pull_request' }} + jobs: bitstream: runs-on: ubuntu-latest diff --git a/.github/workflows/ax7203-dual-clk-heartbeat.yml b/.github/workflows/ax7203-dual-clk-heartbeat.yml index c256ea23a6..5b26ab43dc 100644 --- a/.github/workflows/ax7203-dual-clk-heartbeat.yml +++ b/.github/workflows/ax7203-dual-clk-heartbeat.yml @@ -13,6 +13,12 @@ on: - 'specs/fpga/constraints/dual_clk_heartbeat_ax7203.xdc' - '.github/workflows/ax7203-dual-clk-heartbeat.yml' +# A newer push or PR commit replaces this file's pending run instead of +# queueing behind it; a dispatch is never cancelled (gHashTag/trinity-fpga#869). +concurrency: + group: ${{ github.workflow_ref }}-${{ github.event_name == 'workflow_dispatch' && github.run_id || github.event_name }} + cancel-in-progress: ${{ github.event_name == 'pull_request' }} + jobs: bitstream: name: Generate AX7203 Dual-Clock Heartbeat Bitstream diff --git a/.github/workflows/ax7203-format-cost.yml b/.github/workflows/ax7203-format-cost.yml index 085b3c0ea1..59d63905e3 100644 --- a/.github/workflows/ax7203-format-cost.yml +++ b/.github/workflows/ax7203-format-cost.yml @@ -25,6 +25,12 @@ on: - 'fpga/openxc7-synth/gf_adder_param.v' - 'fpga/openxc7-synth/gf_mul_param.v' +# A newer push or PR commit replaces this file's pending run instead of +# queueing behind it; a dispatch is never cancelled (gHashTag/trinity-fpga#869). +concurrency: + group: ${{ github.workflow_ref }}-${{ github.event_name == 'workflow_dispatch' && github.run_id || github.event_name }} + cancel-in-progress: ${{ github.event_name == 'pull_request' }} + jobs: synth: runs-on: ubuntu-latest diff --git a/.github/workflows/ax7203-gf10-add.yml b/.github/workflows/ax7203-gf10-add.yml index 524a818b64..6b00cc2084 100644 --- a/.github/workflows/ax7203-gf10-add.yml +++ b/.github/workflows/ax7203-gf10-add.yml @@ -4,6 +4,12 @@ on: push: branches: [main] paths: ['fpga/vivado/gf10_add_ax7203.v','fpga/openxc7-synth/gf_adder_param.v','conformance/gf_ref.py','.github/workflows/ax7203-gf10-add.yml'] +# A newer push or PR commit replaces this file's pending run instead of +# queueing behind it; a dispatch is never cancelled (gHashTag/trinity-fpga#869). +concurrency: + group: ${{ github.workflow_ref }}-${{ github.event_name == 'workflow_dispatch' && github.run_id || github.event_name }} + cancel-in-progress: ${{ github.event_name == 'pull_request' }} + jobs: bitstream: runs-on: ubuntu-latest diff --git a/.github/workflows/ax7203-gf10-mul.yml b/.github/workflows/ax7203-gf10-mul.yml index 0179caa2ef..d01a534bee 100644 --- a/.github/workflows/ax7203-gf10-mul.yml +++ b/.github/workflows/ax7203-gf10-mul.yml @@ -4,6 +4,12 @@ on: push: branches: [main] paths: ['fpga/vivado/gf10_mul_ax7203.v','fpga/openxc7-synth/gf_mul_param.v','.github/workflows/ax7203-gf10-mul.yml'] +# A newer push or PR commit replaces this file's pending run instead of +# queueing behind it; a dispatch is never cancelled (gHashTag/trinity-fpga#869). +concurrency: + group: ${{ github.workflow_ref }}-${{ github.event_name == 'workflow_dispatch' && github.run_id || github.event_name }} + cancel-in-progress: ${{ github.event_name == 'pull_request' }} + jobs: bitstream: runs-on: ubuntu-latest diff --git a/.github/workflows/ax7203-gf10-sub.yml b/.github/workflows/ax7203-gf10-sub.yml index 52718d92e2..21ad2c27ff 100644 --- a/.github/workflows/ax7203-gf10-sub.yml +++ b/.github/workflows/ax7203-gf10-sub.yml @@ -4,6 +4,12 @@ on: push: branches: [main] paths: ['fpga/vivado/gf10_sub_ax7203.v','fpga/openxc7-synth/gf_adder_param.v','.github/workflows/ax7203-gf10-sub.yml'] +# A newer push or PR commit replaces this file's pending run instead of +# queueing behind it; a dispatch is never cancelled (gHashTag/trinity-fpga#869). +concurrency: + group: ${{ github.workflow_ref }}-${{ github.event_name == 'workflow_dispatch' && github.run_id || github.event_name }} + cancel-in-progress: ${{ github.event_name == 'pull_request' }} + jobs: bitstream: runs-on: ubuntu-latest diff --git a/.github/workflows/ax7203-gf12-clean.yml b/.github/workflows/ax7203-gf12-clean.yml index 855fcc843f..37416db95c 100644 --- a/.github/workflows/ax7203-gf12-clean.yml +++ b/.github/workflows/ax7203-gf12-clean.yml @@ -4,6 +4,12 @@ on: push: branches: [main] paths: ['fpga/vivado/gf12_clean_ax7203.v','fpga/openxc7-synth/gf_adder_param.v','.github/workflows/ax7203-gf12-clean.yml'] +# A newer push or PR commit replaces this file's pending run instead of +# queueing behind it; a dispatch is never cancelled (gHashTag/trinity-fpga#869). +concurrency: + group: ${{ github.workflow_ref }}-${{ github.event_name == 'workflow_dispatch' && github.run_id || github.event_name }} + cancel-in-progress: ${{ github.event_name == 'pull_request' }} + jobs: bitstream: runs-on: ubuntu-latest diff --git a/.github/workflows/ax7203-gf12-mul-clean.yml b/.github/workflows/ax7203-gf12-mul-clean.yml index 5adf1e9c46..c6852324f3 100644 --- a/.github/workflows/ax7203-gf12-mul-clean.yml +++ b/.github/workflows/ax7203-gf12-mul-clean.yml @@ -4,6 +4,12 @@ on: push: branches: [main] paths: ['fpga/vivado/gf12_mul_ax7203.v','fpga/openxc7-synth/gf_mul_param.v','.github/workflows/ax7203-gf12-mul-clean.yml'] +# A newer push or PR commit replaces this file's pending run instead of +# queueing behind it; a dispatch is never cancelled (gHashTag/trinity-fpga#869). +concurrency: + group: ${{ github.workflow_ref }}-${{ github.event_name == 'workflow_dispatch' && github.run_id || github.event_name }} + cancel-in-progress: ${{ github.event_name == 'pull_request' }} + jobs: bitstream: runs-on: ubuntu-latest diff --git a/.github/workflows/ax7203-gf12-sub.yml b/.github/workflows/ax7203-gf12-sub.yml index adc5d95e3d..c1045fe1ad 100644 --- a/.github/workflows/ax7203-gf12-sub.yml +++ b/.github/workflows/ax7203-gf12-sub.yml @@ -4,6 +4,12 @@ on: push: branches: [main] paths: ['fpga/vivado/gf12_sub_ax7203.v','fpga/openxc7-synth/gf_adder_param.v','.github/workflows/ax7203-gf12-sub.yml'] +# A newer push or PR commit replaces this file's pending run instead of +# queueing behind it; a dispatch is never cancelled (gHashTag/trinity-fpga#869). +concurrency: + group: ${{ github.workflow_ref }}-${{ github.event_name == 'workflow_dispatch' && github.run_id || github.event_name }} + cancel-in-progress: ${{ github.event_name == 'pull_request' }} + jobs: bitstream: runs-on: ubuntu-latest diff --git a/.github/workflows/ax7203-gf14-add.yml b/.github/workflows/ax7203-gf14-add.yml index f5168a0bec..460e3cacf2 100644 --- a/.github/workflows/ax7203-gf14-add.yml +++ b/.github/workflows/ax7203-gf14-add.yml @@ -4,6 +4,12 @@ on: push: branches: [main] paths: ['fpga/vivado/gf14_add_ax7203.v','fpga/openxc7-synth/gf_adder_param.v','conformance/gf_ref.py','.github/workflows/ax7203-gf14-add.yml'] +# A newer push or PR commit replaces this file's pending run instead of +# queueing behind it; a dispatch is never cancelled (gHashTag/trinity-fpga#869). +concurrency: + group: ${{ github.workflow_ref }}-${{ github.event_name == 'workflow_dispatch' && github.run_id || github.event_name }} + cancel-in-progress: ${{ github.event_name == 'pull_request' }} + jobs: bitstream: runs-on: ubuntu-latest diff --git a/.github/workflows/ax7203-gf14-mul.yml b/.github/workflows/ax7203-gf14-mul.yml index b9d147eeb2..04c4504c2a 100644 --- a/.github/workflows/ax7203-gf14-mul.yml +++ b/.github/workflows/ax7203-gf14-mul.yml @@ -4,6 +4,12 @@ on: push: branches: [main] paths: ['fpga/vivado/gf14_mul_ax7203.v','fpga/openxc7-synth/gf_mul_param.v','.github/workflows/ax7203-gf14-mul.yml'] +# A newer push or PR commit replaces this file's pending run instead of +# queueing behind it; a dispatch is never cancelled (gHashTag/trinity-fpga#869). +concurrency: + group: ${{ github.workflow_ref }}-${{ github.event_name == 'workflow_dispatch' && github.run_id || github.event_name }} + cancel-in-progress: ${{ github.event_name == 'pull_request' }} + jobs: bitstream: runs-on: ubuntu-latest diff --git a/.github/workflows/ax7203-gf14-sub.yml b/.github/workflows/ax7203-gf14-sub.yml index 35dfcc2e5a..ffbbc9f010 100644 --- a/.github/workflows/ax7203-gf14-sub.yml +++ b/.github/workflows/ax7203-gf14-sub.yml @@ -4,6 +4,12 @@ on: push: branches: [main] paths: ['fpga/vivado/gf14_sub_ax7203.v','fpga/openxc7-synth/gf_adder_param.v','.github/workflows/ax7203-gf14-sub.yml'] +# A newer push or PR commit replaces this file's pending run instead of +# queueing behind it; a dispatch is never cancelled (gHashTag/trinity-fpga#869). +concurrency: + group: ${{ github.workflow_ref }}-${{ github.event_name == 'workflow_dispatch' && github.run_id || github.event_name }} + cancel-in-progress: ${{ github.event_name == 'pull_request' }} + jobs: bitstream: runs-on: ubuntu-latest diff --git a/.github/workflows/ax7203-gf16-clean.yml b/.github/workflows/ax7203-gf16-clean.yml index b9ec4217cf..5589cc2108 100644 --- a/.github/workflows/ax7203-gf16-clean.yml +++ b/.github/workflows/ax7203-gf16-clean.yml @@ -10,6 +10,12 @@ on: - 'specs/fpga/constraints/gf16_clean_ax7203.xdc' - '.github/workflows/ax7203-gf16-clean.yml' +# A newer push or PR commit replaces this file's pending run instead of +# queueing behind it; a dispatch is never cancelled (gHashTag/trinity-fpga#869). +concurrency: + group: ${{ github.workflow_ref }}-${{ github.event_name == 'workflow_dispatch' && github.run_id || github.event_name }} + cancel-in-progress: ${{ github.event_name == 'pull_request' }} + jobs: bitstream: runs-on: ubuntu-latest diff --git a/.github/workflows/ax7203-gf16-conformance.yml b/.github/workflows/ax7203-gf16-conformance.yml index 9e1c3e903f..8b2cd2cdb0 100644 --- a/.github/workflows/ax7203-gf16-conformance.yml +++ b/.github/workflows/ax7203-gf16-conformance.yml @@ -34,6 +34,12 @@ on: - 'specs/fpga/constraints/gf16_ax7203.xdc' - '.github/workflows/ax7203-gf16-conformance.yml' +# A newer push or PR commit replaces this file's pending run instead of +# queueing behind it; a dispatch is never cancelled (gHashTag/trinity-fpga#869). +concurrency: + group: ${{ github.workflow_ref }}-${{ github.event_name == 'workflow_dispatch' && github.run_id || github.event_name }} + cancel-in-progress: ${{ github.event_name == 'pull_request' }} + jobs: bitstream: name: Generate AX7203 GF16 Codec Bitstream diff --git a/.github/workflows/ax7203-gf16-mul-clean.yml b/.github/workflows/ax7203-gf16-mul-clean.yml index b0f9738851..857646864b 100644 --- a/.github/workflows/ax7203-gf16-mul-clean.yml +++ b/.github/workflows/ax7203-gf16-mul-clean.yml @@ -4,6 +4,12 @@ on: push: branches: [main] paths: ['fpga/vivado/gf16_mul_ax7203.v','fpga/openxc7-synth/gf_mul_param.v','.github/workflows/ax7203-gf16-mul-clean.yml'] +# A newer push or PR commit replaces this file's pending run instead of +# queueing behind it; a dispatch is never cancelled (gHashTag/trinity-fpga#869). +concurrency: + group: ${{ github.workflow_ref }}-${{ github.event_name == 'workflow_dispatch' && github.run_id || github.event_name }} + cancel-in-progress: ${{ github.event_name == 'pull_request' }} + jobs: bitstream: runs-on: ubuntu-latest diff --git a/.github/workflows/ax7203-gf16-sub.yml b/.github/workflows/ax7203-gf16-sub.yml index ed1bb1017e..54a4131829 100644 --- a/.github/workflows/ax7203-gf16-sub.yml +++ b/.github/workflows/ax7203-gf16-sub.yml @@ -4,6 +4,12 @@ on: push: branches: [main] paths: ['fpga/vivado/gf16_sub_ax7203.v','fpga/openxc7-synth/gf_adder_param.v','.github/workflows/ax7203-gf16-sub.yml'] +# A newer push or PR commit replaces this file's pending run instead of +# queueing behind it; a dispatch is never cancelled (gHashTag/trinity-fpga#869). +concurrency: + group: ${{ github.workflow_ref }}-${{ github.event_name == 'workflow_dispatch' && github.run_id || github.event_name }} + cancel-in-progress: ${{ github.event_name == 'pull_request' }} + jobs: bitstream: runs-on: ubuntu-latest diff --git a/.github/workflows/ax7203-gf20-clean.yml b/.github/workflows/ax7203-gf20-clean.yml index 38ca7f9e74..df99d5479c 100644 --- a/.github/workflows/ax7203-gf20-clean.yml +++ b/.github/workflows/ax7203-gf20-clean.yml @@ -10,6 +10,12 @@ on: - 'specs/fpga/constraints/gf16_clean_ax7203.xdc' - '.github/workflows/ax7203-gf20-clean.yml' +# A newer push or PR commit replaces this file's pending run instead of +# queueing behind it; a dispatch is never cancelled (gHashTag/trinity-fpga#869). +concurrency: + group: ${{ github.workflow_ref }}-${{ github.event_name == 'workflow_dispatch' && github.run_id || github.event_name }} + cancel-in-progress: ${{ github.event_name == 'pull_request' }} + jobs: bitstream: runs-on: ubuntu-latest diff --git a/.github/workflows/ax7203-gf20-mul-clean.yml b/.github/workflows/ax7203-gf20-mul-clean.yml index 20dccea086..ac860c0db1 100644 --- a/.github/workflows/ax7203-gf20-mul-clean.yml +++ b/.github/workflows/ax7203-gf20-mul-clean.yml @@ -4,6 +4,12 @@ on: push: branches: [main] paths: ['fpga/vivado/gf20_mul_ax7203.v','fpga/openxc7-synth/gf_mul_param.v','.github/workflows/ax7203-gf20-mul-clean.yml'] +# A newer push or PR commit replaces this file's pending run instead of +# queueing behind it; a dispatch is never cancelled (gHashTag/trinity-fpga#869). +concurrency: + group: ${{ github.workflow_ref }}-${{ github.event_name == 'workflow_dispatch' && github.run_id || github.event_name }} + cancel-in-progress: ${{ github.event_name == 'pull_request' }} + jobs: bitstream: runs-on: ubuntu-latest diff --git a/.github/workflows/ax7203-gf20-sub.yml b/.github/workflows/ax7203-gf20-sub.yml index b1cceb5bc2..f46a1f7ce4 100644 --- a/.github/workflows/ax7203-gf20-sub.yml +++ b/.github/workflows/ax7203-gf20-sub.yml @@ -4,6 +4,12 @@ on: push: branches: [main] paths: ['fpga/vivado/gf20_sub_ax7203.v','fpga/openxc7-synth/gf_adder_param.v','.github/workflows/ax7203-gf20-sub.yml'] +# A newer push or PR commit replaces this file's pending run instead of +# queueing behind it; a dispatch is never cancelled (gHashTag/trinity-fpga#869). +concurrency: + group: ${{ github.workflow_ref }}-${{ github.event_name == 'workflow_dispatch' && github.run_id || github.event_name }} + cancel-in-progress: ${{ github.event_name == 'pull_request' }} + jobs: bitstream: runs-on: ubuntu-latest diff --git a/.github/workflows/ax7203-gf24-clean.yml b/.github/workflows/ax7203-gf24-clean.yml index eeb64cc8ba..bc974a050d 100644 --- a/.github/workflows/ax7203-gf24-clean.yml +++ b/.github/workflows/ax7203-gf24-clean.yml @@ -10,6 +10,12 @@ on: - 'specs/fpga/constraints/gf16_clean_ax7203.xdc' - '.github/workflows/ax7203-gf24-clean.yml' +# A newer push or PR commit replaces this file's pending run instead of +# queueing behind it; a dispatch is never cancelled (gHashTag/trinity-fpga#869). +concurrency: + group: ${{ github.workflow_ref }}-${{ github.event_name == 'workflow_dispatch' && github.run_id || github.event_name }} + cancel-in-progress: ${{ github.event_name == 'pull_request' }} + jobs: bitstream: runs-on: ubuntu-latest diff --git a/.github/workflows/ax7203-gf24-mul-clean.yml b/.github/workflows/ax7203-gf24-mul-clean.yml index 41cc5c7a99..398c849099 100644 --- a/.github/workflows/ax7203-gf24-mul-clean.yml +++ b/.github/workflows/ax7203-gf24-mul-clean.yml @@ -4,6 +4,12 @@ on: push: branches: [main] paths: ['fpga/vivado/gf24_mul_ax7203.v','fpga/openxc7-synth/gf_mul_param.v','.github/workflows/ax7203-gf24-mul-clean.yml'] +# A newer push or PR commit replaces this file's pending run instead of +# queueing behind it; a dispatch is never cancelled (gHashTag/trinity-fpga#869). +concurrency: + group: ${{ github.workflow_ref }}-${{ github.event_name == 'workflow_dispatch' && github.run_id || github.event_name }} + cancel-in-progress: ${{ github.event_name == 'pull_request' }} + jobs: bitstream: runs-on: ubuntu-latest diff --git a/.github/workflows/ax7203-gf24-sub.yml b/.github/workflows/ax7203-gf24-sub.yml index 9d921c74da..b7fdacf8e9 100644 --- a/.github/workflows/ax7203-gf24-sub.yml +++ b/.github/workflows/ax7203-gf24-sub.yml @@ -4,6 +4,12 @@ on: push: branches: [main] paths: ['fpga/vivado/gf24_sub_ax7203.v','fpga/openxc7-synth/gf_adder_param.v','.github/workflows/ax7203-gf24-sub.yml'] +# A newer push or PR commit replaces this file's pending run instead of +# queueing behind it; a dispatch is never cancelled (gHashTag/trinity-fpga#869). +concurrency: + group: ${{ github.workflow_ref }}-${{ github.event_name == 'workflow_dispatch' && github.run_id || github.event_name }} + cancel-in-progress: ${{ github.event_name == 'pull_request' }} + jobs: bitstream: runs-on: ubuntu-latest diff --git a/.github/workflows/ax7203-gf32-add.yml b/.github/workflows/ax7203-gf32-add.yml index 4983aecd03..1352422c2f 100644 --- a/.github/workflows/ax7203-gf32-add.yml +++ b/.github/workflows/ax7203-gf32-add.yml @@ -4,6 +4,12 @@ on: push: branches: [main] paths: ['fpga/vivado/gf32_add_ax7203.v','fpga/openxc7-synth/gf_adder_param.v','.github/workflows/ax7203-gf32-add.yml'] +# A newer push or PR commit replaces this file's pending run instead of +# queueing behind it; a dispatch is never cancelled (gHashTag/trinity-fpga#869). +concurrency: + group: ${{ github.workflow_ref }}-${{ github.event_name == 'workflow_dispatch' && github.run_id || github.event_name }} + cancel-in-progress: ${{ github.event_name == 'pull_request' }} + jobs: bitstream: runs-on: ubuntu-latest diff --git a/.github/workflows/ax7203-gf32-mul.yml b/.github/workflows/ax7203-gf32-mul.yml index cc07fa6f16..41411ed36a 100644 --- a/.github/workflows/ax7203-gf32-mul.yml +++ b/.github/workflows/ax7203-gf32-mul.yml @@ -4,6 +4,12 @@ on: push: branches: [main] paths: ['fpga/vivado/gf32_mul_ax7203.v','fpga/openxc7-synth/gf_mul_param.v','.github/workflows/ax7203-gf32-mul.yml'] +# A newer push or PR commit replaces this file's pending run instead of +# queueing behind it; a dispatch is never cancelled (gHashTag/trinity-fpga#869). +concurrency: + group: ${{ github.workflow_ref }}-${{ github.event_name == 'workflow_dispatch' && github.run_id || github.event_name }} + cancel-in-progress: ${{ github.event_name == 'pull_request' }} + jobs: bitstream: runs-on: ubuntu-latest diff --git a/.github/workflows/ax7203-gf32-sub.yml b/.github/workflows/ax7203-gf32-sub.yml index 717d00c540..2c0d0ad894 100644 --- a/.github/workflows/ax7203-gf32-sub.yml +++ b/.github/workflows/ax7203-gf32-sub.yml @@ -4,6 +4,12 @@ on: push: branches: [main] paths: ['fpga/vivado/gf32_sub_ax7203.v','fpga/openxc7-synth/gf_adder_param.v','conformance/gf_ref.py','.github/workflows/ax7203-gf32-sub.yml'] +# A newer push or PR commit replaces this file's pending run instead of +# queueing behind it; a dispatch is never cancelled (gHashTag/trinity-fpga#869). +concurrency: + group: ${{ github.workflow_ref }}-${{ github.event_name == 'workflow_dispatch' && github.run_id || github.event_name }} + cancel-in-progress: ${{ github.event_name == 'pull_request' }} + jobs: bitstream: runs-on: ubuntu-latest diff --git a/.github/workflows/ax7203-gf4-clean.yml b/.github/workflows/ax7203-gf4-clean.yml index d7bcd43558..c12296d96d 100644 --- a/.github/workflows/ax7203-gf4-clean.yml +++ b/.github/workflows/ax7203-gf4-clean.yml @@ -4,6 +4,12 @@ on: push: branches: [main] paths: ['fpga/vivado/gf4_clean_ax7203.v','fpga/openxc7-synth/gf_adder_param.v','.github/workflows/ax7203-gf4-clean.yml'] +# A newer push or PR commit replaces this file's pending run instead of +# queueing behind it; a dispatch is never cancelled (gHashTag/trinity-fpga#869). +concurrency: + group: ${{ github.workflow_ref }}-${{ github.event_name == 'workflow_dispatch' && github.run_id || github.event_name }} + cancel-in-progress: ${{ github.event_name == 'pull_request' }} + jobs: bitstream: runs-on: ubuntu-latest diff --git a/.github/workflows/ax7203-gf4-mul-clean.yml b/.github/workflows/ax7203-gf4-mul-clean.yml index b10b581ebd..bab5063edd 100644 --- a/.github/workflows/ax7203-gf4-mul-clean.yml +++ b/.github/workflows/ax7203-gf4-mul-clean.yml @@ -4,6 +4,12 @@ on: push: branches: [main] paths: ['fpga/vivado/gf4_mul_ax7203.v','fpga/openxc7-synth/gf_mul_param.v','.github/workflows/ax7203-gf4-mul-clean.yml'] +# A newer push or PR commit replaces this file's pending run instead of +# queueing behind it; a dispatch is never cancelled (gHashTag/trinity-fpga#869). +concurrency: + group: ${{ github.workflow_ref }}-${{ github.event_name == 'workflow_dispatch' && github.run_id || github.event_name }} + cancel-in-progress: ${{ github.event_name == 'pull_request' }} + jobs: bitstream: runs-on: ubuntu-latest diff --git a/.github/workflows/ax7203-gf4-sub.yml b/.github/workflows/ax7203-gf4-sub.yml index d582851a47..3ddb22c4d5 100644 --- a/.github/workflows/ax7203-gf4-sub.yml +++ b/.github/workflows/ax7203-gf4-sub.yml @@ -4,6 +4,12 @@ on: push: branches: [main] paths: ['fpga/vivado/gf4_sub_ax7203.v','fpga/openxc7-synth/gf_adder_param.v','.github/workflows/ax7203-gf4-sub.yml'] +# A newer push or PR commit replaces this file's pending run instead of +# queueing behind it; a dispatch is never cancelled (gHashTag/trinity-fpga#869). +concurrency: + group: ${{ github.workflow_ref }}-${{ github.event_name == 'workflow_dispatch' && github.run_id || github.event_name }} + cancel-in-progress: ${{ github.event_name == 'pull_request' }} + jobs: bitstream: runs-on: ubuntu-latest diff --git a/.github/workflows/ax7203-gf6-clean.yml b/.github/workflows/ax7203-gf6-clean.yml index ff6b791bbf..08fd108cdd 100644 --- a/.github/workflows/ax7203-gf6-clean.yml +++ b/.github/workflows/ax7203-gf6-clean.yml @@ -4,6 +4,12 @@ on: push: branches: [main] paths: ['fpga/vivado/gf6_clean_ax7203.v','fpga/openxc7-synth/gf_adder_param.v','.github/workflows/ax7203-gf6-clean.yml'] +# A newer push or PR commit replaces this file's pending run instead of +# queueing behind it; a dispatch is never cancelled (gHashTag/trinity-fpga#869). +concurrency: + group: ${{ github.workflow_ref }}-${{ github.event_name == 'workflow_dispatch' && github.run_id || github.event_name }} + cancel-in-progress: ${{ github.event_name == 'pull_request' }} + jobs: bitstream: runs-on: ubuntu-latest diff --git a/.github/workflows/ax7203-gf6-mul-clean.yml b/.github/workflows/ax7203-gf6-mul-clean.yml index a2f6524347..53c256929a 100644 --- a/.github/workflows/ax7203-gf6-mul-clean.yml +++ b/.github/workflows/ax7203-gf6-mul-clean.yml @@ -4,6 +4,12 @@ on: push: branches: [main] paths: ['fpga/vivado/gf6_mul_ax7203.v','fpga/openxc7-synth/gf_mul_param.v','.github/workflows/ax7203-gf6-mul-clean.yml'] +# A newer push or PR commit replaces this file's pending run instead of +# queueing behind it; a dispatch is never cancelled (gHashTag/trinity-fpga#869). +concurrency: + group: ${{ github.workflow_ref }}-${{ github.event_name == 'workflow_dispatch' && github.run_id || github.event_name }} + cancel-in-progress: ${{ github.event_name == 'pull_request' }} + jobs: bitstream: runs-on: ubuntu-latest diff --git a/.github/workflows/ax7203-gf6-sub.yml b/.github/workflows/ax7203-gf6-sub.yml index 668678cf91..1599616e23 100644 --- a/.github/workflows/ax7203-gf6-sub.yml +++ b/.github/workflows/ax7203-gf6-sub.yml @@ -4,6 +4,12 @@ on: push: branches: [main] paths: ['fpga/vivado/gf6_sub_ax7203.v','fpga/openxc7-synth/gf_adder_param.v','.github/workflows/ax7203-gf6-sub.yml'] +# A newer push or PR commit replaces this file's pending run instead of +# queueing behind it; a dispatch is never cancelled (gHashTag/trinity-fpga#869). +concurrency: + group: ${{ github.workflow_ref }}-${{ github.event_name == 'workflow_dispatch' && github.run_id || github.event_name }} + cancel-in-progress: ${{ github.event_name == 'pull_request' }} + jobs: bitstream: runs-on: ubuntu-latest diff --git a/.github/workflows/ax7203-gf8-clean.yml b/.github/workflows/ax7203-gf8-clean.yml index 35ddd7fceb..8a4376668f 100644 --- a/.github/workflows/ax7203-gf8-clean.yml +++ b/.github/workflows/ax7203-gf8-clean.yml @@ -4,6 +4,12 @@ on: push: branches: [main] paths: ['fpga/vivado/gf8_clean_ax7203.v','fpga/openxc7-synth/gf_adder_param.v','.github/workflows/ax7203-gf8-clean.yml'] +# A newer push or PR commit replaces this file's pending run instead of +# queueing behind it; a dispatch is never cancelled (gHashTag/trinity-fpga#869). +concurrency: + group: ${{ github.workflow_ref }}-${{ github.event_name == 'workflow_dispatch' && github.run_id || github.event_name }} + cancel-in-progress: ${{ github.event_name == 'pull_request' }} + jobs: bitstream: runs-on: ubuntu-latest diff --git a/.github/workflows/ax7203-gf8-mul-clean.yml b/.github/workflows/ax7203-gf8-mul-clean.yml index 5c97485cee..571deaca01 100644 --- a/.github/workflows/ax7203-gf8-mul-clean.yml +++ b/.github/workflows/ax7203-gf8-mul-clean.yml @@ -4,6 +4,12 @@ on: push: branches: [main] paths: ['fpga/vivado/gf8_mul_ax7203.v','fpga/openxc7-synth/gf_mul_param.v','.github/workflows/ax7203-gf8-mul-clean.yml'] +# A newer push or PR commit replaces this file's pending run instead of +# queueing behind it; a dispatch is never cancelled (gHashTag/trinity-fpga#869). +concurrency: + group: ${{ github.workflow_ref }}-${{ github.event_name == 'workflow_dispatch' && github.run_id || github.event_name }} + cancel-in-progress: ${{ github.event_name == 'pull_request' }} + jobs: bitstream: runs-on: ubuntu-latest diff --git a/.github/workflows/ax7203-gf8-sub.yml b/.github/workflows/ax7203-gf8-sub.yml index 15ead84de8..f37d6cfda2 100644 --- a/.github/workflows/ax7203-gf8-sub.yml +++ b/.github/workflows/ax7203-gf8-sub.yml @@ -4,6 +4,12 @@ on: push: branches: [main] paths: ['fpga/vivado/gf8_sub_ax7203.v','fpga/openxc7-synth/gf_adder_param.v','.github/workflows/ax7203-gf8-sub.yml'] +# A newer push or PR commit replaces this file's pending run instead of +# queueing behind it; a dispatch is never cancelled (gHashTag/trinity-fpga#869). +concurrency: + group: ${{ github.workflow_ref }}-${{ github.event_name == 'workflow_dispatch' && github.run_id || github.event_name }} + cancel-in-progress: ${{ github.event_name == 'pull_request' }} + jobs: bitstream: runs-on: ubuntu-latest diff --git a/.github/workflows/ax7203-gfternary-mul.yml b/.github/workflows/ax7203-gfternary-mul.yml index 4a8894b62a..235ffb81bd 100644 --- a/.github/workflows/ax7203-gfternary-mul.yml +++ b/.github/workflows/ax7203-gfternary-mul.yml @@ -8,6 +8,12 @@ on: - 'fpga/openxc7-synth/corona_compute_gfternary_mul_ax7203.v' - 'conformance/gfternary_compute_conformance_ax7203.py' - '.github/workflows/ax7203-gfternary-mul.yml' +# A newer push or PR commit replaces this file's pending run instead of +# queueing behind it; a dispatch is never cancelled (gHashTag/trinity-fpga#869). +concurrency: + group: ${{ github.workflow_ref }}-${{ github.event_name == 'workflow_dispatch' && github.run_id || github.event_name }} + cancel-in-progress: ${{ github.event_name == 'pull_request' }} + jobs: golden: runs-on: ubuntu-latest diff --git a/.github/workflows/ax7203-identity-echo.yml b/.github/workflows/ax7203-identity-echo.yml index fbeb3aaef8..02d133dfa0 100644 --- a/.github/workflows/ax7203-identity-echo.yml +++ b/.github/workflows/ax7203-identity-echo.yml @@ -4,6 +4,12 @@ on: push: branches: [main] paths: ['fpga/vivado/identity_echo_ax7203.v','.github/workflows/ax7203-identity-echo.yml'] +# A newer push or PR commit replaces this file's pending run instead of +# queueing behind it; a dispatch is never cancelled (gHashTag/trinity-fpga#869). +concurrency: + group: ${{ github.workflow_ref }}-${{ github.event_name == 'workflow_dispatch' && github.run_id || github.event_name }} + cancel-in-progress: ${{ github.event_name == 'pull_request' }} + jobs: bitstream: runs-on: ubuntu-latest diff --git a/.github/workflows/ax7203-led-onehot.yml b/.github/workflows/ax7203-led-onehot.yml index 2877edbc10..e2fcc3b5c0 100644 --- a/.github/workflows/ax7203-led-onehot.yml +++ b/.github/workflows/ax7203-led-onehot.yml @@ -13,6 +13,12 @@ on: - 'specs/fpga/constraints/led_onehot_ax7203.xdc' - '.github/workflows/ax7203-led-onehot.yml' +# A newer push or PR commit replaces this file's pending run instead of +# queueing behind it; a dispatch is never cancelled (gHashTag/trinity-fpga#869). +concurrency: + group: ${{ github.workflow_ref }}-${{ github.event_name == 'workflow_dispatch' && github.run_id || github.event_name }} + cancel-in-progress: ${{ github.event_name == 'pull_request' }} + jobs: bitstream: name: Generate AX7203 LED Onehot Bitstream diff --git a/.github/workflows/ax7203-rx-echo-hb.yml b/.github/workflows/ax7203-rx-echo-hb.yml index 173f6a093e..ce21dfb502 100644 --- a/.github/workflows/ax7203-rx-echo-hb.yml +++ b/.github/workflows/ax7203-rx-echo-hb.yml @@ -11,6 +11,12 @@ on: - 'specs/fpga/constraints/rx_echo_hb_ax7203.xdc' - '.github/workflows/ax7203-rx-echo-hb.yml' +# A newer push or PR commit replaces this file's pending run instead of +# queueing behind it; a dispatch is never cancelled (gHashTag/trinity-fpga#869). +concurrency: + group: ${{ github.workflow_ref }}-${{ github.event_name == 'workflow_dispatch' && github.run_id || github.event_name }} + cancel-in-progress: ${{ github.event_name == 'pull_request' }} + jobs: bitstream: name: Generate AX7203 RX-Echo Heartbeat Bitstream diff --git a/.github/workflows/ax7203-trinet-baud.yml b/.github/workflows/ax7203-trinet-baud.yml index d93548b09d..c81e50740a 100644 --- a/.github/workflows/ax7203-trinet-baud.yml +++ b/.github/workflows/ax7203-trinet-baud.yml @@ -24,6 +24,12 @@ on: # Building all three so the fastest working one can be found by measurement # rather than by argument. +# A newer push or PR commit replaces this file's pending run instead of +# queueing behind it; a dispatch is never cancelled (gHashTag/trinity-fpga#869). +concurrency: + group: ${{ github.workflow_ref }}-${{ github.event_name == 'workflow_dispatch' && github.run_id || github.event_name }} + cancel-in-progress: ${{ github.event_name == 'pull_request' }} + jobs: baud: runs-on: ubuntu-latest diff --git a/.github/workflows/ax7203-trinet-dna-probe.yml b/.github/workflows/ax7203-trinet-dna-probe.yml index e932ad2441..c2ba31c025 100644 --- a/.github/workflows/ax7203-trinet-dna-probe.yml +++ b/.github/workflows/ax7203-trinet-dna-probe.yml @@ -7,6 +7,12 @@ on: - 'fpga/vivado/trinet_dna_probe_ax7203.v' - 'formal/trinet_dna_probe_tb.v' - '.github/workflows/ax7203-trinet-dna-probe.yml' +# A newer push or PR commit replaces this file's pending run instead of +# queueing behind it; a dispatch is never cancelled (gHashTag/trinity-fpga#869). +concurrency: + group: ${{ github.workflow_ref }}-${{ github.event_name == 'workflow_dispatch' && github.run_id || github.event_name }} + cancel-in-progress: ${{ github.event_name == 'pull_request' }} + jobs: golden: runs-on: ubuntu-latest diff --git a/.github/workflows/ax7203-trinet-fleet.yml b/.github/workflows/ax7203-trinet-fleet.yml index 5d6d15dd6f..63e4255aaf 100644 --- a/.github/workflows/ax7203-trinet-fleet.yml +++ b/.github/workflows/ax7203-trinet-fleet.yml @@ -61,6 +61,12 @@ on: # Building the fleet at the old 434 would ship a network that is 4x slower than # the hardware has already been shown to manage. +# A newer push or PR commit replaces this file's pending run instead of +# queueing behind it; a dispatch is never cancelled (gHashTag/trinity-fpga#869). +concurrency: + group: ${{ github.workflow_ref }}-${{ github.event_name == 'workflow_dispatch' && github.run_id || github.event_name }} + cancel-in-progress: ${{ github.event_name == 'pull_request' }} + jobs: fleet: runs-on: ubuntu-latest diff --git a/.github/workflows/ax7203-trinet-mac32.yml b/.github/workflows/ax7203-trinet-mac32.yml index 1570ab05f5..ac50b8c8f0 100644 --- a/.github/workflows/ax7203-trinet-mac32.yml +++ b/.github/workflows/ax7203-trinet-mac32.yml @@ -8,6 +8,12 @@ on: - 'formal/trinet_mac32_tb.v' - 'conformance/trinet_mac32_conformance_ax7203.py' - '.github/workflows/ax7203-trinet-mac32.yml' +# A newer push or PR commit replaces this file's pending run instead of +# queueing behind it; a dispatch is never cancelled (gHashTag/trinity-fpga#869). +concurrency: + group: ${{ github.workflow_ref }}-${{ github.event_name == 'workflow_dispatch' && github.run_id || github.event_name }} + cancel-in-progress: ${{ github.event_name == 'pull_request' }} + jobs: golden: runs-on: ubuntu-latest diff --git a/.github/workflows/ax7203-trinet-node-v2.yml b/.github/workflows/ax7203-trinet-node-v2.yml index e7cdf75fb1..4eb01a370f 100644 --- a/.github/workflows/ax7203-trinet-node-v2.yml +++ b/.github/workflows/ax7203-trinet-node-v2.yml @@ -11,6 +11,12 @@ on: - '.github/workflows/ax7203-trinet-node-v2.yml' # the scripts this job runs: fixing a check must re-run it - 'conformance/trinet_mac32_conformance_ax7203.py' +# A newer push or PR commit replaces this file's pending run instead of +# queueing behind it; a dispatch is never cancelled (gHashTag/trinity-fpga#869). +concurrency: + group: ${{ github.workflow_ref }}-${{ github.event_name == 'workflow_dispatch' && github.run_id || github.event_name }} + cancel-in-progress: ${{ github.event_name == 'pull_request' }} + jobs: golden: runs-on: ubuntu-latest diff --git a/.github/workflows/ax7203-uart-loopback.yml b/.github/workflows/ax7203-uart-loopback.yml index 13c21a13f7..bbd77b2938 100644 --- a/.github/workflows/ax7203-uart-loopback.yml +++ b/.github/workflows/ax7203-uart-loopback.yml @@ -13,6 +13,12 @@ on: - 'specs/fpga/constraints/gf16_ax7203.xdc' - '.github/workflows/ax7203-uart-loopback.yml' +# A newer push or PR commit replaces this file's pending run instead of +# queueing behind it; a dispatch is never cancelled (gHashTag/trinity-fpga#869). +concurrency: + group: ${{ github.workflow_ref }}-${{ github.event_name == 'workflow_dispatch' && github.run_id || github.event_name }} + cancel-in-progress: ${{ github.event_name == 'pull_request' }} + jobs: bitstream: name: Generate AX7203 UART Loopback Bitstream diff --git a/.github/workflows/ax7203-uart-tx-probe.yml b/.github/workflows/ax7203-uart-tx-probe.yml index 28bd817709..e47e97dc9e 100644 --- a/.github/workflows/ax7203-uart-tx-probe.yml +++ b/.github/workflows/ax7203-uart-tx-probe.yml @@ -12,6 +12,12 @@ on: - 'specs/fpga/constraints/uart_tx_probe_ax7203.xdc' - '.github/workflows/ax7203-uart-tx-probe.yml' +# A newer push or PR commit replaces this file's pending run instead of +# queueing behind it; a dispatch is never cancelled (gHashTag/trinity-fpga#869). +concurrency: + group: ${{ github.workflow_ref }}-${{ github.event_name == 'workflow_dispatch' && github.run_id || github.event_name }} + cancel-in-progress: ${{ github.event_name == 'pull_request' }} + jobs: bitstream: name: Generate AX7203 UART TX Probe Bitstream diff --git a/.github/workflows/brain-ci.yml b/.github/workflows/brain-ci.yml index f0aeca87e8..717b05c079 100644 --- a/.github/workflows/brain-ci.yml +++ b/.github/workflows/brain-ci.yml @@ -22,6 +22,12 @@ env: BRAIN_HEALTH_THRESHOLD: "80" # Minimum health score for merge STRESS_TEST_THRESHOLD: "270" # Minimum stress test score (270/300 = 90%) +# A newer push or PR commit replaces this file's pending run instead of +# queueing behind it; a dispatch is never cancelled (gHashTag/trinity-fpga#869). +concurrency: + group: ${{ github.workflow_ref }}-${{ github.event_name == 'workflow_dispatch' && github.run_id || github.event_name }} + cancel-in-progress: ${{ github.event_name == 'pull_request' }} + jobs: # ========================================================================= # Phase -1: Build entry-point detection diff --git a/.github/workflows/build-paper.yml b/.github/workflows/build-paper.yml index 7f9d5af115..a4467bbf61 100644 --- a/.github/workflows/build-paper.yml +++ b/.github/workflows/build-paper.yml @@ -7,6 +7,12 @@ on: - 'research/arxiv_submission/**' - '.github/workflows/build-paper.yml' +# A newer push or PR commit replaces this file's pending run instead of +# queueing behind it; a dispatch is never cancelled (gHashTag/trinity-fpga#869). +concurrency: + group: ${{ github.workflow_ref }}-${{ github.event_name == 'workflow_dispatch' && github.run_id || github.event_name }} + cancel-in-progress: ${{ github.event_name == 'pull_request' }} + jobs: build: runs-on: ubuntu-latest diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 444c56db22..c5ca722415 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -18,6 +18,12 @@ on: # Реальный гейт = per-file `zig test` стабильных модулей + `zig fmt --check`. # Когда миграция VIBEE завершится — убрать `continue-on-error` (отдельный PR). +# A newer push or PR commit replaces this file's pending run instead of +# queueing behind it; a dispatch is never cancelled (gHashTag/trinity-fpga#869). +concurrency: + group: ${{ github.workflow_ref }}-${{ github.event_name == 'workflow_dispatch' && github.run_id || github.event_name }} + cancel-in-progress: ${{ github.event_name == 'pull_request' }} + jobs: test: name: Build & Test diff --git a/.github/workflows/claim-consistency.yml b/.github/workflows/claim-consistency.yml index bef377fcd6..fb1ab9be24 100644 --- a/.github/workflows/claim-consistency.yml +++ b/.github/workflows/claim-consistency.yml @@ -26,6 +26,12 @@ on: - 'research/**/*.md' - 'research/check_claim_consistency.py' +# A newer push or PR commit replaces this file's pending run instead of +# queueing behind it; a dispatch is never cancelled (gHashTag/trinity-fpga#869). +concurrency: + group: ${{ github.workflow_ref }}-${{ github.event_name == 'workflow_dispatch' && github.run_id || github.event_name }} + cancel-in-progress: ${{ github.event_name == 'pull_request' }} + jobs: claim-consistency: runs-on: ubuntu-latest diff --git a/.github/workflows/codegen.yml b/.github/workflows/codegen.yml index dec7d0b3b8..3d314792ed 100644 --- a/.github/workflows/codegen.yml +++ b/.github/workflows/codegen.yml @@ -31,6 +31,12 @@ on: env: ZIG_VERSION: '0.16.0' +# A newer push or PR commit replaces this file's pending run instead of +# queueing behind it; a dispatch is never cancelled (gHashTag/trinity-fpga#869). +concurrency: + group: ${{ github.workflow_ref }}-${{ github.event_name == 'workflow_dispatch' && github.run_id || github.event_name }} + cancel-in-progress: ${{ github.event_name == 'pull_request' }} + jobs: validate: name: Validate VIBEE Codegen diff --git a/.github/workflows/conformance-frame-alignment.yml b/.github/workflows/conformance-frame-alignment.yml index dfedc82ea6..c404733e8b 100644 --- a/.github/workflows/conformance-frame-alignment.yml +++ b/.github/workflows/conformance-frame-alignment.yml @@ -21,6 +21,12 @@ on: # day rather than at the next flash session. - cron: '17 4 * * *' +# A newer push or PR commit replaces this file's pending run instead of +# queueing behind it; a dispatch is never cancelled (gHashTag/trinity-fpga#869). +concurrency: + group: ${{ github.workflow_ref }}-${{ github.event_name == 'workflow_dispatch' && github.run_id || github.event_name }} + cancel-in-progress: ${{ github.event_name == 'pull_request' }} + jobs: alignment: runs-on: ubuntu-latest diff --git a/.github/workflows/conformance-golden-selftests.yml b/.github/workflows/conformance-golden-selftests.yml index e8d01e883c..2b488f458f 100644 --- a/.github/workflows/conformance-golden-selftests.yml +++ b/.github/workflows/conformance-golden-selftests.yml @@ -14,6 +14,12 @@ on: - '.github/workflows/conformance-golden-selftests.yml' workflow_dispatch: +# A newer push or PR commit replaces this file's pending run instead of +# queueing behind it; a dispatch is never cancelled (gHashTag/trinity-fpga#869). +concurrency: + group: ${{ github.workflow_ref }}-${{ github.event_name == 'workflow_dispatch' && github.run_id || github.event_name }} + cancel-in-progress: ${{ github.event_name == 'pull_request' }} + jobs: selftest: runs-on: ubuntu-latest diff --git a/.github/workflows/conformance-selftest.yml b/.github/workflows/conformance-selftest.yml index 4d0eac4646..d6c996fbf3 100644 --- a/.github/workflows/conformance-selftest.yml +++ b/.github/workflows/conformance-selftest.yml @@ -21,6 +21,12 @@ on: paths: - 'conformance/**' +# A newer push or PR commit replaces this file's pending run instead of +# queueing behind it; a dispatch is never cancelled (gHashTag/trinity-fpga#869). +concurrency: + group: ${{ github.workflow_ref }}-${{ github.event_name == 'workflow_dispatch' && github.run_id || github.event_name }} + cancel-in-progress: ${{ github.event_name == 'pull_request' }} + jobs: selftest: runs-on: ubuntu-latest diff --git a/.github/workflows/counted-claim-gate.yml b/.github/workflows/counted-claim-gate.yml index d8a2bef841..87b47572e8 100644 --- a/.github/workflows/counted-claim-gate.yml +++ b/.github/workflows/counted-claim-gate.yml @@ -23,6 +23,12 @@ on: - '.github/workflows/counted-claim-gate.yml' workflow_dispatch: +# A newer push or PR commit replaces this file's pending run instead of +# queueing behind it; a dispatch is never cancelled (gHashTag/trinity-fpga#869). +concurrency: + group: ${{ github.workflow_ref }}-${{ github.event_name == 'workflow_dispatch' && github.run_id || github.event_name }} + cancel-in-progress: ${{ github.event_name == 'pull_request' }} + jobs: counted-claims: runs-on: ubuntu-latest diff --git a/.github/workflows/decode-verify.yml b/.github/workflows/decode-verify.yml index 2b5f604815..56e4e78839 100644 --- a/.github/workflows/decode-verify.yml +++ b/.github/workflows/decode-verify.yml @@ -16,6 +16,12 @@ on: - 'formal/binary16_decode_tb.v' - 'formal/fp8_e5m2_decode_tb.v' - '.github/workflows/decode-verify.yml' +# A newer push or PR commit replaces this file's pending run instead of +# queueing behind it; a dispatch is never cancelled (gHashTag/trinity-fpga#869). +concurrency: + group: ${{ github.workflow_ref }}-${{ github.event_name == 'workflow_dispatch' && github.run_id || github.event_name }} + cancel-in-progress: ${{ github.event_name == 'pull_request' }} + jobs: verify: runs-on: ubuntu-latest diff --git a/.github/workflows/decoder-conformance.yml b/.github/workflows/decoder-conformance.yml index db595ef3a3..2ff68f71c4 100644 --- a/.github/workflows/decoder-conformance.yml +++ b/.github/workflows/decoder-conformance.yml @@ -8,6 +8,12 @@ name: Decoder conformance on: [pull_request, push] +# A newer push or PR commit replaces this file's pending run instead of +# queueing behind it; a dispatch is never cancelled (gHashTag/trinity-fpga#869). +concurrency: + group: ${{ github.workflow_ref }}-${{ github.event_name == 'workflow_dispatch' && github.run_id || github.event_name }} + cancel-in-progress: ${{ github.event_name == 'pull_request' }} + jobs: decoder-conformance: runs-on: ubuntu-latest diff --git a/.github/workflows/dev-enforcement.yml b/.github/workflows/dev-enforcement.yml index 5fbe475243..947f6ea020 100644 --- a/.github/workflows/dev-enforcement.yml +++ b/.github/workflows/dev-enforcement.yml @@ -35,6 +35,12 @@ permissions: contents: read pull-requests: read +# A newer push or PR commit replaces this file's pending run instead of +# queueing behind it; a dispatch is never cancelled (gHashTag/trinity-fpga#869). +concurrency: + group: ${{ github.workflow_ref }}-${{ github.event_name == 'workflow_dispatch' && github.run_id || github.event_name }} + cancel-in-progress: ${{ github.event_name == 'pull_request' }} + jobs: commit-format: name: CI Validation diff --git a/.github/workflows/doc-refs.yml b/.github/workflows/doc-refs.yml index 1a0d217ab4..7985116faa 100644 --- a/.github/workflows/doc-refs.yml +++ b/.github/workflows/doc-refs.yml @@ -31,6 +31,12 @@ on: push: branches: [main, master] +# A newer push or PR commit replaces this file's pending run instead of +# queueing behind it; a dispatch is never cancelled (gHashTag/trinity-fpga#869). +concurrency: + group: ${{ github.workflow_ref }}-${{ github.event_name == 'workflow_dispatch' && github.run_id || github.event_name }} + cancel-in-progress: ${{ github.event_name == 'pull_request' }} + jobs: doc-refs: runs-on: ubuntu-latest diff --git a/.github/workflows/exhaustive-claim-gate.yml b/.github/workflows/exhaustive-claim-gate.yml index 49b9593ff4..4d542e08e4 100644 --- a/.github/workflows/exhaustive-claim-gate.yml +++ b/.github/workflows/exhaustive-claim-gate.yml @@ -26,6 +26,12 @@ on: - '.github/workflows/exhaustive-claim-gate.yml' workflow_dispatch: +# A newer push or PR commit replaces this file's pending run instead of +# queueing behind it; a dispatch is never cancelled (gHashTag/trinity-fpga#869). +concurrency: + group: ${{ github.workflow_ref }}-${{ github.event_name == 'workflow_dispatch' && github.run_id || github.event_name }} + cancel-in-progress: ${{ github.event_name == 'pull_request' }} + jobs: exhaustive-claims: runs-on: ubuntu-latest diff --git a/.github/workflows/figure-names.yml b/.github/workflows/figure-names.yml index a4685ca18c..3d46ead305 100644 --- a/.github/workflows/figure-names.yml +++ b/.github/workflows/figure-names.yml @@ -8,6 +8,12 @@ name: Figure names on: [pull_request, push] +# A newer push or PR commit replaces this file's pending run instead of +# queueing behind it; a dispatch is never cancelled (gHashTag/trinity-fpga#869). +concurrency: + group: ${{ github.workflow_ref }}-${{ github.event_name == 'workflow_dispatch' && github.run_id || github.event_name }} + cancel-in-progress: ${{ github.event_name == 'pull_request' }} + jobs: figure-names: runs-on: ubuntu-latest diff --git a/.github/workflows/format-table-gate.yml b/.github/workflows/format-table-gate.yml index 0490a04d34..6438565c6a 100644 --- a/.github/workflows/format-table-gate.yml +++ b/.github/workflows/format-table-gate.yml @@ -26,6 +26,12 @@ on: - '.github/workflows/format-table-gate.yml' workflow_dispatch: +# A newer push or PR commit replaces this file's pending run instead of +# queueing behind it; a dispatch is never cancelled (gHashTag/trinity-fpga#869). +concurrency: + group: ${{ github.workflow_ref }}-${{ github.event_name == 'workflow_dispatch' && github.run_id || github.event_name }} + cancel-in-progress: ${{ github.event_name == 'pull_request' }} + jobs: format-tables: runs-on: ubuntu-latest diff --git a/.github/workflows/fpga-bitstream.yml b/.github/workflows/fpga-bitstream.yml index 9748d68ce6..df8a948f43 100644 --- a/.github/workflows/fpga-bitstream.yml +++ b/.github/workflows/fpga-bitstream.yml @@ -61,6 +61,12 @@ on: env: CONSCIOUSNESS_DEFAULT: 'aware' +# A newer push or PR commit replaces this file's pending run instead of +# queueing behind it; a dispatch is never cancelled (gHashTag/trinity-fpga#869). +concurrency: + group: ${{ github.workflow_ref }}-${{ github.event_name == 'workflow_dispatch' && github.run_id || github.event_name }} + cancel-in-progress: ${{ github.event_name == 'pull_request' }} + jobs: # ═══════════════════════════════════════════════════════════════════════════════ # EXTRACT CHANGED SPECS diff --git a/.github/workflows/fpga-ci.yml b/.github/workflows/fpga-ci.yml index b83fa49dec..587a7a1044 100644 --- a/.github/workflows/fpga-ci.yml +++ b/.github/workflows/fpga-ci.yml @@ -33,6 +33,12 @@ on: - '.github/workflows/fpga-ci.yml' workflow_dispatch: +# A newer push or PR commit replaces this file's pending run instead of +# queueing behind it; a dispatch is never cancelled (gHashTag/trinity-fpga#869). +concurrency: + group: ${{ github.workflow_ref }}-${{ github.event_name == 'workflow_dispatch' && github.run_id || github.event_name }} + cancel-in-progress: ${{ github.event_name == 'pull_request' }} + jobs: # ═══════════════════════════════════════════════════════════════════════════════ # SACRED CONSTANTS VALIDATION diff --git a/.github/workflows/fpga-docker.yml b/.github/workflows/fpga-docker.yml index 1fa4777deb..fa2355b262 100644 --- a/.github/workflows/fpga-docker.yml +++ b/.github/workflows/fpga-docker.yml @@ -43,6 +43,12 @@ env: REGISTRY: ghcr.io IMAGE_NAME: trinity-openxc7 +# A newer push or PR commit replaces this file's pending run instead of +# queueing behind it; a dispatch is never cancelled (gHashTag/trinity-fpga#869). +concurrency: + group: ${{ github.workflow_ref }}-${{ github.event_name == 'workflow_dispatch' && github.run_id || github.event_name }} + cancel-in-progress: ${{ github.event_name == 'pull_request' }} + jobs: # ═══════════════════════════════════════════════════════════════════════════════ # BUILD DOCKER IMAGE diff --git a/.github/workflows/fpga-formal-comb.yml b/.github/workflows/fpga-formal-comb.yml index 3f4c7459ba..8a67e7ab33 100644 --- a/.github/workflows/fpga-formal-comb.yml +++ b/.github/workflows/fpga-formal-comb.yml @@ -43,6 +43,12 @@ on: - '.github/workflows/fpga-formal-comb.yml' workflow_dispatch: +# A newer push or PR commit replaces this file's pending run instead of +# queueing behind it; a dispatch is never cancelled (gHashTag/trinity-fpga#869). +concurrency: + group: ${{ github.workflow_ref }}-${{ github.event_name == 'workflow_dispatch' && github.run_id || github.event_name }} + cancel-in-progress: ${{ github.event_name == 'pull_request' }} + jobs: comb-miter: name: "🔬 yosys sat — ${{ matrix.op }} ${{ matrix.task }}" diff --git a/.github/workflows/fpga-formal.yml b/.github/workflows/fpga-formal.yml index 574b04282d..63d3c2dd9b 100644 --- a/.github/workflows/fpga-formal.yml +++ b/.github/workflows/fpga-formal.yml @@ -52,6 +52,12 @@ on: - '.github/workflows/fpga-formal.yml' workflow_dispatch: +# A newer push or PR commit replaces this file's pending run instead of +# queueing behind it; a dispatch is never cancelled (gHashTag/trinity-fpga#869). +concurrency: + group: ${{ github.workflow_ref }}-${{ github.event_name == 'workflow_dispatch' && github.run_id || github.event_name }} + cancel-in-progress: ${{ github.event_name == 'pull_request' }} + jobs: formal: name: "🔬 sby z3 — ${{ matrix.op }} ${{ matrix.task }}" diff --git a/.github/workflows/fpga-hslm-bitstream.yml b/.github/workflows/fpga-hslm-bitstream.yml index 338366c9d4..fd615d221d 100644 --- a/.github/workflows/fpga-hslm-bitstream.yml +++ b/.github/workflows/fpga-hslm-bitstream.yml @@ -20,6 +20,12 @@ on: - 'fpga/openxc7-synth/embedding_lookup.v' - 'fpga/openxc7-synth/argmax_unit.v' +# A newer push or PR commit replaces this file's pending run instead of +# queueing behind it; a dispatch is never cancelled (gHashTag/trinity-fpga#869). +concurrency: + group: ${{ github.workflow_ref }}-${{ github.event_name == 'workflow_dispatch' && github.run_id || github.event_name }} + cancel-in-progress: ${{ github.event_name == 'pull_request' }} + jobs: bitstream: name: Generate HSLM Bitstream diff --git a/.github/workflows/fpga-regression.yml b/.github/workflows/fpga-regression.yml index 7157f2ae96..c34bbf7708 100644 --- a/.github/workflows/fpga-regression.yml +++ b/.github/workflows/fpga-regression.yml @@ -30,6 +30,12 @@ on: env: CONSCIOUSNESS_THRESHOLD: "61.8" # φ⁻¹ immortality threshold +# A newer push or PR commit replaces this file's pending run instead of +# queueing behind it; a dispatch is never cancelled (gHashTag/trinity-fpga#869). +concurrency: + group: ${{ github.workflow_ref }}-${{ github.event_name == 'workflow_dispatch' && github.run_id || github.event_name }} + cancel-in-progress: ${{ github.event_name == 'pull_request' }} + jobs: # ═══════════════════════════════════════════════════════════════════════════ # SACRED CONSTANTS VALIDATION diff --git a/.github/workflows/gate-ratchet.yml b/.github/workflows/gate-ratchet.yml index 40af951854..0c4cf2646c 100644 --- a/.github/workflows/gate-ratchet.yml +++ b/.github/workflows/gate-ratchet.yml @@ -53,6 +53,12 @@ on: type: string default: '120' +# A newer push or PR commit replaces this file's pending run instead of +# queueing behind it; a dispatch is never cancelled (gHashTag/trinity-fpga#869). +concurrency: + group: ${{ github.workflow_ref }}-${{ github.event_name == 'workflow_dispatch' && github.run_id || github.event_name }} + cancel-in-progress: ${{ github.event_name == 'pull_request' }} + jobs: ratchet: runs-on: ubuntu-latest diff --git a/.github/workflows/gates-can-fail.yml b/.github/workflows/gates-can-fail.yml index 12278e93fb..b115be876a 100644 --- a/.github/workflows/gates-can-fail.yml +++ b/.github/workflows/gates-can-fail.yml @@ -68,6 +68,12 @@ env: # first commit (its known steps sit in tools/tee_pipefail_baseline.txt). MIN_TESTABLE: '11' +# A newer push or PR commit replaces this file's pending run instead of +# queueing behind it; a dispatch is never cancelled (gHashTag/trinity-fpga#869). +concurrency: + group: ${{ github.workflow_ref }}-${{ github.event_name == 'workflow_dispatch' && github.run_id || github.event_name }} + cancel-in-progress: ${{ github.event_name == 'pull_request' }} + jobs: gates-can-fail: runs-on: ubuntu-latest diff --git a/.github/workflows/gf-adder-ref-tb.yml b/.github/workflows/gf-adder-ref-tb.yml index 1e62e4bd30..3a044b3861 100644 --- a/.github/workflows/gf-adder-ref-tb.yml +++ b/.github/workflows/gf-adder-ref-tb.yml @@ -22,6 +22,12 @@ on: - 'formal/gf_adder_ref_tb.v' - 'formal/gf_mul_ref_tb.v' +# A newer push or PR commit replaces this file's pending run instead of +# queueing behind it; a dispatch is never cancelled (gHashTag/trinity-fpga#869). +concurrency: + group: ${{ github.workflow_ref }}-${{ github.event_name == 'workflow_dispatch' && github.run_id || github.event_name }} + cancel-in-progress: ${{ github.event_name == 'pull_request' }} + jobs: ref-tb: runs-on: ubuntu-latest diff --git a/.github/workflows/hardware-row-gate.yml b/.github/workflows/hardware-row-gate.yml index df83020d37..fdf19ab2f5 100644 --- a/.github/workflows/hardware-row-gate.yml +++ b/.github/workflows/hardware-row-gate.yml @@ -25,6 +25,12 @@ on: - '.github/workflows/hardware-row-gate.yml' workflow_dispatch: +# A newer push or PR commit replaces this file's pending run instead of +# queueing behind it; a dispatch is never cancelled (gHashTag/trinity-fpga#869). +concurrency: + group: ${{ github.workflow_ref }}-${{ github.event_name == 'workflow_dispatch' && github.run_id || github.event_name }} + cancel-in-progress: ${{ github.event_name == 'pull_request' }} + jobs: hardware-rows: runs-on: ubuntu-latest diff --git a/.github/workflows/harness.yml b/.github/workflows/harness.yml index 3468bb1071..988aa98b65 100644 --- a/.github/workflows/harness.yml +++ b/.github/workflows/harness.yml @@ -19,6 +19,12 @@ on: push: branches: [main, master] +# A newer push or PR commit replaces this file's pending run instead of +# queueing behind it; a dispatch is never cancelled (gHashTag/trinity-fpga#869). +concurrency: + group: ${{ github.workflow_ref }}-${{ github.event_name == 'workflow_dispatch' && github.run_id || github.event_name }} + cancel-in-progress: ${{ github.event_name == 'pull_request' }} + jobs: harness: runs-on: ubuntu-latest diff --git a/.github/workflows/latex-hygiene.yml b/.github/workflows/latex-hygiene.yml index 185fa7a643..68c7df4402 100644 --- a/.github/workflows/latex-hygiene.yml +++ b/.github/workflows/latex-hygiene.yml @@ -6,6 +6,12 @@ name: LaTeX hygiene on: [pull_request, push] +# A newer push or PR commit replaces this file's pending run instead of +# queueing behind it; a dispatch is never cancelled (gHashTag/trinity-fpga#869). +concurrency: + group: ${{ github.workflow_ref }}-${{ github.event_name == 'workflow_dispatch' && github.run_id || github.event_name }} + cancel-in-progress: ${{ github.event_name == 'pull_request' }} + jobs: latex-hygiene: runs-on: ubuntu-latest diff --git a/.github/workflows/lut-report.yml b/.github/workflows/lut-report.yml index 1735b14319..ee47881370 100644 --- a/.github/workflows/lut-report.yml +++ b/.github/workflows/lut-report.yml @@ -13,6 +13,12 @@ on: branches: [main] workflow_dispatch: +# A newer push or PR commit replaces this file's pending run instead of +# queueing behind it; a dispatch is never cancelled (gHashTag/trinity-fpga#869). +concurrency: + group: ${{ github.workflow_ref }}-${{ github.event_name == 'workflow_dispatch' && github.run_id || github.event_name }} + cancel-in-progress: ${{ github.event_name == 'pull_request' }} + jobs: yosys-synth: runs-on: ubuntu-22.04 diff --git a/.github/workflows/module-loader-gate.yml b/.github/workflows/module-loader-gate.yml index 874145e7b4..bda562cf8a 100644 --- a/.github/workflows/module-loader-gate.yml +++ b/.github/workflows/module-loader-gate.yml @@ -31,6 +31,12 @@ on: - '.github/workflows/module-loader-gate.yml' workflow_dispatch: +# A newer push or PR commit replaces this file's pending run instead of +# queueing behind it; a dispatch is never cancelled (gHashTag/trinity-fpga#869). +concurrency: + group: ${{ github.workflow_ref }}-${{ github.event_name == 'workflow_dispatch' && github.run_id || github.event_name }} + cancel-in-progress: ${{ github.event_name == 'pull_request' }} + jobs: module-loaders: runs-on: ubuntu-latest diff --git a/.github/workflows/narrow-register-gate.yml b/.github/workflows/narrow-register-gate.yml index 0148ff025d..8fd8b4d6ad 100644 --- a/.github/workflows/narrow-register-gate.yml +++ b/.github/workflows/narrow-register-gate.yml @@ -34,6 +34,12 @@ on: - '.github/workflows/narrow-register-gate.yml' workflow_dispatch: +# A newer push or PR commit replaces this file's pending run instead of +# queueing behind it; a dispatch is never cancelled (gHashTag/trinity-fpga#869). +concurrency: + group: ${{ github.workflow_ref }}-${{ github.event_name == 'workflow_dispatch' && github.run_id || github.event_name }} + cancel-in-progress: ${{ github.event_name == 'pull_request' }} + jobs: narrow-register: runs-on: ubuntu-latest diff --git a/.github/workflows/orphan-artefacts.yml b/.github/workflows/orphan-artefacts.yml index 6784761d37..f5acbdfd0d 100644 --- a/.github/workflows/orphan-artefacts.yml +++ b/.github/workflows/orphan-artefacts.yml @@ -8,6 +8,12 @@ name: Orphaned artefacts on: [pull_request, push] +# A newer push or PR commit replaces this file's pending run instead of +# queueing behind it; a dispatch is never cancelled (gHashTag/trinity-fpga#869). +concurrency: + group: ${{ github.workflow_ref }}-${{ github.event_name == 'workflow_dispatch' && github.run_id || github.event_name }} + cancel-in-progress: ${{ github.event_name == 'pull_request' }} + jobs: orphan-artefacts: runs-on: ubuntu-latest diff --git a/.github/workflows/overfull.yml b/.github/workflows/overfull.yml index 270a8a6fbe..93e3fa7373 100644 --- a/.github/workflows/overfull.yml +++ b/.github/workflows/overfull.yml @@ -8,6 +8,12 @@ name: Overfull boxes on: [pull_request, push] +# A newer push or PR commit replaces this file's pending run instead of +# queueing behind it; a dispatch is never cancelled (gHashTag/trinity-fpga#869). +concurrency: + group: ${{ github.workflow_ref }}-${{ github.event_name == 'workflow_dispatch' && github.run_id || github.event_name }} + cancel-in-progress: ${{ github.event_name == 'pull_request' }} + jobs: overfull: runs-on: ubuntu-latest diff --git a/.github/workflows/pack-layout-gate.yml b/.github/workflows/pack-layout-gate.yml index 9b448ace93..893debac20 100644 --- a/.github/workflows/pack-layout-gate.yml +++ b/.github/workflows/pack-layout-gate.yml @@ -42,6 +42,12 @@ on: - 'conformance/vectors/*_exact.json' workflow_dispatch: +# A newer push or PR commit replaces this file's pending run instead of +# queueing behind it; a dispatch is never cancelled (gHashTag/trinity-fpga#869). +concurrency: + group: ${{ github.workflow_ref }}-${{ github.event_name == 'workflow_dispatch' && github.run_id || github.event_name }} + cancel-in-progress: ${{ github.event_name == 'pull_request' }} + jobs: pack-layouts: runs-on: ubuntu-latest diff --git a/.github/workflows/paper-numbers.yml b/.github/workflows/paper-numbers.yml index 1c52ab76f6..22b97828e5 100644 --- a/.github/workflows/paper-numbers.yml +++ b/.github/workflows/paper-numbers.yml @@ -27,6 +27,12 @@ on: push: branches: [main, master] +# A newer push or PR commit replaces this file's pending run instead of +# queueing behind it; a dispatch is never cancelled (gHashTag/trinity-fpga#869). +concurrency: + group: ${{ github.workflow_ref }}-${{ github.event_name == 'workflow_dispatch' && github.run_id || github.event_name }} + cancel-in-progress: ${{ github.event_name == 'pull_request' }} + jobs: paper-numbers: runs-on: ubuntu-latest diff --git a/.github/workflows/reachability-ratchet.yml b/.github/workflows/reachability-ratchet.yml index 57e9dd257e..f3c8a5dc69 100644 --- a/.github/workflows/reachability-ratchet.yml +++ b/.github/workflows/reachability-ratchet.yml @@ -49,6 +49,12 @@ defaults: run: shell: bash -eo pipefail {0} +# A newer push or PR commit replaces this file's pending run instead of +# queueing behind it; a dispatch is never cancelled (gHashTag/trinity-fpga#869). +concurrency: + group: ${{ github.workflow_ref }}-${{ github.event_name == 'workflow_dispatch' && github.run_id || github.event_name }} + cancel-in-progress: ${{ github.event_name == 'pull_request' }} + jobs: ratchet: runs-on: ubuntu-latest diff --git a/.github/workflows/ref-kinds.yml b/.github/workflows/ref-kinds.yml index 22c8ca8de8..01fc277af4 100644 --- a/.github/workflows/ref-kinds.yml +++ b/.github/workflows/ref-kinds.yml @@ -8,6 +8,12 @@ name: Reference kinds on: [pull_request, push] +# A newer push or PR commit replaces this file's pending run instead of +# queueing behind it; a dispatch is never cancelled (gHashTag/trinity-fpga#869). +concurrency: + group: ${{ github.workflow_ref }}-${{ github.event_name == 'workflow_dispatch' && github.run_id || github.event_name }} + cancel-in-progress: ${{ github.event_name == 'pull_request' }} + jobs: ref-kinds: runs-on: ubuntu-latest diff --git a/.github/workflows/reproducibility-gate.yml b/.github/workflows/reproducibility-gate.yml index d80050becc..26020d905c 100644 --- a/.github/workflows/reproducibility-gate.yml +++ b/.github/workflows/reproducibility-gate.yml @@ -29,6 +29,12 @@ on: - '.github/workflows/reproducibility-gate.yml' workflow_dispatch: +# A newer push or PR commit replaces this file's pending run instead of +# queueing behind it; a dispatch is never cancelled (gHashTag/trinity-fpga#869). +concurrency: + group: ${{ github.workflow_ref }}-${{ github.event_name == 'workflow_dispatch' && github.run_id || github.event_name }} + cancel-in-progress: ${{ github.event_name == 'pull_request' }} + jobs: reproducibility: runs-on: ubuntu-latest diff --git a/.github/workflows/rtl-check.yml b/.github/workflows/rtl-check.yml index b1d9e43e55..c124d6eb4f 100644 --- a/.github/workflows/rtl-check.yml +++ b/.github/workflows/rtl-check.yml @@ -22,6 +22,12 @@ on: permissions: contents: read +# A newer push or PR commit replaces this file's pending run instead of +# queueing behind it; a dispatch is never cancelled (gHashTag/trinity-fpga#869). +concurrency: + group: ${{ github.workflow_ref }}-${{ github.event_name == 'workflow_dispatch' && github.run_id || github.event_name }} + cancel-in-progress: ${{ github.event_name == 'pull_request' }} + jobs: mini: uses: gHashTag/trinity/.github/workflows/rtl-check.yml@main diff --git a/.github/workflows/rtl-datapath-gate.yml b/.github/workflows/rtl-datapath-gate.yml index 06446ed81e..5b94d8e55c 100644 --- a/.github/workflows/rtl-datapath-gate.yml +++ b/.github/workflows/rtl-datapath-gate.yml @@ -26,6 +26,12 @@ on: - '.github/workflows/rtl-datapath-gate.yml' workflow_dispatch: +# A newer push or PR commit replaces this file's pending run instead of +# queueing behind it; a dispatch is never cancelled (gHashTag/trinity-fpga#869). +concurrency: + group: ${{ github.workflow_ref }}-${{ github.event_name == 'workflow_dispatch' && github.run_id || github.event_name }} + cancel-in-progress: ${{ github.event_name == 'pull_request' }} + jobs: datapath: runs-on: ubuntu-latest diff --git a/.github/workflows/run-syntax-gate.yml b/.github/workflows/run-syntax-gate.yml index 59b47d7369..00692e12c6 100644 --- a/.github/workflows/run-syntax-gate.yml +++ b/.github/workflows/run-syntax-gate.yml @@ -19,6 +19,12 @@ on: - 'tools/test_check_run_syntax.py' workflow_dispatch: +# A newer push or PR commit replaces this file's pending run instead of +# queueing behind it; a dispatch is never cancelled (gHashTag/trinity-fpga#869). +concurrency: + group: ${{ github.workflow_ref }}-${{ github.event_name == 'workflow_dispatch' && github.run_id || github.event_name }} + cancel-in-progress: ${{ github.event_name == 'pull_request' }} + jobs: run-syntax: runs-on: ubuntu-latest diff --git a/.github/workflows/script-rot.yml b/.github/workflows/script-rot.yml index f68e83a666..e72b25f110 100644 --- a/.github/workflows/script-rot.yml +++ b/.github/workflows/script-rot.yml @@ -14,6 +14,12 @@ on: push: branches: [main, master] +# A newer push or PR commit replaces this file's pending run instead of +# queueing behind it; a dispatch is never cancelled (gHashTag/trinity-fpga#869). +concurrency: + group: ${{ github.workflow_ref }}-${{ github.event_name == 'workflow_dispatch' && github.run_id || github.event_name }} + cancel-in-progress: ${{ github.event_name == 'pull_request' }} + jobs: script-rot: runs-on: ubuntu-latest diff --git a/.github/workflows/self-consistency.yml b/.github/workflows/self-consistency.yml index bcdc62f4cf..a2ec4acd86 100644 --- a/.github/workflows/self-consistency.yml +++ b/.github/workflows/self-consistency.yml @@ -10,6 +10,12 @@ name: Document self-consistency on: [pull_request, push] +# A newer push or PR commit replaces this file's pending run instead of +# queueing behind it; a dispatch is never cancelled (gHashTag/trinity-fpga#869). +concurrency: + group: ${{ github.workflow_ref }}-${{ github.event_name == 'workflow_dispatch' && github.run_id || github.event_name }} + cancel-in-progress: ${{ github.event_name == 'pull_request' }} + jobs: self-consistency: runs-on: ubuntu-latest diff --git a/.github/workflows/stale-citation-gate.yml b/.github/workflows/stale-citation-gate.yml index 384f974cf8..ad14ffd01e 100644 --- a/.github/workflows/stale-citation-gate.yml +++ b/.github/workflows/stale-citation-gate.yml @@ -30,6 +30,12 @@ on: - '.github/workflows/stale-citation-gate.yml' workflow_dispatch: +# A newer push or PR commit replaces this file's pending run instead of +# queueing behind it; a dispatch is never cancelled (gHashTag/trinity-fpga#869). +concurrency: + group: ${{ github.workflow_ref }}-${{ github.event_name == 'workflow_dispatch' && github.run_id || github.event_name }} + cancel-in-progress: ${{ github.event_name == 'pull_request' }} + jobs: stale-citations: runs-on: ubuntu-latest diff --git a/.github/workflows/tee-pipefail-gate.yml b/.github/workflows/tee-pipefail-gate.yml index 18f8a72644..e17fcaf297 100644 --- a/.github/workflows/tee-pipefail-gate.yml +++ b/.github/workflows/tee-pipefail-gate.yml @@ -24,6 +24,12 @@ on: - 'tools/tee_pipefail_baseline.txt' workflow_dispatch: +# A newer push or PR commit replaces this file's pending run instead of +# queueing behind it; a dispatch is never cancelled (gHashTag/trinity-fpga#869). +concurrency: + group: ${{ github.workflow_ref }}-${{ github.event_name == 'workflow_dispatch' && github.run_id || github.event_name }} + cancel-in-progress: ${{ github.event_name == 'pull_request' }} + jobs: tee-pipefail: runs-on: ubuntu-latest diff --git a/.github/workflows/tnf-boundary-gate.yml b/.github/workflows/tnf-boundary-gate.yml index df64a9a6af..815918d737 100644 --- a/.github/workflows/tnf-boundary-gate.yml +++ b/.github/workflows/tnf-boundary-gate.yml @@ -53,6 +53,12 @@ defaults: run: shell: bash -eo pipefail {0} +# A newer push or PR commit replaces this file's pending run instead of +# queueing behind it; a dispatch is never cancelled (gHashTag/trinity-fpga#869). +concurrency: + group: ${{ github.workflow_ref }}-${{ github.event_name == 'workflow_dispatch' && github.run_id || github.event_name }} + cancel-in-progress: ${{ github.event_name == 'pull_request' }} + jobs: boundary: runs-on: ubuntu-latest diff --git a/.github/workflows/tri-build.yml b/.github/workflows/tri-build.yml index 810b6d555d..6f9f0d07d7 100644 --- a/.github/workflows/tri-build.yml +++ b/.github/workflows/tri-build.yml @@ -45,6 +45,12 @@ on: env: ZIG_VERSION: '0.16.0' +# A newer push or PR commit replaces this file's pending run instead of +# queueing behind it; a dispatch is never cancelled (gHashTag/trinity-fpga#869). +concurrency: + group: ${{ github.workflow_ref }}-${{ github.event_name == 'workflow_dispatch' && github.run_id || github.event_name }} + cancel-in-progress: ${{ github.event_name == 'pull_request' }} + jobs: build: runs-on: ubuntu-latest diff --git a/.github/workflows/trinet-portability.yml b/.github/workflows/trinet-portability.yml index 3a9be357c1..a1119d0292 100644 --- a/.github/workflows/trinet-portability.yml +++ b/.github/workflows/trinet-portability.yml @@ -33,6 +33,12 @@ on: schedule: - cron: '41 5 * * *' +# A newer push or PR commit replaces this file's pending run instead of +# queueing behind it; a dispatch is never cancelled (gHashTag/trinity-fpga#869). +concurrency: + group: ${{ github.workflow_ref }}-${{ github.event_name == 'workflow_dispatch' && github.run_id || github.event_name }} + cancel-in-progress: ${{ github.event_name == 'pull_request' }} + jobs: receipt-keys: runs-on: ubuntu-latest diff --git a/.github/workflows/undefined-outputs.yml b/.github/workflows/undefined-outputs.yml index 2c7a8a6bca..61ab880f85 100644 --- a/.github/workflows/undefined-outputs.yml +++ b/.github/workflows/undefined-outputs.yml @@ -12,6 +12,12 @@ name: Undefined outputs on: [pull_request, push] +# A newer push or PR commit replaces this file's pending run instead of +# queueing behind it; a dispatch is never cancelled (gHashTag/trinity-fpga#869). +concurrency: + group: ${{ github.workflow_ref }}-${{ github.event_name == 'workflow_dispatch' && github.run_id || github.event_name }} + cancel-in-progress: ${{ github.event_name == 'pull_request' }} + jobs: undefined-outputs: runs-on: ubuntu-latest diff --git a/.github/workflows/withdrawn-live.yml b/.github/workflows/withdrawn-live.yml index 1f74a07ced..775078643d 100644 --- a/.github/workflows/withdrawn-live.yml +++ b/.github/workflows/withdrawn-live.yml @@ -8,6 +8,12 @@ name: Withdrawn numbers on: [pull_request, push] +# A newer push or PR commit replaces this file's pending run instead of +# queueing behind it; a dispatch is never cancelled (gHashTag/trinity-fpga#869). +concurrency: + group: ${{ github.workflow_ref }}-${{ github.event_name == 'workflow_dispatch' && github.run_id || github.event_name }} + cancel-in-progress: ${{ github.event_name == 'pull_request' }} + jobs: withdrawn-live: runs-on: ubuntu-latest diff --git a/.github/workflows/workflow-path-gate.yml b/.github/workflows/workflow-path-gate.yml index b9f0b7b452..1e5fbfa25c 100644 --- a/.github/workflows/workflow-path-gate.yml +++ b/.github/workflows/workflow-path-gate.yml @@ -22,6 +22,12 @@ on: - 'research/audit_workflow_paths.py' workflow_dispatch: +# A newer push or PR commit replaces this file's pending run instead of +# queueing behind it; a dispatch is never cancelled (gHashTag/trinity-fpga#869). +concurrency: + group: ${{ github.workflow_ref }}-${{ github.event_name == 'workflow_dispatch' && github.run_id || github.event_name }} + cancel-in-progress: ${{ github.event_name == 'pull_request' }} + jobs: paths: runs-on: ubuntu-latest diff --git a/.github/workflows/wrapper-conversion-conformance.yml b/.github/workflows/wrapper-conversion-conformance.yml index e6f86c1f29..54e7d0f9a8 100644 --- a/.github/workflows/wrapper-conversion-conformance.yml +++ b/.github/workflows/wrapper-conversion-conformance.yml @@ -36,6 +36,12 @@ on: - '.github/workflows/wrapper-conversion-conformance.yml' workflow_dispatch: +# A newer push or PR commit replaces this file's pending run instead of +# queueing behind it; a dispatch is never cancelled (gHashTag/trinity-fpga#869). +concurrency: + group: ${{ github.workflow_ref }}-${{ github.event_name == 'workflow_dispatch' && github.run_id || github.event_name }} + cancel-in-progress: ${{ github.event_name == 'pull_request' }} + jobs: conversion: runs-on: ubuntu-latest diff --git a/.github/workflows/wrapper-fsm-sim.yml b/.github/workflows/wrapper-fsm-sim.yml index 5287558158..9611bd56f2 100644 --- a/.github/workflows/wrapper-fsm-sim.yml +++ b/.github/workflows/wrapper-fsm-sim.yml @@ -28,6 +28,12 @@ on: # all five results - 'fpga/openxc7-synth/STARTUPE2_mock.v' - '.github/workflows/wrapper-fsm-sim.yml' +# A newer push or PR commit replaces this file's pending run instead of +# queueing behind it; a dispatch is never cancelled (gHashTag/trinity-fpga#869). +concurrency: + group: ${{ github.workflow_ref }}-${{ github.event_name == 'workflow_dispatch' && github.run_id || github.event_name }} + cancel-in-progress: ${{ github.event_name == 'pull_request' }} + jobs: fsm-sim: runs-on: ubuntu-latest From e4064a5cc06dbe0bb7f46ac16108f912c4ff6213 Mon Sep 17 00:00:00 2001 From: Dmitrii Vasilev Date: Mon, 5 Oct 2026 02:30:12 +0700 Subject: [PATCH 2/3] ci(concurrency): gate every push-to-main workflow on a group of its own The first commit of this branch gave the 102 ungrouped push workflows a concurrency group (2/104 -> 104/104). Nothing kept the next workflow from arriving without one. This adds the gate. tools/check_concurrency_groups.py reads every workflow with PyYAML's composer, as the runner reads it, and fails on three things: UNGROUPED runs on a push to main, no top-level group BAD_REUSABLE workflow_call with a top-level group: a called workflow reads its caller's github context, so the group is the caller's own SHARED_KEY two files whose groups are the same text once each file's path and name are put in (two files here share a name:) A dispatchable group without github.run_id is printed as a note, not a failure: bench007-probe-ax7203 and trinity-identity-gate are built that way. Push-to-main follows GitHub's filter rules (branches in order with `!`, branches-ignore, tags-only, `?` and `+` as repeats). Exit 0 clean, 1 defect, 2 cannot tell (no PyYAML, unreadable YAML, duplicate or merge key, no workflows). Measured before committing: - this branch: 123 read, 105 on push to main, 105 grouped, rc 0 - main (57de4a1f5): 102 UNGROUPED, rc 1 -- the negative control - per-file agreement with an independent line-based reader of the same rules: 122/122 on main and 122/122 on this branch - tools/test_check_concurrency_groups.py: 57 of 57 planted cases - mutation run on the checker: 29/29 mutants turn the test red; one more (`*` crossing `/`) is equivalent, since only `main` is asked about and it has no `/` - research/audit_workflow_paths.py: 0 dead paths, 0 unwatched scripts - the gates-can-fail case: clean rc 0, planted workflow rc 1 Registered in tools/check_gates_can_fail.py; MIN_TESTABLE 11 -> 12, as that workflow's own comment asks of every gate added to the harness. Refs #869 Co-Authored-By: Claude Opus 5.5 --- .github/workflows/concurrency-gate.yml | 47 ++++ .github/workflows/gates-can-fail.yml | 11 +- tools/check_concurrency_groups.py | 362 +++++++++++++++++++++++++ tools/check_gates_can_fail.py | 12 + tools/test_check_concurrency_groups.py | 196 +++++++++++++ 5 files changed, 624 insertions(+), 4 deletions(-) create mode 100644 .github/workflows/concurrency-gate.yml create mode 100644 tools/check_concurrency_groups.py create mode 100644 tools/test_check_concurrency_groups.py diff --git a/.github/workflows/concurrency-gate.yml b/.github/workflows/concurrency-gate.yml new file mode 100644 index 0000000000..9d90463427 --- /dev/null +++ b/.github/workflows/concurrency-gate.yml @@ -0,0 +1,47 @@ +name: Concurrency gate + +# Every workflow that runs on a push to main needs a top-level concurrency +# group of its own, or a burst of merges queues every run of every workflow. +# Before #870, 2 of 104 had one and the repository had 194 runs queued at once +# (gHashTag/trinity-fpga#869). #870 gave the other 102 the group this file +# uses; this gate keeps a new workflow from arriving without one. +# See tools/check_concurrency_groups.py; tools/test_check_concurrency_groups.py +# plants every case it claims to decide, and runs first. + +on: + push: + branches: [main] + paths: + - '.github/workflows/**' + - 'tools/check_concurrency_groups.py' + - 'tools/test_check_concurrency_groups.py' + pull_request: + paths: + - '.github/workflows/**' + - 'tools/check_concurrency_groups.py' + - 'tools/test_check_concurrency_groups.py' + workflow_dispatch: + +# A newer push or PR commit replaces this file's pending run instead of +# queueing behind it; a dispatch is never cancelled (gHashTag/trinity-fpga#869). +concurrency: + group: ${{ github.workflow_ref }}-${{ github.event_name == 'workflow_dispatch' && github.run_id || github.event_name }} + cancel-in-progress: ${{ github.event_name == 'pull_request' }} + +jobs: + concurrency-groups: + runs-on: ubuntu-latest + timeout-minutes: 5 + steps: + - uses: actions/checkout@v4 + # The checker reads workflows with PyYAML, as the runner reads them. + # Without PyYAML it answers "cannot tell" (rc 2), never clean. + - name: PyYAML for the checker + run: | + python3 -c 'import yaml' 2>/dev/null \ + || python3 -m pip install --quiet --user --break-system-packages pyyaml + python3 -c 'import yaml; print("PyYAML", yaml.__version__)' + - name: The checker decides every planted case + run: python3 tools/test_check_concurrency_groups.py + - name: Every push-to-main workflow has a group of its own + run: python3 tools/check_concurrency_groups.py diff --git a/.github/workflows/gates-can-fail.yml b/.github/workflows/gates-can-fail.yml index b115be876a..da54dc838e 100644 --- a/.github/workflows/gates-can-fail.yml +++ b/.github/workflows/gates-can-fail.yml @@ -66,7 +66,9 @@ env: # main for that one cell, so the harness could not plant its defect. # 10 -> 11: check_tee_pipefail added to the harness, green on main from its # first commit (its known steps sit in tools/tee_pipefail_baseline.txt). - MIN_TESTABLE: '11' + # 11 -> 12: check_concurrency_groups added to the harness, green from its + # first commit, which also gave the 102 ungrouped workflows a group (#870). + MIN_TESTABLE: '12' # A newer push or PR commit replaces this file's pending run instead of # queueing behind it; a dispatch is never cancelled (gHashTag/trinity-fpga#869). @@ -89,9 +91,10 @@ jobs: - name: Materialise the sibling namespaces run: bash tools/materialise_siblings.sh - # check_tee_pipefail reads workflows with PyYAML; without it the gate - # says "cannot tell" on a clean tree, and could not be shown to fail. - - name: PyYAML for check_tee_pipefail + # check_tee_pipefail and check_concurrency_groups read workflows with + # PyYAML; without it each says "cannot tell" on a clean tree, and could + # not be shown to fail. + - name: PyYAML for the workflow gates run: | python3 -c 'import yaml' 2>/dev/null \ || python3 -m pip install --quiet --user --break-system-packages pyyaml diff --git a/tools/check_concurrency_groups.py b/tools/check_concurrency_groups.py new file mode 100644 index 0000000000..ff77ae3f82 --- /dev/null +++ b/tools/check_concurrency_groups.py @@ -0,0 +1,362 @@ +#!/usr/bin/env python3 +r"""Does every workflow that runs on a push to main have a concurrency group? + +Without one, a burst of merges queues every run of every workflow: each push +to main starts its own copy, no copy replaces another, and the account-wide +runner pool drains them one at a time. Before gHashTag/trinity-fpga#870, 2 of +the 104 push-to-main workflows here had a group; on 2026-10-04 the repository +had 194 runs queued at once, and figure-names.yml ran three copies on main at +the same time (#869). + +A workflow passes when it never runs on a push to main, or does and has a +top-level `concurrency:` with a group. Three things fail: + + - UNGROUPED: it runs on a push to main and has no top-level group. A job's + own `concurrency:` does not count -- the run still starts and queues; only + that job waits. + - BAD_REUSABLE: `workflow_call` and a top-level group. A called workflow + reads the `github` context of its CALLER, so a group built from + `github.workflow_ref` or `github.workflow` is the caller's own group, and + the call can wait on the run that made it (GitHub cancels such a pair as a + deadlock). The caller's group already covers the call. + - SHARED_KEY: two files whose groups are the same text once each file's + `github.workflow_ref` (its path) and `github.workflow` (its `name:`, or its + path when it has none) are put in. Two files here share a `name:` + (conformance-selftest.yml and conformance-golden-selftests.yml), so a group + keyed on `github.workflow` would make one file's push replace the other's + pending run. Every file with a group is compared, not only push ones. + +Noted, not failed: a workflow that can be dispatched by hand and whose group +has no `github.run_id` (DISPATCH_CANCELS). A newer push then replaces its +pending dispatch, or cancels a running one under cancel-in-progress. Two +workflows here are built that way on purpose; the choice is the owner's. + +"Runs on a push to main" follows GitHub's filters: `branches:` patterns read +in order, the last match deciding and `!` excluding (`*` stops at `/`, `**` +does not, `?` and `+` repeat the character before them); `branches-ignore:`; +and a filter with only `tags:` / `tags-ignore:` never starts the workflow for +a branch push. `paths:` is ignored: it makes a run rarer, not single. + +YAML is read with PyYAML's composer, as the runner reads it: the `on` key stays +the string `on` (a loader turns it into the boolean True), and an alias is the +node its anchor names. Every `if` below tests a named bool. + +Exit 0 clean, 1 a defect above, 2 cannot tell: no PyYAML, no workflows, a file +that is not YAML, a key given twice, a merge key (`<<:`). Nothing read is not +clean. tools/test_check_concurrency_groups.py plants every case and runs first. + +Not decided here: whether two groups that are different text still meet at run +time (a literal group equal to another file's `name:`), and whether a group's +expression evaluates -- GitHub refuses that run as a startup failure, which is +already red. +""" +from __future__ import annotations + +import pathlib +import re +import sys + +ROOT = pathlib.Path(__file__).resolve().parents[1] +WORKFLOWS = ROOT / ".github" / "workflows" + +MERGE_TAG = "tag:yaml.org,2002:merge" +NULL_TAG = "tag:yaml.org,2002:null" +WORKFLOW_REF = re.compile(r"\bgithub\.workflow_ref\b") +WORKFLOW_NAME = re.compile(r"\bgithub\.workflow\b(?!_)") +EXPRESSION = re.compile(r"\$\{\{(.*?)\}\}", re.S) + + +class CannotTell(Exception): + pass + + +def mapping(node, where: str) -> dict: + """A mapping node as {key: value node}. A key that is not a string, a key + given twice or a merge key is "cannot tell": which value the runner takes + is then not ours to guess.""" + import yaml + + is_map = isinstance(node, yaml.MappingNode) + if not is_map: + raise CannotTell(f"{where} is not a mapping") + out: dict = {} + for k, v in node.value: + is_merge = k.tag == MERGE_TAG + if is_merge: + raise CannotTell(f"{where}: a YAML merge key (`<<:`) -- cannot tell what it brings in") + is_text = isinstance(k, yaml.ScalarNode) + if not is_text: + raise CannotTell(f"{where}: a key that is not a string") + twice = k.value in out + if twice: + raise CannotTell(f"{where}: key `{k.value}` given twice") + out[k.value] = v + return out + + +def scalar(node, where: str) -> str: + import yaml + + is_text = isinstance(node, yaml.ScalarNode) + if not is_text: + raise CannotTell(f"{where} is not a string") + return node.value + + +def is_empty(node) -> bool: + """`push:` with nothing after it.""" + import yaml + + return node is None or (isinstance(node, yaml.ScalarNode) and node.tag == NULL_TAG) + + +def words(node, where: str) -> list[str]: + """A string or a list of strings, as a list.""" + import yaml + + is_list = isinstance(node, yaml.SequenceNode) + if is_list: + return [scalar(n, where) for n in node.value] + return [scalar(node, where)] + + +def triggers(node) -> dict: + """{event: its filter node, or None} of the `on:` value.""" + import yaml + + is_map = isinstance(node, yaml.MappingNode) + if is_map: + return mapping(node, "`on:`") + return {e: None for e in words(node, "`on:`")} + + +def pattern(glob: str) -> re.Pattern: + """A GitHub branch filter as a regex: `**` anything, `*` anything but `/`, + `?` / `+` zero-or-one / one-or-more of the character before, `[..]` a set, + `\\` quotes the next character.""" + out, i = [], 0 + while i < len(glob): + c = glob[i] + double = glob.startswith("**", i) + is_set = c == "[" + quoted = c == "\\" and i + 1 < len(glob) + if double: + out.append(".*") + i += 2 + continue + if is_set: + end = glob.find("]", i + 1) + unclosed = end < 0 + if unclosed: + raise CannotTell(f"branch filter `{glob}`: a `[` with no `]`") + out.append(glob[i:end + 1]) + i = end + 1 + continue + if quoted: + out.append(re.escape(glob[i + 1])) + i += 2 + continue + star = c == "*" + repeat = c in "?+" + if star: + out.append("[^/]*") + elif repeat: + out.append(c) + else: + out.append(re.escape(c)) + i += 1 + return re.compile("".join(out) + r"\Z") + + +def matches(branch: str, globs: list[str]) -> bool: + """GitHub's rule: the last pattern that matches decides, and a `!` pattern + excludes again.""" + hit = False + for g in globs: + negated = g.startswith("!") + body = g[1:] if negated else g + found = pattern(body).match(branch) is not None + if found: + hit = not negated + return hit + + +def push_reaches_main(node) -> bool: + empty = is_empty(node) + if empty: + return True + f = mapping(node, "`on.push`") + has_branches = "branches" in f + has_ignore = "branches-ignore" in f + both = has_branches and has_ignore + if both: + raise CannotTell("`on.push` has both `branches` and `branches-ignore`") + if has_branches: + return matches("main", words(f["branches"], "`on.push.branches`")) + if has_ignore: + return not matches("main", words(f["branches-ignore"], "`on.push.branches-ignore`")) + tags_only = "tags" in f or "tags-ignore" in f + if tags_only: # GitHub: only tags filters, and a branch push never starts it + return False + return True + + +def group_of(node) -> tuple[str, str]: + """(group, cancel-in-progress) of a top-level `concurrency:` value.""" + import yaml + + is_text = isinstance(node, yaml.ScalarNode) + if is_text: + return node.value, "false" + c = mapping(node, "`concurrency:`") + has_group = "group" in c + group = scalar(c["group"], "`concurrency.group`") if has_group else "" + has_cancel = "cancel-in-progress" in c + cancel = scalar(c["cancel-in-progress"], "`concurrency.cancel-in-progress`") if has_cancel else "false" + return group, cancel + + +def render(group: str, path: str, name: str) -> str: + """The group as it reads for this file: its own path and name put in, and + the spacing inside each `${{ }}` made one form.""" + key = WORKFLOW_REF.sub(f"", group) + key = WORKFLOW_NAME.sub(f"", key) + key = EXPRESSION.sub(lambda m: "${{ " + " ".join(m.group(1).split()) + " }}", key) + return key.strip() + + +def relpath(path: pathlib.Path) -> str: + try: + return path.relative_to(ROOT).as_posix() + except ValueError: + return path.name + + +def classify(path: pathlib.Path) -> dict: + """{file, class, why, key} of one workflow. class None: it never runs on a + push to main and so needs no group; its key still counts for SHARED_KEY.""" + import yaml + + rel = relpath(path) + root = yaml.compose(path.read_text(), Loader=yaml.SafeLoader) + empty_file = root is None + if empty_file: + raise CannotTell("an empty file") + top = mapping(root, "the workflow") + has_on = "on" in top + if not has_on: + raise CannotTell("no top-level `on:`") + on = triggers(top["on"]) + named = "name" in top + name = scalar(top["name"], "`name:`") if named else rel + has_conc = "concurrency" in top + group, cancel = group_of(top["concurrency"]) if has_conc else ("", "false") + grouped = bool(group.strip()) + key = render(group, rel, name) if grouped else "" + has_push = "push" in on + to_main = has_push and push_reaches_main(on["push"]) + row = {"file": rel, "class": None, "why": "", "key": key, "on_main": to_main} + + reusable = "workflow_call" in on + bad_reusable = reusable and grouped + if bad_reusable: + row.update({"class": "BAD_REUSABLE", + "why": "a called workflow's group reads the caller's `github` context " + "-- it is the caller's group; give the caller the group instead"}) + return row + if not to_main: + return row + if reusable: + row.update({"class": "REUSABLE", "why": "called: the caller's group covers it"}) + return row + if not grouped: + why = "`concurrency:` without a group" if has_conc else "no top-level `concurrency:`" + row.update({"class": "UNGROUPED", "why": f"runs on a push to main, {why}"}) + return row + dispatchable = "workflow_dispatch" in on + per_run = "github.run_id" in group + cancels_dispatch = dispatchable and not per_run + if cancels_dispatch: + cancels = cancel.strip().strip("'\"") == "true" + how = "a push cancels a running dispatch" if cancels else "a newer run replaces a pending dispatch" + row.update({"class": "DISPATCH_CANCELS", "why": f"group has no github.run_id: {how}"}) + return row + row.update({"class": "GROUPED", "why": ""}) + return row + + +def main() -> int: + try: + import yaml + except ImportError: + print("CANNOT TELL: PyYAML is not installed -- no workflow can be read") + return 2 + has_dir = WORKFLOWS.is_dir() + files = sorted(list(WORKFLOWS.glob("*.yml")) + list(WORKFLOWS.glob("*.yaml"))) if has_dir else [] + none_found = not files + if none_found: + print(f"CANNOT TELL: no workflows under {relpath(WORKFLOWS)}") + return 2 + + rows, unread = [], [] + for wf in files: + try: + rows.append(classify(wf)) + except yaml.YAMLError as e: + unread.append(f"{relpath(wf)}: not readable as YAML -- {str(e).splitlines()[0]}") + except CannotTell as e: + unread.append(f"{relpath(wf)}: {e}") + + owners: dict[str, list[str]] = {} + for r in rows: + keyed = bool(r["key"]) + if keyed: + owners.setdefault(r["key"], []).append(r["file"]) + for r in rows: + others = [f for f in owners.get(r["key"], []) if f != r["file"]] + shared = bool(r["key"]) and bool(others) + if shared: + r["class"], r["why"] = "SHARED_KEY", "the same group as " + ", ".join(others) + + on_main = [r for r in rows if r["on_main"]] + covered = [r for r in on_main if r["class"] in ("GROUPED", "DISPATCH_CANCELS", "REUSABLE")] + bad = [r for r in rows if r["class"] in ("UNGROUPED", "BAD_REUSABLE", "SHARED_KEY")] + noted = [r for r in rows if r["class"] == "DISPATCH_CANCELS"] + print(f"workflows read: {len(rows)} of {len(files)}; on a push to main: {len(on_main)}, " + f"with a group of their own (or called): {len(covered)}") + + rc = 0 + for cls in ("UNGROUPED", "BAD_REUSABLE", "SHARED_KEY"): + hits = [r for r in bad if r["class"] == cls] + found = bool(hits) + if found: + print(f"\nFAIL {cls}: {len(hits)} workflow(s)") + for r in hits: + print(f" {r['file']}: {r['why']}") + rc = 1 + fails = rc == 1 + if fails: + print("\nA group that replaces a pending run and never cancels a dispatch:\n" + " concurrency:\n" + " group: ${{ github.workflow_ref }}-${{ github.event_name == 'workflow_dispatch'" + " && github.run_id || github.event_name }}\n" + " cancel-in-progress: ${{ github.event_name == 'pull_request' }}") + has_notes = bool(noted) + if has_notes: + print(f"\nNOTE DISPATCH_CANCELS (not a failure): {len(noted)} workflow(s)") + for r in noted: + print(f" {r['file']}: {r['why']}") + has_unread = bool(unread) + if has_unread: + print(f"\nCANNOT TELL: {len(unread)} workflow(s) not read:") + for e in unread: + print(f" {e}") + rc = rc or 2 + clean = rc == 0 + if clean: + print("OK: every workflow that runs on a push to main has a group of its own") + return rc + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/tools/check_gates_can_fail.py b/tools/check_gates_can_fail.py index 9e879d7591..76c1e12931 100644 --- a/tools/check_gates_can_fail.py +++ b/tools/check_gates_can_fail.py @@ -112,6 +112,18 @@ " run: |\n" " yosys -p synth.ys 2>&1 | tee synth.log\n", ), + # A workflow that runs on every push to main with no concurrency group: + # a burst of merges queues one run of it per push (#869). + "check_concurrency_groups": ( + "create", ".github/workflows/gate-selftest-concurrency.yml", + "# Written by check_gates_can_fail and removed again.\n" + "on: push\n" + "jobs:\n" + " selftest:\n" + " runs-on: ubuntu-latest\n" + " steps:\n" + " - run: true\n", + ), "check_withdrawn_live": ( "\\paragraph{Withdrawn.} The earlier claim of $77.31\\times$ is withdrawn: " "it was measured against the broken oracle.\n\n" diff --git a/tools/test_check_concurrency_groups.py b/tools/test_check_concurrency_groups.py new file mode 100644 index 0000000000..b458851ddc --- /dev/null +++ b/tools/test_check_concurrency_groups.py @@ -0,0 +1,196 @@ +#!/usr/bin/env python3 +"""Plants every case tools/check_concurrency_groups.py claims to decide, and +checks its exit code: 0 clean, 1 a defect, 2 cannot tell. + +"This turns red when X" is a claim to test by planting X (#819). Each case +writes its workflows into an empty tree and runs the checker's main() on it. +The cases began as the fixtures of a second, independent reader of the same +rules (a line-based one, in the loop that measured #869); on this repository's +122 workflows the two give the same class to every file, on main (102 +UNGROUPED) and on #870 (none). Three of those fixtures exist because a +mutation run of the line reader let a defect through: a trigger list at its +key's indent (`on:` then `- push`), a `branches:` list the same way, and a +comment shallower than the triggers. + +Exit 0 when every case gives its expected code, 1 otherwise. +""" +from __future__ import annotations + +import contextlib +import importlib.util +import io +import pathlib +import sys +import tempfile + +HERE = pathlib.Path(__file__).resolve().parent +spec = importlib.util.spec_from_file_location("ccg", HERE / "check_concurrency_groups.py") +ccg = importlib.util.module_from_spec(spec) +spec.loader.exec_module(ccg) + +# The group #870 gives every push workflow: one per file, a newer push or PR +# commit replaces the pending run, a dispatch gets a group of its own. +G = ("concurrency:\n" + " group: ${{ github.workflow_ref }}-${{ github.event_name == 'workflow_dispatch'" + " && github.run_id || github.event_name }}\n" + " cancel-in-progress: ${{ github.event_name == 'pull_request' }}\n") +J = "jobs:\n x:\n runs-on: ubuntu-latest\n steps:\n - run: true\n" + + +def one(text: str) -> list[tuple[str, str]]: + return [("a.yml", text)] + + +# (name, expected rc, [(file, text)], text the output must contain) +CASES: list[tuple[str, int, list[tuple[str, str]], str]] = [ + ("push to main, no group", 1, one("on:\n push:\n branches: [main]\n" + J), "UNGROUPED"), + ("push and dispatch, the #870 group", 0, one("on:\n push:\n workflow_dispatch:\n" + G + J), "OK"), + ("a group without run_id under dispatch is a note, not a failure", 0, + one("on:\n workflow_dispatch: {}\n push:\n branches: [main, master]\n" + "concurrency:\n group: id-${{ github.ref }}\n cancel-in-progress: true\n" + J), + "push cancels a running dispatch"), + ("... and without cancel-in-progress it replaces a pending one", 0, + one("on:\n workflow_dispatch:\n push:\nconcurrency:\n group: id-${{ github.ref }}\n" + J), + "replaces a pending dispatch"), + ("dispatch only", 0, one("on:\n workflow_dispatch:\n" + J), "OK"), + ("pull_request only", 0, one("on: pull_request\n" + J), "OK"), + ("push to another branch", 0, one("on:\n push:\n branches:\n - dev\n" + J), "OK"), + ("tags only: a branch push never starts it", 0, one("on:\n push:\n tags: ['v*']\n" + J), "OK"), + ("tags-ignore only, the same", 0, one("on:\n push:\n tags-ignore: ['v*']\n" + J), "OK"), + ("tags and branches: main again", 1, one("on:\n push:\n tags: ['v*']\n branches: [main]\n" + J), + "UNGROUPED"), + ("paths only: a rarer run, not a single one", 1, one("on:\n push:\n paths: ['src/**']\n" + J), + "UNGROUPED"), + ("inline list of events", 1, one("on: [push, pull_request]\n" + J), "UNGROUPED"), + ("scalar event", 1, one("on: push\n" + J), "UNGROUPED"), + ("quoted on key", 1, one('"on":\n push:\n branches:\n - "main"\n' + J), "UNGROUPED"), + ("events as a list at the key's indent", 1, one("on:\n- push\n- workflow_dispatch\n" + J), "UNGROUPED"), + ("branches as a list at the key's indent", 1, one("on:\n push:\n branches:\n - main\n" + J), + "UNGROUPED"), + ("a comment shallower than the triggers", 1, one("on:\n # a note\n push:\n" + J), "UNGROUPED"), + ("a commented-out group is no group", 1, one("on:\n push:\n# concurrency:\n# group: x\n" + J), + "UNGROUPED"), + ("a job's group does not stop the run queuing", 1, + one("on:\n push:\njobs:\n x:\n runs-on: ubuntu-latest\n concurrency:\n group: j\n" + " steps:\n - run: true\n"), "UNGROUPED"), + ("`concurrency:` with no group", 1, one("on: push\nconcurrency:\n cancel-in-progress: true\n" + J), + "without a group"), + ("an empty `concurrency:`", 1, one("on: push\nconcurrency:\n" + J), "without a group"), + ("the group as a plain string", 0, one("name: A\non: push\nconcurrency: ${{ github.workflow }}\n" + J), "OK"), + ("branches as one string", 1, one("on:\n push:\n branches: main\n" + J), "UNGROUPED"), + ("`**` reaches main", 1, one("on:\n push:\n branches: ['**']\n" + J), "UNGROUPED"), + ("`*` reaches main", 1, one("on:\n push:\n branches: ['*']\n" + J), "UNGROUPED"), + ("`release/*` does not", 0, one("on:\n push:\n branches: ['release/*']\n" + J), "OK"), + ("`!main` after `**` excludes it", 0, one("on:\n push:\n branches: ['**', '!main']\n" + J), "OK"), + ("`main` after `!main` brings it back: the last match decides", 1, + one("on:\n push:\n branches: ['!main', 'main']\n" + J), "UNGROUPED"), + ("`+` repeats the character before it, not any character", 1, + one("on:\n push:\n branches: ['mai+n']\n" + J), "UNGROUPED"), + ("`?` makes the character before it optional", 1, + one("on:\n push:\n branches: ['mainn?']\n" + J), "UNGROUPED"), + ("`ma?n` is `mn` or `man`, not `ma`, any character, `n`", 0, + one("on:\n push:\n branches: ['ma?n']\n" + J), "OK"), + ("a character set", 1, one("on:\n push:\n branches: ['[lm]ain']\n" + J), "UNGROUPED"), + ("branches-ignore: main", 0, one("on:\n push:\n branches-ignore: [main]\n" + J), "OK"), + ("branches-ignore of other branches reaches main", 1, + one("on:\n push:\n branches-ignore: ['feat/**']\n" + J), "UNGROUPED"), + ("an alias for the push filter is the node it names", 1, + one("on:\n pull_request: &f\n branches: [main]\n push: *f\n" + J), "UNGROUPED"), + ("called, no group: the caller's group covers it", 0, one("on:\n push:\n workflow_call:\n" + J), "OK"), + ("called, with a group: the caller's own group", 1, one("on:\n push:\n workflow_call:\n" + G + J), + "BAD_REUSABLE"), + ("called only, with a group: the same", 1, one("on:\n workflow_call:\n" + G + J), "BAD_REUSABLE"), + ("a .yaml file is read too", 1, [("a.yaml", "on: push\n" + J)], "a.yaml"), + ("two files, one name, a group keyed on the name", 1, + [("a.yml", "name: Same\non: push\nconcurrency:\n group: ${{ github.workflow }}-${{ github.ref }}\n" + J), + ("b.yml", "name: Same\non: push\nconcurrency:\n group: ${{ github.workflow }}-${{ github.ref }}\n" + J)], + "SHARED_KEY"), + ("... and spacing inside ${{ }} does not separate them", 1, + [("a.yml", "name: Same\non: push\nconcurrency: ${{github.workflow}}\n" + J), + ("b.yml", "name: Same\non: push\nconcurrency: ${{ github.workflow }}\n" + J)], "SHARED_KEY"), + ("two files, one name, the #870 group keyed on the file", 0, + [("a.yml", "name: Same\non: push\n" + G + J), ("b.yml", "name: Same\non: push\n" + G + J)], "OK"), + ("two names, a group keyed on the name", 0, + [("a.yml", "name: One\non: push\nconcurrency: ${{ github.workflow }}\n" + J), + ("b.yml", "name: Two\non: push\nconcurrency: ${{ github.workflow }}\n" + J)], "OK"), + ("no name: github.workflow is the path, so two files differ", 0, + [("a.yml", "on: push\nconcurrency: ${{ github.workflow }}\n" + J), + ("b.yml", "on: push\nconcurrency: ${{ github.workflow }}\n" + J)], "OK"), + ("one literal group in two files", 1, + [("a.yml", "on: push\nconcurrency:\n group: fixed\n" + J), + ("b.yml", "on: push\nconcurrency:\n group: fixed\n" + J)], "SHARED_KEY"), + ("... in two pull_request-only files too", 1, + [("a.yml", "on: pull_request\nconcurrency: fixed\n" + J), + ("b.yml", "on: pull_request\nconcurrency: fixed\n" + J)], "SHARED_KEY"), + ("a defect beside an unreadable file is still a defect", 1, + [("a.yml", "on: push\n" + J), ("b.yml", "on: [push\n")], "UNGROUPED"), + ("not YAML", 2, one("on: [push\n" + J), "not readable as YAML"), + ("a key given twice", 2, one("on: push\non: pull_request\n" + G + J), "given twice"), + ("a merge key", 2, one("x: &x\n push:\non:\n <<: *x\n" + G + J), "merge key"), + ("no `on:`", 2, one(J), "no top-level `on:`"), + ("an empty file", 2, one(""), "empty file"), + ("branches and branches-ignore together", 2, + one("on:\n push:\n branches: [main]\n branches-ignore: [dev]\n" + J), "both"), + ("a `[` with no `]`", 2, one("on:\n push:\n branches: ['[main']\n" + J), "no `]`"), +] + + +def run_case(files: list[tuple[str, str]], make_dir: bool = True) -> tuple[int, str]: + with tempfile.TemporaryDirectory() as d: + root = pathlib.Path(d) + wf = root / ".github" / "workflows" + if make_dir: + wf.mkdir(parents=True) + for name, text in files: + (wf / name).write_text(text) + ccg.ROOT, ccg.WORKFLOWS = root, wf + out = io.StringIO() + with contextlib.redirect_stdout(out): + rc = ccg.main() + return rc, out.getvalue() + + +def check(label: str, want: int, rc: int, out: str, needle: str) -> bool: + good = rc == want and needle in out + if good: + print(f"ok {label} (rc {rc})") + else: + print(f"BAD {label}: want rc {want} and {needle!r}, got rc {rc}") + print(" " + out.strip().replace("\n", "\n ")) + return good + + +def main() -> int: + bad = 0 + for name, want, files, needle in CASES: + rc, out = run_case(files) + passed = check(name, want, rc, out, needle) + if not passed: + bad += 1 + + extra = [ + ("no workflows directory = cannot tell", 2, run_case([], make_dir=False), "no workflows"), + ("an empty workflows directory = cannot tell", 2, run_case([]), "no workflows"), + ] + # Without PyYAML nothing is read, and nothing read is not clean. + saved = sys.modules.get("yaml") + sys.modules["yaml"] = None # `import yaml` now raises ImportError + try: + extra.append(("no PyYAML = cannot tell", 2, run_case(one("on: push\n" + J)), "PyYAML")) + finally: + if saved is None: + del sys.modules["yaml"] + else: + sys.modules["yaml"] = saved + for label, want, (rc, out), needle in extra: + passed = check(label, want, rc, out, needle) + if not passed: + bad += 1 + + total = len(CASES) + len(extra) + print(f"\n{total - bad} of {total} cases as expected") + return 1 if bad else 0 + + +if __name__ == "__main__": + sys.exit(main()) From a4d4508d22abf3862ffdb870a65bb103d4c95a66 Mon Sep 17 00:00:00 2001 From: Dmitrii Vasilev Date: Mon, 5 Oct 2026 03:58:08 +0700 Subject: [PATCH 3/3] ci(concurrency): stop the last two push runs from cancelling a dispatch bench007-probe-ax7203.yml and trinity-identity-gate.yml kept their own groups, - with cancel-in-progress: true. A push to main shared that group with a dispatch on main and cancelled it. For the BENCH-007 probe that is a bitstream build of up to four hours (timeout-minutes: 240). Neither commit that set those groups (9947512ff, b71a1442d) gives a reason, and #798 does not mention one. Both now carry the block the other 103 push workflows have. With that, DISPATCH_CANCELS moves from a note to a failure in the gate, so the count stays at zero: before (066bfa285 tree): NOTE DISPATCH_CANCELS 2, rc 0 new gate on that tree: FAIL DISPATCH_CANCELS 2, rc 1 new gate on this tree: 105 of 105 grouped, rc 0 planted cases: 59 of 59 (two cases flipped to rc 1, two added) This commit stands alone. Reverting it restores both old groups and the note. Refs #869 Co-Authored-By: Claude Opus 5.5 --- .github/workflows/bench007-probe-ax7203.yml | 8 +++++-- .github/workflows/trinity-identity-gate.yml | 8 +++++-- tools/check_concurrency_groups.py | 24 +++++++++------------ tools/test_check_concurrency_groups.py | 10 +++++++-- 4 files changed, 30 insertions(+), 20 deletions(-) diff --git a/.github/workflows/bench007-probe-ax7203.yml b/.github/workflows/bench007-probe-ax7203.yml index ef2194c0be..665432ab5f 100644 --- a/.github/workflows/bench007-probe-ax7203.yml +++ b/.github/workflows/bench007-probe-ax7203.yml @@ -33,9 +33,13 @@ on: permissions: contents: read +# A newer push replaces this file's pending run instead of queueing behind it; +# a dispatch is never cancelled (gHashTag/trinity-fpga#869). The old group, +# bench007-probe- with cancel-in-progress, let a push to main kill a +# dispatched bitstream build up to four hours in. concurrency: - group: bench007-probe-${{ github.ref }} - cancel-in-progress: true + group: ${{ github.workflow_ref }}-${{ github.event_name == 'workflow_dispatch' && github.run_id || github.event_name }} + cancel-in-progress: ${{ github.event_name == 'pull_request' }} jobs: bitstream: diff --git a/.github/workflows/trinity-identity-gate.yml b/.github/workflows/trinity-identity-gate.yml index 8606444620..ae9c3a08c4 100644 --- a/.github/workflows/trinity-identity-gate.yml +++ b/.github/workflows/trinity-identity-gate.yml @@ -15,9 +15,13 @@ on: permissions: contents: read +# A newer push or PR commit replaces this file's pending run instead of +# queueing behind it; a dispatch is never cancelled (gHashTag/trinity-fpga#869). +# The old group, trinity-identity- with cancel-in-progress, let a push to +# main cancel a dispatched or nightly run on main. concurrency: - group: trinity-identity-${{ github.ref }} - cancel-in-progress: true + group: ${{ github.workflow_ref }}-${{ github.event_name == 'workflow_dispatch' && github.run_id || github.event_name }} + cancel-in-progress: ${{ github.event_name == 'pull_request' }} jobs: identity-witness: diff --git a/tools/check_concurrency_groups.py b/tools/check_concurrency_groups.py index ff77ae3f82..b36dc506b6 100644 --- a/tools/check_concurrency_groups.py +++ b/tools/check_concurrency_groups.py @@ -9,7 +9,7 @@ the same time (#869). A workflow passes when it never runs on a push to main, or does and has a -top-level `concurrency:` with a group. Three things fail: +top-level `concurrency:` with a group. Four things fail: - UNGROUPED: it runs on a push to main and has no top-level group. A job's own `concurrency:` does not count -- the run still starts and queues; only @@ -25,11 +25,12 @@ (conformance-selftest.yml and conformance-golden-selftests.yml), so a group keyed on `github.workflow` would make one file's push replace the other's pending run. Every file with a group is compared, not only push ones. - -Noted, not failed: a workflow that can be dispatched by hand and whose group -has no `github.run_id` (DISPATCH_CANCELS). A newer push then replaces its -pending dispatch, or cancels a running one under cancel-in-progress. Two -workflows here are built that way on purpose; the choice is the owner's. + - DISPATCH_CANCELS: it runs on a push to main, can be dispatched by hand, + and its group has no `github.run_id`. A newer push then replaces its + pending dispatch, or cancels a running one under cancel-in-progress. This + was a note until the last two such files, bench007-probe-ax7203.yml (a + bitstream build of up to four hours) and trinity-identity-gate.yml, took + the #870 group; neither commit that set their old groups gave a reason. "Runs on a push to main" follows GitHub's filters: `branches:` patterns read in order, the last match deciding and `!` excluding (`*` stops at `/`, `**` @@ -320,13 +321,13 @@ def main() -> int: on_main = [r for r in rows if r["on_main"]] covered = [r for r in on_main if r["class"] in ("GROUPED", "DISPATCH_CANCELS", "REUSABLE")] - bad = [r for r in rows if r["class"] in ("UNGROUPED", "BAD_REUSABLE", "SHARED_KEY")] - noted = [r for r in rows if r["class"] == "DISPATCH_CANCELS"] + failing = ("UNGROUPED", "BAD_REUSABLE", "SHARED_KEY", "DISPATCH_CANCELS") + bad = [r for r in rows if r["class"] in failing] print(f"workflows read: {len(rows)} of {len(files)}; on a push to main: {len(on_main)}, " f"with a group of their own (or called): {len(covered)}") rc = 0 - for cls in ("UNGROUPED", "BAD_REUSABLE", "SHARED_KEY"): + for cls in failing: hits = [r for r in bad if r["class"] == cls] found = bool(hits) if found: @@ -341,11 +342,6 @@ def main() -> int: " group: ${{ github.workflow_ref }}-${{ github.event_name == 'workflow_dispatch'" " && github.run_id || github.event_name }}\n" " cancel-in-progress: ${{ github.event_name == 'pull_request' }}") - has_notes = bool(noted) - if has_notes: - print(f"\nNOTE DISPATCH_CANCELS (not a failure): {len(noted)} workflow(s)") - for r in noted: - print(f" {r['file']}: {r['why']}") has_unread = bool(unread) if has_unread: print(f"\nCANNOT TELL: {len(unread)} workflow(s) not read:") diff --git a/tools/test_check_concurrency_groups.py b/tools/test_check_concurrency_groups.py index b458851ddc..82bf2dc787 100644 --- a/tools/test_check_concurrency_groups.py +++ b/tools/test_check_concurrency_groups.py @@ -45,13 +45,19 @@ def one(text: str) -> list[tuple[str, str]]: CASES: list[tuple[str, int, list[tuple[str, str]], str]] = [ ("push to main, no group", 1, one("on:\n push:\n branches: [main]\n" + J), "UNGROUPED"), ("push and dispatch, the #870 group", 0, one("on:\n push:\n workflow_dispatch:\n" + G + J), "OK"), - ("a group without run_id under dispatch is a note, not a failure", 0, + ("a group without run_id under dispatch fails", 1, one("on:\n workflow_dispatch: {}\n push:\n branches: [main, master]\n" "concurrency:\n group: id-${{ github.ref }}\n cancel-in-progress: true\n" + J), "push cancels a running dispatch"), - ("... and without cancel-in-progress it replaces a pending one", 0, + ("... and without cancel-in-progress it replaces a pending one", 1, one("on:\n workflow_dispatch:\n push:\nconcurrency:\n group: id-${{ github.ref }}\n" + J), "replaces a pending dispatch"), + ("... and the failure names the class", 1, + one("on:\n workflow_dispatch:\n push:\nconcurrency:\n group: id-${{ github.ref }}\n" + J), + "FAIL DISPATCH_CANCELS"), + ("the same group without dispatch is fine: no hand run to lose", 0, + one("on:\n push:\nconcurrency:\n group: id-${{ github.ref }}\n cancel-in-progress: true\n" + J), + "OK"), ("dispatch only", 0, one("on:\n workflow_dispatch:\n" + J), "OK"), ("pull_request only", 0, one("on: pull_request\n" + J), "OK"), ("push to another branch", 0, one("on:\n push:\n branches:\n - dev\n" + J), "OK"),