|
14 | 14 | # and release.yml, which trigger only on intentional Git tags. |
15 | 15 | # |
16 | 16 | # Triggers: |
17 | | -# - Push to any branch |
| 17 | +# - Push to a long-lived integration branch (master, next_version) only — |
| 18 | +# a feature/topic branch gets checks exclusively through the pull_request |
| 19 | +# event once a PR exists (see the push: trigger below for why) |
18 | 20 | # - Pull request targeting any branch (a PR can be opened against any |
19 | 21 | # integration branch, not just master, as the project matures multiple |
20 | | -# branches at once) |
| 22 | +# branches at once); skipped for a still-draft PR until marked ready |
21 | 23 | # - Manual dispatch (workflow_dispatch) |
22 | 24 | # |
23 | 25 | # Skip-CI: |
|
33 | 35 | --- |
34 | 36 | name: CI |
35 | 37 |
|
36 | | -# Triggers the workflow on push to any branch (tag pushes excluded — handled by |
37 | | -# prerelease.yml and release.yml), pull requests targeting any branch, or manual dispatch |
| 38 | +# Triggers the workflow on push to a long-lived integration branch (tag pushes |
| 39 | +# excluded — handled by prerelease.yml and release.yml), pull requests targeting |
| 40 | +# any branch, or manual dispatch |
38 | 41 | on: |
39 | 42 | push: |
40 | | - branches: |
41 | | - - '**' # All branches; branches: ['**'] scopes push to refs/heads/ only, |
42 | | - # excluding refs/tags/ pushes — see Skip-CI above |
| 43 | + # Deliberately narrow, not '**': a feature/topic branch's own push event used |
| 44 | + # to also trigger this workflow, so a push immediately followed by opening a |
| 45 | + # PR from it fired two runs for the identical commit -- push and pull_request |
| 46 | + # are independent, asynchronously-fired events with no ordering guarantee |
| 47 | + # between them, so no point-in-time check on either side can reliably catch |
| 48 | + # this before the fact (see the concurrency: block below for the backstop |
| 49 | + # that still exists for whatever this doesn't prevent). Scoping push: to only |
| 50 | + # the branches that are never themselves the source of a PR removes the |
| 51 | + # redundant trigger at its source instead: a feature branch now gets CI |
| 52 | + # exactly once, via the PR opened from it. |
| 53 | + branches: [master, next_version] |
43 | 54 | pull_request: |
44 | 55 | # No branch restriction: a PR merging into any branch gets checks. The one |
45 | 56 | # exclusion is gh-pages, which only ever receives generated site output and |
@@ -74,13 +85,24 @@ jobs: |
74 | 85 | # Job: Build MkDocs HTML documentation bundle for gem inclusion |
75 | 86 | generate-docs: |
76 | 87 | name: "Generate Local Docs Bundle for Gem Inclusion" |
| 88 | + # pull_request fires for a draft PR the same as a ready one; github.event.pull_request |
| 89 | + # is only ever populated on that event type, so this has no effect on push or |
| 90 | + # workflow_dispatch runs. Holds off the full matrix's cost until a PR is actually |
| 91 | + # ready for review -- a draft's own incremental commits get this workflow's checks |
| 92 | + # for free the moment it's marked ready, no separate action needed. |
| 93 | + if: github.event_name != 'pull_request' || github.event.pull_request.draft == false |
77 | 94 | uses: ./.github/workflows/_generate-docs.yml |
78 | 95 |
|
79 | 96 |
|
80 | 97 | # Job: Linux test suite |
81 | 98 | tests-linux: |
82 | 99 | name: "Linux Test Suite" |
83 | 100 | needs: generate-docs |
| 101 | + # success() is spelled out explicitly here (not left implicit) because a custom |
| 102 | + # if: replaces GitHub's default success()-on-needs: check entirely rather than |
| 103 | + # being ANDed with it -- every job below with a needs: dependency carries this |
| 104 | + # same explicit success() alongside its own draft-PR guard for that reason. |
| 105 | + if: success() && (github.event_name != 'pull_request' || github.event.pull_request.draft == false) |
84 | 106 | runs-on: ubuntu-latest |
85 | 107 | strategy: |
86 | 108 | fail-fast: false |
@@ -238,6 +260,7 @@ jobs: |
238 | 260 | tests-windows: |
239 | 261 | name: "Windows Test Suite" |
240 | 262 | needs: generate-docs |
| 263 | + if: success() && (github.event_name != 'pull_request' || github.event.pull_request.draft == false) |
241 | 264 | runs-on: windows-latest |
242 | 265 | strategy: |
243 | 266 | fail-fast: false |
@@ -321,6 +344,7 @@ jobs: |
321 | 344 | tests-macos: |
322 | 345 | name: "macOS Test Suite" |
323 | 346 | needs: generate-docs |
| 347 | + if: success() && (github.event_name != 'pull_request' || github.event.pull_request.draft == false) |
324 | 348 | runs-on: macos-latest |
325 | 349 | strategy: |
326 | 350 | fail-fast: false |
@@ -408,6 +432,7 @@ jobs: |
408 | 432 | # a path-based Bundler deployment (deploy_gem) that does not require a gem build. |
409 | 433 | tests-linux-locale: |
410 | 434 | name: "Linux Test Suite (ja_JP.UTF-8 Locale)" |
| 435 | + if: github.event_name != 'pull_request' || github.event.pull_request.draft == false |
411 | 436 | runs-on: ubuntu-latest |
412 | 437 | steps: |
413 | 438 | - uses: actions/cache@v6 |
@@ -479,6 +504,7 @@ jobs: |
479 | 504 | # deployment (deploy_gem) that does not require a gem build. |
480 | 505 | tests-linux-encoding-stress: |
481 | 506 | name: "Linux Encoding Stress Test (C/POSIX)" |
| 507 | + if: github.event_name != 'pull_request' || github.event.pull_request.draft == false |
482 | 508 | runs-on: ubuntu-latest |
483 | 509 | steps: |
484 | 510 | - uses: actions/cache@v6 |
@@ -553,6 +579,7 @@ jobs: |
553 | 579 | - tests-macos |
554 | 580 | - tests-linux-locale |
555 | 581 | - tests-linux-encoding-stress |
| 582 | + if: success() && (github.event_name != 'pull_request' || github.event.pull_request.draft == false) |
556 | 583 | runs-on: ubuntu-latest |
557 | 584 |
|
558 | 585 | steps: |
|
0 commit comments