mirror of
https://github.com/OPCFoundation/UA-.NETStandard.git
synced 2026-10-17 05:11:13 +08:00
Allow release.yml to promote release-line previews to nuget.org (#4574)
# Description Adds a way to publish a numbered release-line preview (e.g. `2.0.0-preview.6`) to nuget.org. Since #4513, `nuget-publish.yml` pushes previews only to GitHub Packages, and `release.yml` (the only nuget.org path) refused every non-stable candidate. That left no way to continue the `2.0.0-preview.N` series on nuget.org (last published there: `2.0.0-preview.5`). ## Changes - **`release.yml`** gains a `channel` input (`stable` | `preview`, default `stable`). - `stable` behaves exactly as before. - `preview` accepts only a public release-line preview, `M.m.p-preview.N` with no commit id, from the matching canonical `release/M.m` branch. `master` / PR builds (`-preview.N.g<sha>`) are rejected. - The candidate manifest's recorded `channel` must equal the dispatched one, so a stable promotion can never publish a preview by accident, or the reverse. - **`package-version-policy.ps1`**: new `Test-PromotablePreviewPackageVersion`, and an `-AllowPreview` switch on `Test-CanonicalReleaseBranchForPackageVersion`. Callers without the switch (the stable gate in `nuget-publish.yml`) behave exactly as before. - **`docs/ReleaseProcess.md`**: new "Preview release" procedure, including how to pick the preview number with `versionHeightOffset`. - **Tests**: predicate cases, the `-AllowPreview` switch, and a pin that `release.yml` defaults to `stable` and relaxes the gate only for `channel=preview`. ## GitHub Packages preview feed is unchanged - `nuget-publish.yml` is not touched. `master` keeps auto-publishing `2.0.0-preview.<height>.g<sha>` to GitHub Packages, and release-branch builds keep pushing their own `<base>-preview.N` there. - The commit-id suffix means `master` versions can never collide with a bare `2.0.0-preview.N`. - For a preview, `release.yml`'s GitHub Packages push is a verified no-op. The candidate's own `nuget-publish.yml` run already pushed those bytes, and the existing identity preflight and post-push check prove they match. ## Getting `2.0.0-preview.6` after merge 1. Cut `release/2.0` from `master` after this merges. `release.yml` executes the policy script from the candidate commit, so that commit must contain this change. 2. On `release/2.0`, commit a `versionHeightOffset` so the build lands on `.6`. Verified locally with nbgv 3.10.94: at the current `master` tip (height 24), one commit setting `-19` yields `2.0.0-preview.6` and the next yields `.7`. Recompute for the actual cut point with `nbgv get-version -v NuGetPackageVersion`. 3. Dispatch `release.yml --ref release/2.0 -f release_run_id=<run> -f channel=preview`, with a dry run first. `2.0.0-preview.6` is currently free: nuget.org has `.2`–`.5`, and the retired `release/2.0.0` pushed up to `.5` to GitHub Packages. ## Related Issues - Follow-up to #4513. Supersedes the `master` → `release/2.0.0` merge attempts #4566 / #4570; `release/2.0.0` is retired and cannot publish. ## 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. - [x] 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. Locally: `PackageVersionPolicyTests` + `NuGetPackageSetValidationTests` on net10.0, 96/96 passed. 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -4,13 +4,15 @@ name: Release to nuget.org
|
||||
# nuget-publish.yml run on a canonical "release/<major>.<minor>" branch (e.g.
|
||||
# release/2.0). The release environment provides the approval boundary (its
|
||||
# deployment_branch_policy restricts execution to release/* branches) and the
|
||||
# NuGet trusted publishing identity. This workflow additionally requires the
|
||||
# candidate's base package version to be an exact stable release (no
|
||||
# prerelease label or build metadata, e.g. "2.0.0") - a preview candidate is
|
||||
# already published automatically by nuget-publish.yml and must never be
|
||||
# promoted here. See docs/ReleaseProcess.md ("Approved promotion") for the
|
||||
# full procedure, including how to pick a release_run_id and how to recover
|
||||
# from a partial or failed promotion.
|
||||
# NuGet trusted publishing identity. The "channel" input states what is being
|
||||
# promoted and must match the candidate: "stable" (the default) requires an
|
||||
# exact stable release (no prerelease label or build metadata, e.g. "2.0.0");
|
||||
# "preview" requires a public release-line preview (e.g. "2.0.0-preview.6").
|
||||
# nuget-publish.yml only ever pushes previews to GitHub Packages, so this is
|
||||
# the one path that puts a preview on nuget.org. See docs/ReleaseProcess.md
|
||||
# ("Approved promotion" and "Preview release") for the full procedures,
|
||||
# including how to pick a release_run_id and how to recover from a partial or
|
||||
# failed promotion.
|
||||
#
|
||||
# One-time nuget.org trusted publishing policy:
|
||||
# Repository Owner: OPCFoundation
|
||||
@@ -25,6 +27,14 @@ on:
|
||||
description: Successful nuget-publish.yml run ID from a release/<major>.<minor> branch
|
||||
required: true
|
||||
type: string
|
||||
channel:
|
||||
description: What the candidate is - an exact stable release, or a release-line preview (e.g. 2.0.0-preview.6)
|
||||
required: false
|
||||
type: choice
|
||||
options:
|
||||
- stable
|
||||
- preview
|
||||
default: stable
|
||||
dry_run:
|
||||
description: Validate the selected package set without publishing
|
||||
required: false
|
||||
@@ -157,6 +167,7 @@ jobs:
|
||||
RELEASE_RUN_ID: ${{ inputs.release_run_id }}
|
||||
RELEASE_BRANCH: ${{ steps.source.outputs.branch }}
|
||||
RELEASE_COMMIT: ${{ steps.source.outputs.commit }}
|
||||
RELEASE_CHANNEL: ${{ inputs.channel }}
|
||||
run: |
|
||||
. ./.azurepipelines/package-version-policy.ps1
|
||||
$manifestPath = 'promotion/release-manifest.json'
|
||||
@@ -182,24 +193,53 @@ jobs:
|
||||
$manifest.packageVersion
|
||||
}
|
||||
|
||||
# This workflow exists solely to promote a stable release (e.g.
|
||||
# "2.0.0", "2.0.1", "2.1.0") to nuget.org. In-development preview
|
||||
# candidates are already published automatically by
|
||||
# nuget-publish.yml and must never be promoted here. See
|
||||
# docs/ReleaseProcess.md ("Approved promotion").
|
||||
if (-not (Test-StablePackageVersion -Version $expectedBaseVersion)) {
|
||||
# The dispatcher states which kind of release this is, and the
|
||||
# candidate must be exactly that: a stable promotion can never
|
||||
# publish a preview by accident, nor the reverse. A preview must be
|
||||
# a public release-line build ("M.m.p-preview.N", no commit id), so
|
||||
# master or feature-branch previews can never reach nuget.org. See
|
||||
# docs/ReleaseProcess.md ("Approved promotion", "Preview release").
|
||||
$channel = $env:RELEASE_CHANNEL
|
||||
if ($channel -cne 'stable' -and $channel -cne 'preview') {
|
||||
throw "channel must be 'stable' or 'preview', not '$channel'."
|
||||
}
|
||||
if ($channel -ceq 'stable' -and
|
||||
-not (Test-StablePackageVersion -Version $expectedBaseVersion)) {
|
||||
throw (
|
||||
"Run $($env:RELEASE_RUN_ID) produced base version " +
|
||||
"'$expectedBaseVersion', which is not an exact stable release. Only a " +
|
||||
'stable candidate (no prerelease label or build metadata) may be ' +
|
||||
'promoted through this workflow.')
|
||||
"promoted with channel 'stable'; dispatch with channel 'preview' to " +
|
||||
'publish a release-line preview.')
|
||||
}
|
||||
if ($channel -ceq 'preview' -and
|
||||
-not (Test-PromotablePreviewPackageVersion -Version $expectedBaseVersion)) {
|
||||
throw (
|
||||
"Run $($env:RELEASE_RUN_ID) produced base version " +
|
||||
"'$expectedBaseVersion', which is not a release-line preview. Only an " +
|
||||
"'M.m.p-preview.N' candidate without a commit id may be promoted with " +
|
||||
"channel 'preview'.")
|
||||
}
|
||||
# schemaVersion 1 manifests predate the recorded channel. They stay
|
||||
# promotable as stable, but a preview must prove its channel.
|
||||
if ($channel -ceq 'preview' -and $manifest.schemaVersion -ne 2) {
|
||||
throw (
|
||||
"The promotion manifest is schemaVersion $($manifest.schemaVersion), which " +
|
||||
"records no channel. Only a schemaVersion 2 candidate may be promoted " +
|
||||
"with channel 'preview'.")
|
||||
}
|
||||
if ($manifest.schemaVersion -eq 2 -and $manifest.channel -cne $channel) {
|
||||
throw (
|
||||
"The promotion manifest records channel '$($manifest.channel)', but this " +
|
||||
"promotion was dispatched with channel '$channel'.")
|
||||
}
|
||||
if (-not (Test-CanonicalReleaseBranchForPackageVersion `
|
||||
-Ref $expectedRef `
|
||||
-Version $expectedBaseVersion)) {
|
||||
-Version $expectedBaseVersion `
|
||||
-AllowPreview:($channel -ceq 'preview'))) {
|
||||
throw (
|
||||
"Candidate version '$expectedBaseVersion' does not match release branch " +
|
||||
"'$expectedRef'. A stable M.m.p candidate must be promoted only from " +
|
||||
"'$expectedRef'. An M.m.p candidate must be promoted only from " +
|
||||
'the matching release/M.m branch.')
|
||||
}
|
||||
|
||||
@@ -309,7 +349,7 @@ jobs:
|
||||
} |
|
||||
Sort-Object Name)
|
||||
if ($packages.Count -eq 0) {
|
||||
throw 'The stable candidate contains no non-Debug packages to push to nuget.org.'
|
||||
throw 'The candidate contains no non-Debug packages to push to nuget.org.'
|
||||
}
|
||||
Write-Host (
|
||||
"Publishing $($packages.Count) non-Debug package(s) to nuget.org. " +
|
||||
@@ -342,7 +382,9 @@ jobs:
|
||||
# any feed automatically (see its "Skip feed publication" step); this
|
||||
# is the one place a stable release also gets mirrored to GitHub
|
||||
# Packages, gated behind the same approval and dry-run switch as the
|
||||
# nuget.org push above.
|
||||
# nuget.org push above. A preview candidate was already pushed here by
|
||||
# its own nuget-publish.yml run, so this loop is a verified no-op for
|
||||
# it: the preflight and post-push checks prove those are its bytes.
|
||||
if: ${{ !inputs.dry_run }}
|
||||
shell: pwsh
|
||||
env:
|
||||
@@ -378,6 +420,7 @@ jobs:
|
||||
env:
|
||||
RELEASE_RUN_ID: ${{ inputs.release_run_id }}
|
||||
RELEASE_BRANCH: ${{ steps.source.outputs.branch }}
|
||||
RELEASE_CHANNEL: ${{ inputs.channel }}
|
||||
RELEASE_DRY_RUN: ${{ inputs.dry_run }}
|
||||
run: |
|
||||
$manifestPath = 'promotion/release-manifest.json'
|
||||
@@ -400,6 +443,7 @@ jobs:
|
||||
|
||||
- Source run: ``$($env:RELEASE_RUN_ID)``
|
||||
- Source branch: ``$($env:RELEASE_BRANCH)``
|
||||
- Channel: ``$($env:RELEASE_CHANNEL)``
|
||||
- Version: ``$version``
|
||||
- Packages: ``$count``
|
||||
- Dry run: ``$($env:RELEASE_DRY_RUN)``
|
||||
|
||||
Reference in New Issue
Block a user