Re-sync of the two workflow fixes this repo surfaced upstream, now merged in ptr727/ProjectTemplate on develop:
Apply here adapted to this single-target NuGet repo (no pypi/docker/executable jobs). These workflow files are CRLF — preserve line endings. Source of truth for every snippet below is template PRs ptr727/ProjectTemplate#215, #216, and #218 (the #217 delete-gating follow-up).
This is the first downstream re-sync of these fixes; once validated here it goes to the other derived repos.
A. #213 / #221 - version each leg from its checked-out branch, validate at entry
Updated for #221 + #219. The original GITHUB_REF-override approach (template PR ptr727/ProjectTemplate#215) was ineffective - GITHUB_REF is GitHub-reserved and a step env: cannot override it (the runner re-injects the dispatch ref). Use NBGV's own IGNORE_GITHUB_REF instead (template PR ptr727/ProjectTemplate#222). If you already applied the earlier version of this section, see the migration note in the comments below.
A1. get-version-task.yml - make NBGV ignore the CI ref
On the Run Nerdbank.GitVersioning tool step, set NBGV's IGNORE_GITHUB_REF so it versions from the checked-out branch (each leg already checks out its own branch) instead of the dispatch ref:
- name: Run Nerdbank.GitVersioning tool stepid: nbgvuses: dotnet/nbgv@masterenv:
# Version from the checked-out branch, not the CI ref. GITHUB_REF is reserved and a step env can't reliably# override it; IGNORE_GITHUB_REF makes NBGV ignore it and use the checked-out branch. validate-release backstops.IGNORE_GITHUB_REF: "true"
Do not add a branch input to get-version-task.yml or thread branch into its callers - that was the ineffective #215 approach and is unnecessary.
A2. build-release-task.yml - validate-at-entry gate (smoke-skipped)
Add a validate-release job (right after get-version) that the build jobs needs:. It checks branch-versus-version consistency on real publishes and is skipped for smoke builds - a smoke build's detached PR HEAD always versions as prerelease, which would otherwise fail a main-base promotion PR (#219):
validate-release:
name: Validate release version jobneeds: [get-version]runs-on: ubuntu-lateststeps:
- name: Validate branch and version consistency stepenv:
SEMVER2: ${{ needs.get-version.outputs.SemVer2 }}BRANCH: ${{ inputs.branch }}SMOKE: ${{ inputs.smoke }}run: | set -euo pipefail # Smoke builds never publish and always version as prerelease (detached PR HEAD), which would trip the main arm. if [[ "$SMOKE" == "true" ]]; then echo "Smoke build; skipping release version validation." exit 0 fi CORE_AND_PRE="${SEMVER2%%+*}" if [[ "$BRANCH" == "main" ]]; then if [[ "$CORE_AND_PRE" == *-* ]]; then echo "::error::Public (main) release version '$SEMVER2' carries a prerelease suffix; refusing to publish." exit 1 fi elif [[ "$CORE_AND_PRE" != *-* ]]; then echo "::error::Prerelease ($BRANCH) version '$SEMVER2' has no prerelease suffix (NBGV classified it public); refusing to publish." exit 1 fibuild-nugetlibrary job: needs: [get-version] -> needs: [get-version, validate-release]github-release job: add validate-release to its needs:- Remove the old
Verify public release version step from github-release (the entry gate covers it both directions).
B. #214 — surgical consume-then-delete, drop the blanket cleanup
B1. build-release-task.ymlgithub-release job — delete the consumed assets
After the Create GitHub release step, add:
Includes the #217 refinement (template PR ptr727/ProjectTemplate#218): the delete is gated on the sameif: as the Create step (steps.release-exists is the existing "Check for existing release step" already in this job), so a scheduled re-run on an existing tag doesn't delete the freshly built artifacts. If you already applied an earlier un-gated version of this step, just update its if: line to match below.
# Surgical cleanup at the point of consumption: the release-asset-<branch>-* transfer artifacts now have durable# copies on the release, so delete them by exact pattern. Gated to the same condition as the Create step so it only# deletes when a release was actually created/refreshed this run; on a skipped create (existing tag, no new# commits) the fresh artifacts stay for the run, reaped by the retention-days: 1 backstop. Best-effort: a# listing/delete hiccup must not red the job. Deletes every matching id. Needs the caller `actions: write` (B2).
- name: Delete consumed release asset artifacts stepif: ${{ inputs.expect_release_assets && (steps.release-exists.outputs.exists == 'false' || github.event_name == 'workflow_dispatch') }}continue-on-error: trueenv:
GH_TOKEN: ${{ github.token }}run: | set -euo pipefail if ! ids=$(gh api "repos/$GITHUB_REPOSITORY/actions/runs/${{ github.run_id }}/artifacts" --paginate \ --jq ".artifacts[] | select(.name | startswith(\"release-asset-${{ inputs.branch }}-\")) | .id"); then echo "::warning::Could not list run artifacts; retention-days backstop will reap them." ids="" fi for id in $ids; do gh api --method DELETE "repos/$GITHUB_REPOSITORY/actions/artifacts/$id" \ || echo "::warning::Failed to delete artifact $id; retention-days backstop will reap it." doneB2. publish-release.yml
On the publish job (the one that uses: ./.github/workflows/build-release-task.yml), add actions: write next to contents: write so the github-release job can delete:
permissions:
contents: write# actions:write lets the github-release job delete the release-asset-* artifacts it consumes (surgical cleanup).actions: write
Remove the cleanup-artifacts job entirely. (No publish-pypi here — single target — so no other consume-then-delete is needed.)
B3. test-pull-request.yml
- Remove the
cleanup-artifacts job entirely. check-workflow-status does not list it in needs:, so the required merge check is unaffected. Smoke builds upload no release-asset-* (gated on !smoke), so there is nothing left to clean here.
B4. Confirm the backstop
retention-days: 1 must be set on the release-asset-<branch>-nugetlibrary upload in build-nugetlibrary-task.yml (it is the failure-path backstop for B1/B2).
C. #211 — rulesets: nothing to carry here
Branch rulesets are now maintained only in the template as .github/rulesets/{develop,main}.json (template PR ptr727/ProjectTemplate#212); they are not carried/re-synced as a per-repo file. This repo's live rulesets were already verified in sync with the template's committed JSON, so there is nothing to do. To re-confirm, run the drift check from the template's AGENTS.md "Staying in Sync" (from a template checkout, against ptr727/LanguageTags).
Verify
actionlint clean: docker run --rm -v "$PWD":/repo --workdir /repo rhysd/actionlint:latestvalidate-release fails a synthetic clean-X.Y.Z develop version and passes a X.Y.Z-g<sha> one; runs before the build job.- After a real publish: no
release-asset-* artifacts remain (consumed-then-deleted), diagnostics/build-records are retained; a forced mid-run failure leaves its artifact for retention-days: 1 rather than red-failing cleanup. - CRLF preserved on every touched workflow file.
Once this is validated, the same set of changes (adapted per repo's target set) will be re-synced to the other derived repos.
Re-sync of the two workflow fixes this repo surfaced upstream, now merged in
ptr727/ProjectTemplateondevelop:5adbcb9)161d8a9)Apply here adapted to this single-target NuGet repo (no pypi/docker/executable jobs). These workflow files are CRLF — preserve line endings. Source of truth for every snippet below is template PRs ptr727/ProjectTemplate#215, #216, and #218 (the #217 delete-gating follow-up).
This is the first downstream re-sync of these fixes; once validated here it goes to the other derived repos.
A. #213 / #221 - version each leg from its checked-out branch, validate at entry
A1.
get-version-task.yml- make NBGV ignore the CI refOn the
Run Nerdbank.GitVersioning tool step, set NBGV'sIGNORE_GITHUB_REFso it versions from the checked-out branch (each leg already checks out its own branch) instead of the dispatch ref:Do not add a
branchinput toget-version-task.ymlor threadbranchinto its callers - that was the ineffective #215 approach and is unnecessary.A2.
build-release-task.yml- validate-at-entry gate (smoke-skipped)Add a
validate-releasejob (right afterget-version) that the build jobsneeds:. It checks branch-versus-version consistency on real publishes and is skipped for smoke builds - a smoke build's detached PR HEAD always versions as prerelease, which would otherwise fail a main-base promotion PR (#219):build-nugetlibraryjob:needs: [get-version]->needs: [get-version, validate-release]github-releasejob: addvalidate-releaseto itsneeds:Verify public release version stepfromgithub-release(the entry gate covers it both directions).B. #214 — surgical consume-then-delete, drop the blanket cleanup
B1.
build-release-task.ymlgithub-releasejob — delete the consumed assetsAfter the
Create GitHub release step, add:B2.
publish-release.ymlOn the
publishjob (the one thatuses: ./.github/workflows/build-release-task.yml), addactions: writenext tocontents: writeso thegithub-releasejob can delete:Remove the
cleanup-artifactsjob entirely. (Nopublish-pypihere — single target — so no other consume-then-delete is needed.)B3.
test-pull-request.ymlcleanup-artifactsjob entirely.check-workflow-statusdoes not list it inneeds:, so the required merge check is unaffected. Smoke builds upload norelease-asset-*(gated on!smoke), so there is nothing left to clean here.B4. Confirm the backstop
retention-days: 1must be set on therelease-asset-<branch>-nugetlibraryupload inbuild-nugetlibrary-task.yml(it is the failure-path backstop for B1/B2).C. #211 — rulesets: nothing to carry here
Branch rulesets are now maintained only in the template as
.github/rulesets/{develop,main}.json(template PR ptr727/ProjectTemplate#212); they are not carried/re-synced as a per-repo file. This repo's live rulesets were already verified in sync with the template's committed JSON, so there is nothing to do. To re-confirm, run the drift check from the template'sAGENTS.md"Staying in Sync" (from a template checkout, againstptr727/LanguageTags).Verify
actionlintclean:docker run --rm -v "$PWD":/repo --workdir /repo rhysd/actionlint:latestvalidate-releasefails a synthetic clean-X.Y.Zdevelop version and passes aX.Y.Z-g<sha>one; runs before the build job.release-asset-*artifacts remain (consumed-then-deleted), diagnostics/build-records are retained; a forced mid-run failure leaves its artifact forretention-days: 1rather than red-failing cleanup.Once this is validated, the same set of changes (adapted per repo's target set) will be re-synced to the other derived repos.