Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 16 additions & 6 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,13 @@ on:
branches: [main, dev]
release:
types: [published]
# Merge-queue runs. Inert until a queue ruleset is enabled on a branch (a repo setting),
# but the required checks must handle the event BEFORE that click, or every queued PR
# stalls on checks that never report. dorny/paths-filter v4.0.1+ resolves merge_group
# diffs from the payload's base_sha/head_sha whenever the base input is empty — exactly
# what the filter steps pass for non-push events — so path classification works
# unchanged in a queue run.
merge_group:

permissions:
contents: write
Expand All @@ -16,10 +23,11 @@ permissions:
# A re-push to a PR cancels that PR's superseded in-flight run: finishing a build of code
# that is no longer the PR's head helps nobody, and the shared Windows runner pool is what
# serializes everyone's CI (#1697 sat queued behind two dev builds), so a cancelled stale
# run is reclaimed capacity for whatever is queued. Push and release runs deliberately get
# a UNIQUE group per run (run_id): dev/main integration builds and release builds are never
# queued behind, replaced by, or cancelled through this mechanism — every push commit keeps
# its own reported check result, which release cuts and merge gating read.
# run is reclaimed capacity for whatever is queued. Push, release, and merge-queue runs
# deliberately get a UNIQUE group per run (run_id): integration builds, release builds, and
# queue validations are never queued behind, replaced by, or cancelled through this
# mechanism — every push commit keeps its own reported check result, which release cuts and
# merge gating read.
concurrency:
group: ${{ github.event_name == 'pull_request' && format('build-pr-{0}', github.event.pull_request.number) || format('build-run-{0}', github.run_id) }}
cancel-in-progress: ${{ github.event_name == 'pull_request' }}
Expand Down Expand Up @@ -137,6 +145,8 @@ jobs:
# they restore unconditionally even for a docs-only commit. Cheap
# insurance: this only forces the restore back on, it does not force
# the per-product build/test steps, which stay path-gated as before.
# merge_group — a queue run is the LAST validation before its result lands on dev,
# so it takes the same always-restore path as a push.
# areas — belt and suspenders: even when the counts say docs-only, any lit
# area filter vetoes the fast path, because an area=true with restore
# skipped would run `dotnet build --no-restore` against nothing. The
Expand All @@ -162,9 +172,9 @@ jobs:
exit 0
fi

if [ "${{ github.event_name }}" = "push" ]; then
if [ "${{ github.event_name }}" = "push" ] || [ "${{ github.event_name }}" = "merge_group" ]; then
echo "engaged=false" >> "$GITHUB_OUTPUT"
echo "::notice title=Full build::Push to '${{ github.ref_name }}' - branch pushes always restore, even for a docs-only commit."
echo "::notice title=Full build::${{ github.event_name }} on '${{ github.ref_name }}' - integration runs always restore, even for a docs-only change."
exit 0
fi

Expand Down
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

### Changed

- **CI: build.yml handles the merge_group event, so enabling a dev merge queue becomes a safe one-click** ([#1716]) - lands the prerequisite [#1715]'s merge-queue recommendation named: without a `merge_group` trigger, the required `build` and `Darling PostgreSQL tests` checks would never report inside a queue and every queued PR would stall - enabling the ruleset before this wiring would brick dev merges. The trigger is INERT until the queue ruleset is actually enabled (a repo setting, Erik's click, with the suggested settings in #1715's body). Queue runs take the same always-restore path as dev/main pushes - a queue run is the last validation before its result lands on dev - and the per-run concurrency group, so they are never cancelled or replaced. Path classification works unchanged in a queue: dorny/paths-filter v4.0.1+ resolves merge_group diffs from the payload's base_sha/head_sha whenever the `base` input is empty, which is exactly what the filter steps pass for non-push events.
- **CI: the change classification the v4 pin bump silently broke is restored - and this time it is probe-validated** ([#1715]) - a timing baseline over the 30 most recent build.yml runs plus a throwaway probe PR (#1714) turned the planned build-time audit into a regression find. The 2026-07-26 08:02 pin bump moved dorny/paths-filter v3 -> v4, and v4 evaluates every filter pattern as an INDEPENDENT predicate under its default predicate-quantifier 'some' (a filter is true when any changed file matches at least one rule), so a bare `!**/*.md` exclusion stopped being a subtraction and became its own rule: "any file that is not markdown". Every area filter carrying that line went true for ANY non-markdown change anywhere in the repo - a single root .gitignore edit built and tested all four products (probe run 30219202642), darling-pg ran the full TimescaleDB suite on every PR since the bump including md-only ones (run 30218459544 matched CHANGELOG.md against the darling filter), and the [#1712] docs fast path shipped unable to engage, because every file matches '**' so its code: gate was never false - its measured md-only 1m43s runs were real, but they were the area filters at work (markdown matches no include), not the fast path. The regression was invisible by construction: the bump's own PR touched build.yml, so root=true forced a full build that looks identical to a correct run, and so does every over-built run after it. Fixed keeping v4 (v3 is on the deprecated-runtime track): area filters carry the markdown carve-out INSIDE each include as an extglob (`Darling/**/!(*.md)`) where quantifier semantics cannot detach it; the uninvertible code: filter is replaced by an `all:` counter with docs-only decided by all_count == docs_count; the classify step additionally refuses to engage while any area filter is lit, so the two classifications can never disagree into a `dotnet build --no-restore` with no restore behind it; and check-version-bump.yml, which had the identical '**'-plus-exclusions shape and an equally dead md-only skip, takes the same counter fix. Validated with a per-file truth table on the probe PR (run 30219765613: a Darling .txt probe, a Darling .md probe, .gitignore and the workflow file in one diff - each filter's matched-file list recorded in the PR body). **Also in this pass:** a PR re-push now cancels that PR's superseded in-flight build.yml/sql-validation.yml runs, while push and release runs keep unique per-run concurrency groups - never queued behind or cancelled by anything, every dev/main commit keeps its own check result; the [#1712] allowlist's flagged judgment calls are ratified (CITATION.cff, Screenshots/) but its directory-wide grants become extension-explicit (`docs/**/*.{md,svg,png,jpg,jpeg,gif}`) so a .sql dropped into docs/ tomorrow defaults to code; the scheduled nightly re-dispatches itself onto the dev ref instead of doing real work from main's copy of the workflow file - scheduled workflows execute the DEFAULT branch's copy against dev's checked-out tree, which is exactly how the 2026-07-26 06:00 nightly failed (run 30194606068: main's stale copy read Dashboard/Dashboard.csproj, moved to deprecated/ by #1612 - the #1550 trap again) - so after a ONE-TIME sync of nightly.yml to main (command in the PR body; the schedule stays red each morning until it happens) nightly logic changes take effect the night they merge to dev, with manual dispatches still always building and the artifact job still pinned to dev; and every job that never carries the release/signing path gets a timeout-minutes ceiling at ~3x its worst cold path (darling-pg 30, nightly build 90 / pg 60 / check 10, sql-validation 30 per leg, claude-review 30) so hung-not-slow failures stop holding a shared-pool runner for the 6h default - build.yml's build job stays unbounded on purpose, because the release path waits on SignPath's manual approval gate. **Measured and deliberately not done** (numbers in the PR body): per-area restore splitting (warm restore is 19-33s; four condition-mirrored restore steps buy seconds at the price of the drift risk [#1701] just retired) and cross-job test splitting (Run Lite tests ~2m25s dominates the full build, but a second Windows job costs ~2m45s of checkout/setup/restore/build before its first test - a net loss on a shared serialized pool). Merge queue remains a recommendation with exact settings in the PR body: it is a repo setting, and build.yml needs a merge_group trigger first or queued PRs stall on never-reporting required checks.
- **CI: a documentation change stops paying for a .NET restore** ([#1712]) - non-executable changes now skip .NET setup, restore and versioning in the required `build` job, while the job still RUNS so the check reports and cannot block a merge. The gate is an explicit **allowlist** of non-executable content (`**/*.md`, `LICENSE`, `CITATION.cff`, `.gitignore`, `.gitattributes`, `docs/**`, `Screenshots/**`) rather than a subtraction, so an unfamiliar new file type defaults to being treated as code - the safe direction to be wrong in. `*.sql`, `*.yml`, `*.csproj`/`*.props`/`packages.lock.json` are deliberately excluded from the allowlist because some job compiles or consumes each of them, and `*.cs` is excluded however comment-only a change looks, since the compiler is what proves it still builds. **Two guards against under-building**: the fast path never engages on a `release` event or on a push to `dev`/`main` (those restore unconditionally - it only forces the restore back on, per-product build/test steps stay path-gated exactly as before), and both jobs now emit a `::notice::` naming WHY they took the path they took plus the file list classified as documentation, because a job that reports success having quietly run nothing is indistinguishable from one that tested everything. Measured against the real gap: markdown-only changes were ALREADY skipping every heavy step (PR #1707, pure `.md`, ran `build` in 1m43s), so what this actually fixes is non-markdown documentation - a `LICENSE`, `.gitignore` or screenshot edit previously matched the catch-all and paid a six-project locked-mode restore for nothing. The remaining ~1m45s floor is `actions/checkout` on a Windows runner, not work this gate can remove.
- **CI: the Lite fast / analysis-heavy test split collapses back into one step** ([#1701]) - the split existed because the seven analysis classes rebuilt the full DuckDB schema inside every test and their subset alone cost ~9 CI minutes, so a narrower lite_analysis path gate let non-analysis Lite changes skip it. After [#1693]/[#1694]/[#1698] that subset runs in ~66s on the same runner, so the split stopped earning its second test-host spin-up - and its hand-maintained class-name filters were a standing drift risk (a renamed analysis class would silently fall out of the heavy filter into the fast bucket). One Run Lite tests step now runs the whole suite behind the lite path gate; the unconsumed lite_analysis filter block is gone. The lite gate is a strict superset of the old lite_analysis gate, so nothing that ran before is skipped now.
Expand Down Expand Up @@ -1699,6 +1700,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
[#1708]: https://github.com/erikdarlingdata/PerformanceMonitor/pull/1708
[#1712]: https://github.com/erikdarlingdata/PerformanceMonitor/pull/1712
[#1715]: https://github.com/erikdarlingdata/PerformanceMonitor/pull/1715
[#1716]: https://github.com/erikdarlingdata/PerformanceMonitor/pull/1716
[#1710]: https://github.com/erikdarlingdata/PerformanceMonitor/pull/1710
[#1690]: https://github.com/erikdarlingdata/PerformanceMonitor/pull/1690
[#1693]: https://github.com/erikdarlingdata/PerformanceMonitor/pull/1693
Expand Down
Loading