mirror of
https://github.com/OPCFoundation/UA-.NETStandard.git
synced 2026-10-29 15:11:53 +08:00
Let pushes to protected branches finish their required checks (#4594)
# Description
Makes every push to `master` and `release/*` report both required
checks, so a release branch can be cut from `master`.
Two things stopped `master` commits from getting a green `images
summary`, which the `Release` ruleset requires, including when a branch
is created:
1. **Cancellation.** `CI` (`buildandtest.yml`) and `Images CI`
(`docker-image.yml`) used one concurrency group per ref with
`cancel-in-progress: true`. Each merge to `master` cancels the
still-running checks of the commit before it. A queued run in the same
group is replaced as well ("Canceling since a higher priority waiting
request … exists"). `Images CI` takes about 100 minutes, and `master`
currently gets a merge about every 90 minutes:
| Commit | `build-and-test summary` | `images summary` |
| --- | --- | --- |
| `87c35b84b` (#4572) | success | failure: `pumpserver` / `refserver`
cancelled after ~100 min by the #4573 push |
| `20c2d77d5` (#4573) | success | failure: `refserver` / `mcpserver` /
`pumpserver` cancelled by the #4577 push |
The ruleset has no bypass actors, so `git push origin
20c2d77d5:refs/heads/release/2.0` is rejected with `Required status
check "images summary" is failing`.
2. **Push path filter.** `Images CI`'s push trigger had `paths-ignore:
docs/**, tests/**`. A documentation- or test-only push to `master` or
`release/*` never started the workflow, so it never reported `images
summary` and could never be a cut point.
## Changes
- **Concurrency (both workflows):** pushes to protected lines (`master`,
`main`, `master378`, `release/*`) get one concurrency group per commit
(`github.sha`) and never cancel, so every commit runs its checks to
completion. Pull requests and other branches are unchanged: they group
by PR number or ref and cancel superseded runs.
- The per-commit group is needed as well as `cancel-in-progress: false`.
With a shared group GitHub still replaces a *queued* run.
- **`Images CI` push trigger:** the `paths-ignore` is gone. The
`discover` job now decides relevance for pushes the same way it already
did for pull requests.
- It diffs `github.event.before..HEAD` and skips the image builds for
docs/tests-only changes, while `images summary` still reports.
- A new branch (all-zero `before`) or a `before` commit the clone
doesn't have builds everything, and a failed diff still fails the gate.
- `main` and `master378` are not added to the push trigger: `main`
doesn't exist, and `master378` runs its own copy of this workflow.
## Verification
- The `discover` script was extracted and run locally for push events:
| Push | `relevant_changes` |
| --- | --- |
| docs-only | `false` (images skipped, summary reports) |
| workflow change | `true` |
| new branch | `true` |
| unknown `before` | `true` |
- The CI-related `Opc.Ua.Tools.Tests` (`~Ci`, 53 tests, including the
fail-closed diff pin) pass on net10.0.
## Cost
Overlapping merges to `master` now run CI and image builds in parallel
instead of cancelling each other, which uses more runner minutes on busy
days. Image pushes from overlapping `master` runs can finish out of
order, so `latest-master` reflects whichever finishes last. Version tags
are per build and unaffected.
## Related Issues
- Blocks cutting `release/2.0` for `2.0.0-preview.6` (see #4574).
## Checklist
- [ ] I have signed the
[CLA](https://opcfoundation.org/license/cla/ContributorLicenseAgreementv1.0.pdf)
and read the
[CONTRIBUTING](https://github.com/OPCFoundation/UA-.NETStandard/blob/master/CONTRIBUTING.md)
doc.
- [ ] I have added tests that prove my fix is effective or that my
feature works and increased code coverage.
- [x] I have added all necessary documentation.
- [ ] I have verified that my changes do not introduce (new) build or
analyzer warnings.
- [ ] I ran **all** tests locally using the **UA.slnx** solution against
at least .net **framework** and .net **10**, and all passed.
- [ ] I fixed **all** failing and flaky tests in the CI pipelines and
**all** CodeQL warnings.
- [ ] I have addressed **all** PR feedback received.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
01d6b5d26c
commit
034d9934d1
@@ -54,9 +54,16 @@ env:
|
||||
# pull requests from forks.
|
||||
ENABLE_CODECOV: 'true'
|
||||
|
||||
# Pull requests and feature branches cancel a superseded run: only the newest
|
||||
# commit's result matters there. A push to a protected line (master, main,
|
||||
# master378, release/*) never cancels, and each commit gets its own group,
|
||||
# because a queued run in a shared group is replaced as well as a running one
|
||||
# being cancelled. Every protected commit therefore finishes its required
|
||||
# checks ('build-and-test summary', 'images summary'), which the Release
|
||||
# ruleset demands before a release/<major>.<minor> branch can be cut from it.
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
|
||||
cancel-in-progress: true
|
||||
group: ${{ github.workflow }}-${{ github.event.pull_request.number || ((github.ref == 'refs/heads/master' || github.ref == 'refs/heads/main' || github.ref == 'refs/heads/master378' || startsWith(github.ref, 'refs/heads/release/')) && github.sha) || github.ref }}
|
||||
cancel-in-progress: ${{ github.event_name == 'pull_request' || !(github.ref == 'refs/heads/master' || github.ref == 'refs/heads/main' || github.ref == 'refs/heads/master378' || startsWith(github.ref, 'refs/heads/release/')) }}
|
||||
|
||||
jobs:
|
||||
discover:
|
||||
|
||||
@@ -3,9 +3,10 @@ name: Images CI
|
||||
on:
|
||||
push:
|
||||
branches: [ master, release/*, docker* ]
|
||||
paths-ignore:
|
||||
- 'docs/**'
|
||||
- 'tests/**'
|
||||
# No paths-ignore here either: every push to master or release/* must
|
||||
# report `images summary`, which the Release ruleset requires before a
|
||||
# release branch can be cut from that commit. The discover job skips the
|
||||
# image builds for documentation- or test-only pushes instead.
|
||||
pull_request:
|
||||
branches: [ master, main, master378, develop/*, release/* ]
|
||||
# NOTE: deliberately no `paths-ignore` filter, and the same branch list as
|
||||
@@ -21,9 +22,16 @@ on:
|
||||
# image security update that changes nothing in this repository.
|
||||
workflow_dispatch:
|
||||
|
||||
# Pull requests and feature branches cancel a superseded run: only the newest
|
||||
# commit's result matters there. A push to a protected line (master, main,
|
||||
# master378, release/*) never cancels, and each commit gets its own group,
|
||||
# because a queued run in a shared group is replaced as well as a running one
|
||||
# being cancelled. Every protected commit therefore finishes its required
|
||||
# checks ('build-and-test summary', 'images summary'), which the Release
|
||||
# ruleset demands before a release/<major>.<minor> branch can be cut from it.
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
|
||||
cancel-in-progress: true
|
||||
group: ${{ github.workflow }}-${{ github.event.pull_request.number || ((github.ref == 'refs/heads/master' || github.ref == 'refs/heads/main' || github.ref == 'refs/heads/master378' || startsWith(github.ref, 'refs/heads/release/')) && github.sha) || github.ref }}
|
||||
cancel-in-progress: ${{ github.event_name == 'pull_request' || !(github.ref == 'refs/heads/master' || github.ref == 'refs/heads/main' || github.ref == 'refs/heads/master378' || startsWith(github.ref, 'refs/heads/release/')) }}
|
||||
|
||||
env:
|
||||
REGISTRY: ghcr.io
|
||||
@@ -49,22 +57,38 @@ jobs:
|
||||
env:
|
||||
EVENT_NAME: ${{ github.event_name }}
|
||||
BASE_SHA: ${{ github.event.pull_request.base.sha }}
|
||||
BEFORE_SHA: ${{ github.event.before }}
|
||||
run: |
|
||||
# Reproduces the `paths-ignore` allow-list that used to sit on the
|
||||
# pull_request trigger: an image is rebuilt for any change that is not
|
||||
# purely documentation or test code. Filtering the whole workflow out
|
||||
# would stop `images summary` from ever reporting, which would block
|
||||
# every documentation-only pull request once it becomes required.
|
||||
# Reproduces the `paths-ignore` allow-list that used to sit on both
|
||||
# triggers: an image is rebuilt for any change that is not purely
|
||||
# documentation or test code. Filtering the whole workflow out would
|
||||
# stop `images summary` from ever reporting, which would block every
|
||||
# documentation-only pull request, and every documentation-only push
|
||||
# as a release-branch cut point, since the check is required.
|
||||
$relevantChanges = $true
|
||||
$range = $null
|
||||
if ($env:EVENT_NAME -eq 'pull_request' -and -not [string]::IsNullOrWhiteSpace($env:BASE_SHA)) {
|
||||
# Triple-dot: diff from the merge base, not from the current base
|
||||
# branch head, so a change that landed on the base branch after
|
||||
# this one was cut is not misread as an image-relevant change.
|
||||
$changed = @(& git diff --name-only "$($env:BASE_SHA)...HEAD")
|
||||
$range = "$($env:BASE_SHA)...HEAD"
|
||||
}
|
||||
elseif ($env:EVENT_NAME -eq 'push' -and $env:BEFORE_SHA -match '^[0-9a-f]{40}$' -and
|
||||
$env:BEFORE_SHA -notmatch '^0+$') {
|
||||
# A new branch has an all-zero 'before', and a force push can name
|
||||
# a commit this clone does not have: build everything in both cases
|
||||
# rather than guess what changed.
|
||||
& git cat-file -e "$($env:BEFORE_SHA)^{commit}" 2>$null
|
||||
if ($LASTEXITCODE -eq 0) {
|
||||
$range = "$($env:BEFORE_SHA)..HEAD"
|
||||
}
|
||||
}
|
||||
if ($range) {
|
||||
$changed = @(& git diff --name-only $range)
|
||||
if ($LASTEXITCODE -ne 0) {
|
||||
throw (
|
||||
"Could not diff pull-request base '$($env:BASE_SHA)' against HEAD " +
|
||||
"(git exit code $LASTEXITCODE). Refusing to skip required image builds.")
|
||||
"Could not diff '$range' (git exit code $LASTEXITCODE). " +
|
||||
'Refusing to skip required image builds.')
|
||||
}
|
||||
$relevantChanges = $false
|
||||
foreach ($file in $changed) {
|
||||
|
||||
Reference in New Issue
Block a user