Adopt two-phase CI/CD with PR smoke builds and opt-in publish - #98
Conversation
Decouple merging from publishing (issue #97). By default, PRs run path-gated smoke builds (Docker amd64-only, reduced executable matrix, no push) and merges to main/develop no longer publish; the weekly schedule and manual dispatch are the sole publishers, building and releasing both branches in one run via a branch matrix. Set the PUBLISH_ON_MERGE repository variable to true to opt back into legacy publish-on-every-merge. Thread ref/branch/smoke through every reusable build task so branch- derived config keys off inputs.branch (not github.ref_name), letting a scheduled run version and build develop as well as main. Add per-target enable gates and a paths-filter changes job for fast, modular PR feedback; the required status aggregator now fails if path detection fails so a target-changing PR cannot merge with its smoke build skipped. Branch-suffix build artifacts and branch-scope the Docker registry cache so the two matrix legs do not collide or overwrite each other's cache. Move codegen from weekly to daily, fold unit tests into the PR workflow, and remove the now-redundant test-release-task and publish-periodic-docker-release workflows. Correct the Copilot review runbook: the requestReviews GraphQL mutation now reliably re-requests a review (previously needed a manual UI click), and make the wait-for-maintainer-merge gate explicit.
There was a problem hiding this comment.
Pull request overview
This PR restructures the template’s CI/CD into a two-phase model: pull requests run fast, path-gated smoke builds, while publishing is batched into a scheduled/manual workflow with an opt-in “publish on merge” switch.
Changes:
- Replaces the PR validation workflow with unit tests + path-gated smoke builds of only the affected delivery targets.
- Refactors publishing so
publish-release.ymlbecomes the central publisher (weekly + manual), with push-triggered publishing gated byPUBLISH_ON_MERGE. - Threads
ref/branch/smokethrough reusable build workflows to support a branch matrix (publishingmainanddevelopin one run) and avoid artifact/cache collisions.
Reviewed changes
Copilot reviewed 16 out of 16 changed files in this pull request and generated 9 comments.
Show a summary per file
| File | Description |
|---|---|
| README.md | Updates documentation to describe the new two-phase model and codegen schedule. |
| ProjectTemplate.slnx | Removes deleted workflow files from solution items. |
| AGENTS.md | Adds authoritative “Release Model” section and updates review-loop guidance. |
| .github/workflows/test-release-task.yml | Removes the prior “test release” reusable workflow. |
| .github/workflows/test-pull-request.yml | Implements PR path detection + smoke build orchestration + hardened required-check aggregator. |
| .github/workflows/run-periodic-codegen-pull-request.yml | Changes codegen schedule from weekly to daily and updates rationale comments. |
| .github/workflows/publish-release.yml | Introduces publish planning + branch matrix; gates push publishing via PUBLISH_ON_MERGE; adds weekly schedule; changes concurrency cancellation behavior. |
| .github/workflows/publish-periodic-docker-release.yml | Removes the old periodic Docker-only publisher workflow. |
| .github/workflows/get-version-task.yml | Adds ref input to versioning task to support matrix publishing. |
| .github/workflows/build-release-task.yml | Adds ref/branch/smoke + per-target gates; forwards parameters to leaf build tasks; adjusts GitHub release tagging logic. |
| .github/workflows/build-pypilibrary-task.yml | Adds ref/branch; branches artifact names; switches version logic to use inputs.branch. |
| .github/workflows/build-nugetlibrary-task.yml | Adds ref/branch; branches artifact names; uses inputs.branch for config selection. |
| .github/workflows/build-executable-task.yml | Adds ref/branch/smoke; reduces runtime matrix under smoke; branches artifact names; skips aggregation under smoke. |
| .github/workflows/build-docker-task.yml | Adds ref/branch/smoke; makes smoke amd64-only; introduces branch-scoped registry cache. |
| .github/workflows/build-datebadge-task.yml | Adds branch input and gates badge write by inputs.branch. |
| .github/copilot-instructions.md | Updates the Copilot runbook to document GraphQL requestReviews re-request flow. |
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 16 out of 16 changed files in this pull request and generated 2 comments.
Comments suppressed due to low confidence (1)
.github/workflows/build-docker-task.yml:69
- The Docker Hub login step always runs, which will fail on forked PR smoke builds (secrets are unavailable) even though
pushis false. Gate the login oninputs.pushso PR smoke builds can still compile Docker images anonymously, while publish runs authenticate.
- name: Setup Buildx step
uses: docker/setup-buildx-action@8d2750c68a42422c14e847fe6c8ac0403b4cbd6f # v3.12.0
with:
platforms: ${{ inputs.smoke && 'linux/amd64' || 'linux/amd64,linux/arm64' }}
# Always login to Docker Hub, not just on push, to benefit from
# higher rate limits with a Docker subscription for pulls and cache
- name: Login to Docker Hub step
uses: docker/login-action@c94ce9fb468520275223c153574b00df6fe4bcc9 # v3.7.0
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 16 out of 16 changed files in this pull request and generated 2 comments.
Comments suppressed due to low confidence (1)
.github/workflows/build-docker-task.yml:68
docker/login-actionruns unconditionally, so PR smoke builds (and any non-publishing builds where secrets aren’t available, e.g. fork PRs / Dependabot runs without Docker Hub secrets) can fail before they even reach the build. Sincepush=falsesmoke builds are explicitly meant to work without registry credentials, gate the login step on eitherinputs.pushor the presence of the Docker Hub secrets.
platforms: ${{ inputs.smoke && 'linux/amd64' || 'linux/amd64,linux/arm64' }}
# Always login to Docker Hub, not just on push, to benefit from
# higher rate limits with a Docker subscription for pulls and cache
- name: Login to Docker Hub step
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Release merge of `develop` into `main`. ## Headline change (#98, closes#97) Two-phase CI/CD that decouples merging from publishing across all four delivery targets (Docker, NuGet, PyPI, console executable): - **PRs smoke-test only** — path-gated, reduced builds (Docker `linux/amd64`, trimmed executable matrix), no publish. - **Sole publisher** — `publish-release.yml` (weekly schedule + manual dispatch) builds/publishes **both** branches via a matrix; the `push` trigger publishes only when the **`PUBLISH_ON_MERGE`** repository variable is `true` (opt-in legacy continuous-release). Default is two-phase. - Parameterized chain (`ref`/`branch`/`smoke`, per-target `enable_*` gates), branch-suffixed artifacts, branch-scoped Docker cache, hardened required-status aggregator. - Robustness: skip GitHub release on a duplicate version (no-op weeks), pin the release tag to NBGV `GitCommitId`, global publish concurrency, `cache-to`/login gating reviewed. - CodeGen weekly → daily; `test-release-task.yml` + `publish-periodic-docker-release.yml` removed. - Corrected the Copilot review runbook (reliable `requestReviews` mutation) and made the wait-for-maintainer-merge gate explicit. Also includes routine codegen updates (#93, #95). 🤖 Generated with [Claude Code](https://claude.com/claude-code)
Closes#407. Two robustness fixes to the `github-release` job in `publish-release.yml`, ported from ptr727/ProjectTemplate#98. ## 1. Skip duplicate release on no-op weeks The weekly schedule re-runs even when `main` has no new commits, so NBGV produces the **same `SemVer2`** and `softprops/action-gh-release` is asked to (re)create an existing release/tag — churning the release every week and risking failures re-uploading existing assets. Added a `Check for existing release` step (`gh release view "$SemVer2"`) that gates the release-creation step, so a no-op week is a true no-op. ## 2. Pin release tag to the built commit, not a moving ref `target_commitish: main` is a **moving ref** — a commit landing on `main` mid-run could tag the release on a newer commit than the one built. Pinned it to NBGV's `GitCommitId` (the exact commit the version was computed from; `get-version` runs with `ref: main`). Added `GitCommitId` as a `get-version-task.yml` output. Matches the existing file conventions (no new action pins introduced). 🤖 Generated with [Claude Code](https://claude.com/claude-code)
Release merge of `develop` into `main`. ## Changes - **Release-job robustness** (#408, closes#407): skip GitHub release creation on no-op weekly runs (when the `SemVer2` tag already exists), while still allowing `workflow_dispatch` to repair a release; and pin `target_commitish` + the release-job checkout to NBGV `GitCommitId` (the exact built commit) instead of the moving `main` ref. Ported from ptr727/ProjectTemplate#98. - Dependabot: bump `dorny/paths-filter` 3 → 4 (#409). 🤖 Generated with [Claude Code](https://claude.com/claude-code)
Closes#97.
What
Decouple merging from publishing across all four delivery targets (Docker, NuGet, PyPI, console executable).
linux/amd64only, reduced executable matrix, no push). Merges tomain/developno longer publish.publish-release.yml(weekly schedule + manual dispatch) builds and publishes both branches in one run via a branch matrix; itspushtrigger publishes only when thePUBLISH_ON_MERGErepository variable istrue(opt-in legacy continuous-release).ref/branch/smokeare threaded through every reusable task; branch-derived config keys offinputs.branch, nevergithub.ref_name, so a scheduled run can version/builddevelopas well asmain.get-version-task.ymlgained arefinput.buildcache-main/buildcache-develop, reads both, writes own) so the two legs never collide or overwrite each other's cache.test-release-task.ymlandpublish-periodic-docker-release.ymlremoved.enable_*gates so a derived project can drop a target with minimal edits (checklist in AGENTS.md).Copilot runbook
Corrects
.github/copilot-instructions.md: therequestReviewsGraphQL mutation now reliably re-requests a review (previously documented as non-working / manual-UI-only), and the wait-for-maintainer-merge gate is now explicit in AGENTS.md.Notes
.NETNuGet restore caching is intentionally left off (low overhead, no lock files under Central Package Management) — documented inline.🤖 Generated with Claude Code