Skip to content

CI: run Pack alongside the tests instead of after them #409

Closed

Description

@CaffeinatedCoder

Problem

main and release/** require four checks on an up-to-date branch (strict). In the 10.2.0 burst on 2026-10-25, eight green PRs had to merge one at a time. Each merge put the next PR behind, so each one cost another full run.

The run is longer than it needs to be. Pack has needs: [ test, integration ], so it starts only once the slowest integration leg and the gate are done. In the last three runs it started at +139 s to +181 s and the wall clock was 261 s to 266 s:

Run Test Slowest integration leg Gate ends Pack starts → ends Wall
40181175011 (PR, 10.x) 110 s 137 s +142 s +144 s → +266 s 266 s
40181139920 (PR, main) 107 s 151 s +156 s +158 s → +263 s 263 s
40181096454 (push, main) 121 s 173 s +179 s +181 s → +261 s 261 s

The gate buys nothing. Pack shares no artifact with Test or Integration. It builds the solution itself and runs its own checks: the README in each package, consumer-smoke-test.sh dist, the SBOMs. A failing test fails the PR through its own required check either way. At release time, release.yml's Verify and pack job builds, runs the whole suite with CI=true, packs, and runs the smoke test against dist before Publish can start. A tag never publishes from the Pack artifact of dotnet.yml.

Change

  • Remove needs: [ test, integration ] from pack, so it runs alongside the other jobs. It still builds, packs, verifies the READMEs, runs ./test/consumer-smoke-test.sh dist and generates the SBOMs.
  • The workflow comments say why the gate is not needed.

Expected wall clock: the slowest integration leg plus the gate, about 155 s to 180 s instead of 261 s to 266 s.

Not done: a docs-only fast path

The idea: when a PR changes only docs/** and *.md, the integration legs and Consumer smoke test still report success but skip their steps. Test always runs fully, because OracleCatalogueTests, DocumentedSampleTests and the changelog conventions read the docs.

The mechanics would work. The selection needs no third-party action: on a pull_request run the checkout is the merge commit, so git diff --name-only HEAD^1 HEAD with fetch-depth: 2 lists exactly what the PR changes. Step-level conditions leave every leg success, so the Integration (live PostgreSQL) gate stays correct.

I didn't build it, for two reasons:

  1. It would weaken a required check. docs/ is not inert for the integration lane. FloatCheckConstraintTests reads the CHECK constraints from the code block in docs/efcore.md and runs them against PostgreSQL 16 and 18 (A server-side predicate over a stored NaN or infinite float answers in PostgreSQL's order; only loading throws #232). A PR that broke that SQL would merge green, and only the push run on main would catch it. Integration tests read docs/efcore.md and candidates.json as declared inputs, not through the repository root #344 describes exactly this case: the file is read by walking up to the repository root, so it is not an MSBuild input, and no path rule can tell that the lane depends on it. A carve-out for docs/efcore.md would be a hand-kept list that silently goes stale the next time an integration test reads a doc.
  2. It saves almost nothing once Pack is ungated. Pack must still run on a docs-only PR: package READMEs are *.md and go into the packages, and Pack runs the same smoke test in its dist mode. So a docs-only run would still take as long as Test or Pack, whichever is slower (about 105 s to 120 s). Integration is about 155 s to 180 s. The fast path would save about 40 s to 60 s on docs-only PRs. Skipping Consumer smoke test alone saves nothing, because Pack is longer.

The right version of this is #345: a lane map derived from MSBuild, behind the single gate of #341, with #344 making docs/efcore.md a declared input of the integration projects first. Until #344 lands, no docs rule is safe for the integration lane.

Recommendation: release/** strict up-to-date

The ruleset is the maintainer's call; nothing here changes it. With this change one serialized merge costs about 3 minutes instead of 4.5. That helps, but eight PRs are still about 25 minutes of waiting.

Recommended: merge the top of a stack, not each PR. Ports to release/10.x in a burst are usually independent cherry-picks. Stack them (each PR based on the previous one, labelled stacked), let CI run on each, then merge only the top PR into release/10.x. That is one up-to-date run for the batch, and every commit was still tested in a run that contained it. GitHub closes the PRs below as merged when their commits reach the base. The changelog rows conflict at every step of the stack (they always do). They are resolved once, while building the stack, not at every merge.

Alternative: relax strict during a pre-release burst only. Turn off "Require branches to be up to date" on release/** for the burst and turn it back on after the tag. What still guards it: the push run on every merge commit of release/10.x, and #365's release checklist, under which the tag goes only on a commit whose push run is green and release.yml verifies the tag commit again before Publish. The risk is two PRs that pass separately and fail together. That shows up on the push run, after the merge, so it is cheap to find but needs a fix-up PR before the tag. The cost is a setting that has to be turned back on by hand. If it is forgotten, release/10.x stays relaxed.

10.x

release/10.x has its own copy of dotnet.yml, with four integration legs (PostgreSQL 16 and 18 × EF Core floor and newest patch) and the same pack gate. The same change applies there. Is a cherry-pick (-x) to release/10.x wanted? None is opened until it is asked for.

Done when

pack has no needs, the comments say why, and the PR's own run reports its measured wall clock.

Activity

  1. added this to the 11.0.0 milestone on Oct 25, 2026
  2. added
    choreBuild, CI, packaging, refactoring with no behaviour change
    area/releaseVersioning, changelogs, release wiring
    on Oct 25, 2026
  3. CaffeinatedCoder commented on Oct 25, 2026

    @CaffeinatedCoder
    OwnerAuthor

    Measured on PR #410's own run (40181793770, up to date with main, every check green): 166 s wall clock, down from 261 s to 266 s. Pack now finishes at +103 s. The critical path is the PostgreSQL 18 leg (162 s) plus the gate (2 s).

    That matches the estimate above, so a serialized merge on a strict branch now costs about 2.8 minutes instead of 4.4. The release/10.x copy has four integration legs, so its critical path would be the slowest of those four. In run 40181175011 that was 137 s, which puts it at about 140 s to 145 s with the same change.

    Still open: should this be cherry-picked to release/10.x?

  4. CaffeinatedCoder commented on Oct 25, 2026

    @CaffeinatedCoder
    OwnerAuthor

    The 10.x half is PR #412 (cherry-pick -x, milestone 10.2.0). It's green, at 143 s wall clock on its own run (40182915762), down from 266 s. The critical path is the slowest of the four integration legs (138 s) plus the gate. Pack (122 s) finishes just ahead of it. Closes #409 acts only on main, so #410 closes this issue. #412 needs no separate close.

  5. CaffeinatedCoder commented on Oct 25, 2026

    @CaffeinatedCoder
    OwnerAuthor

    Done on both lines: PR #410 merged into main (eb81580), and its cherry-pick, PR #412, into release/10.x (604b7e1, shipping in 10.3.0). Pack now runs alongside the other jobs on both branches.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    area/releaseVersioning, changelogs, release wiringchoreBuild, CI, packaging, refactoring with no behaviour change

    Projects

    No projects

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions