mirror of
https://github.com/OPCFoundation/UA-.NETStandard.git
synced 2026-10-18 22:55:08 +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:
@@ -257,6 +257,27 @@ function Test-StablePackageVersion {
|
||||
return $Version -match '^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)$'
|
||||
}
|
||||
|
||||
function Test-PromotablePreviewPackageVersion {
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Returns $true when a package version is a public release-line
|
||||
preview, exactly "major.minor.patch-preview.N", for example
|
||||
"2.0.0-preview.6". This is the only preview shape release.yml may
|
||||
promote to nuget.org.
|
||||
.DESCRIPTION
|
||||
A canonical release/M.m branch is a public release for
|
||||
Nerdbank.GitVersioning, so its previews carry no commit id. Every
|
||||
other shape is rejected: "-preview.N.gabc123" and "+gabc123" are
|
||||
non-public builds (master, PR and feature branches), and anything
|
||||
else ("-rc.1", "-preview", a four-component version) is not a
|
||||
numbered preview this repository ever produces. The label is matched
|
||||
case-sensitively (-cmatch): "-PREVIEW.6" is not the shape NBGV emits.
|
||||
#>
|
||||
param([Parameter(Mandatory)][string]$Version)
|
||||
|
||||
return $Version -cmatch '^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)-preview\.(0|[1-9]\d*)$'
|
||||
}
|
||||
|
||||
function Test-CanonicalReleaseBranchRef {
|
||||
<#
|
||||
.SYNOPSIS
|
||||
@@ -304,13 +325,22 @@ function Test-CanonicalReleaseBranchForPackageVersion {
|
||||
comparison below can stay textual: "release/02.0" is not an alternate
|
||||
spelling of the 2.0 line, it is simply not a release line. See
|
||||
Test-CanonicalReleaseBranchRef for why case and leading zeroes matter.
|
||||
|
||||
-AllowPreview additionally accepts a public release-line preview
|
||||
(Test-PromotablePreviewPackageVersion, e.g. "2.0.0-preview.6") under
|
||||
the same major/minor rule. Only release.yml's explicit preview
|
||||
promotion passes it; the stable gates in nuget-publish.yml and
|
||||
release.yml never do.
|
||||
#>
|
||||
param(
|
||||
[Parameter(Mandatory)][string]$Ref,
|
||||
[Parameter(Mandatory)][string]$Version
|
||||
[Parameter(Mandatory)][string]$Version,
|
||||
[switch]$AllowPreview
|
||||
)
|
||||
|
||||
if (-not (Test-StablePackageVersion -Version $Version)) {
|
||||
$isStable = Test-StablePackageVersion -Version $Version
|
||||
$isPreview = $AllowPreview -and (Test-PromotablePreviewPackageVersion -Version $Version)
|
||||
if (-not ($isStable -or $isPreview)) {
|
||||
return $false
|
||||
}
|
||||
|
||||
@@ -321,7 +351,7 @@ function Test-CanonicalReleaseBranchForPackageVersion {
|
||||
}
|
||||
|
||||
$versionMatch = [regex]::Match(
|
||||
$Version, '^(?<major>0|[1-9]\d*)\.(?<minor>0|[1-9]\d*)\.(?:0|[1-9]\d*)$')
|
||||
$Version, '^(?<major>0|[1-9]\d*)\.(?<minor>0|[1-9]\d*)\.(?:0|[1-9]\d*)(?:-preview\.(?:0|[1-9]\d*))?$')
|
||||
return $versionMatch.Success -and
|
||||
$branchMatch.Groups['major'].Value -eq $versionMatch.Groups['major'].Value -and
|
||||
$branchMatch.Groups['minor'].Value -eq $versionMatch.Groups['minor'].Value
|
||||
|
||||
@@ -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)``
|
||||
|
||||
+82
-5
@@ -145,6 +145,7 @@ Every procedure below starts here.
|
||||
| A production-only bug needs a fix that did not start on `master` | [Emergency hotfix](#emergency-hotfix) |
|
||||
| You have a `nuget-publish.yml` run and want to inspect it before/without publishing | [Candidate and dry run](#candidate-and-dry-run) |
|
||||
| A maintainer has approved a stable candidate for publication | [Approved promotion](#approved-promotion) |
|
||||
| Publishing a numbered preview (e.g. `2.0.0-preview.6`) to nuget.org | [Preview release](#preview-release) |
|
||||
| A release or promotion attempt errored, or you are unsure what published | [Failed or partial release](#failed-or-partial-release) |
|
||||
| You just promoted a release and want to confirm everything is consistent | [Post-release verification](#post-release-verification) |
|
||||
|
||||
@@ -159,7 +160,10 @@ Every procedure below starts here.
|
||||
(`release/2.0`, `release/2.1`, ...). While stabilizing, its root version is
|
||||
also `-preview.{height}` (e.g. `2.0.1-preview.{height}` between the `2.0.0`
|
||||
and `2.0.1` releases) and publishes automatically to GitHub Packages, same
|
||||
as `master`. At the exact commit approved for release, its root version is
|
||||
as `master`. Such a release-line preview (`M.m.p-preview.N`, no commit id)
|
||||
reaches nuget.org only through the explicitly dispatched
|
||||
[Preview release](#preview-release) procedure; `master` previews carry a
|
||||
commit id and never can. At the exact commit approved for release, its root version is
|
||||
set to the plain `<major>.<minor>.<patch>` (e.g. `2.0.0`, `2.0.1`, `2.1.0`)
|
||||
- `channel: stable` - and that specific package set is only ever published
|
||||
by the [Approved promotion](#approved-promotion) procedure, never
|
||||
@@ -175,8 +179,9 @@ Every procedure below starts here.
|
||||
`publicReleaseRefSpec` (so only that ref shape ever computes
|
||||
`NBGV_PublicRelease == 'True'`), the `Aggregate and validate packages` step
|
||||
in `nuget-publish.yml` (refuses to build a stable set from a non-canonical
|
||||
ref), `release.yml` (refuses to promote from a non-canonical ref or a
|
||||
non-stable candidate), and the `release` GitHub Environment's
|
||||
ref), `release.yml` (refuses to promote from a non-canonical ref, and
|
||||
refuses any candidate whose version does not match the dispatched `channel`
|
||||
input, which defaults to `stable`), and the `release` GitHub Environment's
|
||||
`deployment_branch_policy` (restricts which branch may even execute the
|
||||
`release.yml` job).
|
||||
- The branch and stable package version must agree: `release/2.0` may
|
||||
@@ -454,8 +459,9 @@ maintainer can subsequently approve [Approved promotion](#approved-promotion).
|
||||
exact stable `major.minor.patch`, or the two disagree; print `$manifest.ref`
|
||||
and `$manifest.basePackageVersion` to see which. For a **preview**
|
||||
candidate this predicate returns `False` by design, because a preview
|
||||
version is not a stable release version - check the shape alone with
|
||||
`Test-CanonicalReleaseBranchRef -Ref $manifest.ref` in that case.
|
||||
version is not a stable release version. For a preview you intend to
|
||||
publish with [Preview release](#preview-release), pass `-AllowPreview`,
|
||||
exactly as `release.yml` does for a `channel: preview` dispatch.
|
||||
|
||||
For a stable candidate, also prove that the preview-only families in the
|
||||
set still sort strictly above everything already published, using the same
|
||||
@@ -592,6 +598,77 @@ fails the promotion instead of being silently skipped.
|
||||
[Patch release](#patch-release), or [Minor release](#minor-release)) to
|
||||
complete its final "advance to the next preview version" step.
|
||||
|
||||
## Preview release
|
||||
|
||||
Publishes a numbered preview of a release line (e.g. `2.0.0-preview.6`) to
|
||||
nuget.org. `nuget-publish.yml` only ever pushes previews to GitHub Packages,
|
||||
so this is the only way a preview reaches nuget.org. It reuses the
|
||||
[Approved promotion](#approved-promotion) machinery, dispatched with
|
||||
`channel=preview`.
|
||||
|
||||
**Owner**: a repository maintainer with `release` GitHub Environment
|
||||
reviewer access.
|
||||
|
||||
**Preconditions**: the [common preflight](#common-preflight) is complete; the
|
||||
canonical `release/<major>.<minor>` branch exists; nothing on nuget.org or
|
||||
GitHub Packages already holds the preview number you intend to publish.
|
||||
|
||||
What stays the same for the GitHub Packages preview feed:
|
||||
|
||||
- `master` keeps publishing `<base>-preview.<height>.g<commit>` to GitHub
|
||||
Packages automatically. Those versions carry a commit id, so they can never
|
||||
collide with a bare release-line `<base>-preview.N`, and `release.yml`
|
||||
rejects them for `channel=preview`.
|
||||
- Every build of the release branch already pushed its own
|
||||
`<base>-preview.N` to GitHub Packages. `release.yml`'s GitHub Packages step
|
||||
is therefore a verified no-op for a preview: the identity preflight and the
|
||||
post-push check prove the feed holds this candidate's exact bytes.
|
||||
|
||||
1. **Choose the preview number.** A canonical release branch is a public
|
||||
release for Nerdbank.GitVersioning, so its previews are
|
||||
`<base>-preview.<height + versionHeightOffset>`, and a branch freshly cut
|
||||
from `master` inherits `master`'s height. To make the next build carry a
|
||||
specific number (for example to continue after `2.0.0-preview.5`), commit
|
||||
a `versionHeightOffset` change on the release branch through a normal PR,
|
||||
and check the result on that commit before merging:
|
||||
```powershell
|
||||
nbgv get-version -v NuGetPackageVersion
|
||||
```
|
||||
*Completion evidence*: it prints exactly the intended version, e.g.
|
||||
`2.0.0-preview.6`. The offset is negative when `master`'s height is already
|
||||
past the target. Each later commit on the branch increases the number by
|
||||
one, so publish the build of the exact commit you verified.
|
||||
|
||||
2. Find that commit's successful `nuget-publish.yml` run and inspect it as
|
||||
in [Candidate and dry run](#candidate-and-dry-run) steps 1-2.
|
||||
*Completion evidence*: `channel` is `preview`, `basePackageVersion` is the
|
||||
intended version with no `.g<commit>` suffix, and `ref` is the release
|
||||
branch.
|
||||
|
||||
3. Dry-run, then publish, from the same release branch:
|
||||
```powershell
|
||||
gh workflow run release.yml --ref release/<major>.<minor> `
|
||||
-f release_run_id=<run-id> -f channel=preview -f dry_run=true
|
||||
gh workflow run release.yml --ref release/<major>.<minor> `
|
||||
-f release_run_id=<run-id> -f channel=preview -f dry_run=false
|
||||
```
|
||||
Approve the `release` environment deployment when prompted.
|
||||
*Completion evidence*: the run passes "Validate promotion manifest and
|
||||
package bytes", reports its nuget.org publication byte-identical to the
|
||||
candidate, and the summary shows `Channel: preview`. A candidate whose
|
||||
version does not match the dispatched channel fails in the validation
|
||||
step, before anything is published.
|
||||
|
||||
4. Confirm the preview is live on nuget.org:
|
||||
```powershell
|
||||
Invoke-RestMethod 'https://api.nuget.org/v3-flatcontainer/opcfoundation.netstandard.opc.ua.core/index.json' |
|
||||
Select-Object -ExpandProperty versions | Select-Object -Last 3
|
||||
```
|
||||
*Completion evidence*: the intended preview version appears. Recover a
|
||||
partial preview publication exactly as in
|
||||
[Failed or partial release](#failed-or-partial-release), keeping
|
||||
`channel=preview` on the re-run.
|
||||
|
||||
## Failed or partial release
|
||||
|
||||
Recovers from an error anywhere in [Candidate and dry run](#candidate-and-dry-run)
|
||||
|
||||
@@ -280,6 +280,98 @@ namespace Opc.Ua.Tools.Tests
|
||||
Assert.That(result.GetProperty("actual").GetBoolean(), Is.EqualTo(expectedMatch));
|
||||
}
|
||||
|
||||
[TestCase("2.0.0-preview.6", true, Description = "Public release-line preview")]
|
||||
[TestCase("2.0.1-preview.0", true, Description = "Zero preview number")]
|
||||
[TestCase("2.1.0-preview.12", true, Description = "Multi-digit preview number")]
|
||||
[TestCase("2.0.0", false, Description = "Stable is not a preview")]
|
||||
[TestCase("2.0.0-preview.1.gabc123def0", false, Description = "Non-public build carries a commit id")]
|
||||
[TestCase("2.0.0-preview.6+gabc123def0", false, Description = "Build metadata")]
|
||||
[TestCase("2.0.0-PREVIEW.6", false, Description = "Upper-case label")]
|
||||
[TestCase("2.0.0-Preview.6", false, Description = "Mixed-case label")]
|
||||
[TestCase("2.0.0-preview", false, Description = "Unnumbered preview")]
|
||||
[TestCase("2.0.0-preview.06", false, Description = "Leading zero in the preview number")]
|
||||
[TestCase("2.0.0-rc.1", false, Description = "Other prerelease label")]
|
||||
[TestCase("2.0.0.0-preview.1", false, Description = "Four-component version")]
|
||||
[TestCase("2.0-preview.6", false, Description = "Two-component version")]
|
||||
public async Task TestPromotablePreviewPackageVersionAsync(string version, bool expectedPreview)
|
||||
{
|
||||
JsonElement result = await RunPolicyScriptAsync(
|
||||
$$"""
|
||||
. '{{PolicyScriptPath}}'
|
||||
@{ actual = (Test-PromotablePreviewPackageVersion -Version '{{version}}') } | ConvertTo-Json
|
||||
""").ConfigureAwait(false);
|
||||
|
||||
Assert.That(result.GetProperty("actual").GetBoolean(), Is.EqualTo(expectedPreview));
|
||||
}
|
||||
|
||||
[TestCase("refs/heads/release/2.0", "2.0.0-preview.6", true)]
|
||||
[TestCase("refs/heads/release/2.0", "2.0.1-preview.3", true)]
|
||||
[TestCase("refs/heads/release/2.0", "2.0.0", true, Description = "Stable still accepted")]
|
||||
[TestCase("refs/heads/release/2.0", "2.1.0-preview.1", false, Description = "Other minor line")]
|
||||
[TestCase("refs/heads/release/2.0", "2.0.0-PREVIEW.6", false, Description = "Upper-case label")]
|
||||
[TestCase("refs/heads/release/2.0", "2.0.0-preview.6.gabc123def0", false, Description = "Non-public build")]
|
||||
[TestCase("refs/heads/release/2.0", "2.0.0-preview.6+gabc123def0", false, Description = "Build metadata")]
|
||||
[TestCase("refs/heads/release/2.0.0", "2.0.0-preview.6", false, Description = "Retired three-component branch")]
|
||||
[TestCase("refs/heads/master", "2.0.0-preview.6", false, Description = "master never promotes")]
|
||||
[TestCase("refs/heads/Release/2.0", "2.0.0-preview.6", false, Description = "Case-sensitive like nbgv")]
|
||||
public async Task TestCanonicalReleaseBranchForPreviewPackageVersionAsync(
|
||||
string ruleRef,
|
||||
string version,
|
||||
bool expectedMatch)
|
||||
{
|
||||
JsonElement result = await RunPolicyScriptAsync(
|
||||
$$"""
|
||||
. '{{PolicyScriptPath}}'
|
||||
@{
|
||||
allowed = (Test-CanonicalReleaseBranchForPackageVersion -Ref '{{ruleRef}}' -Version '{{version}}' -AllowPreview)
|
||||
stableOnly = (Test-CanonicalReleaseBranchForPackageVersion -Ref '{{ruleRef}}' -Version '{{version}}')
|
||||
} | ConvertTo-Json
|
||||
""").ConfigureAwait(false);
|
||||
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.That(result.GetProperty("allowed").GetBoolean(), Is.EqualTo(expectedMatch));
|
||||
Assert.That(
|
||||
result.GetProperty("stableOnly").GetBoolean(),
|
||||
Is.EqualTo(expectedMatch && !version.Contains('-', StringComparison.Ordinal)),
|
||||
"Without -AllowPreview the stable gate must keep rejecting every preview.");
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ReleaseWorkflowPromotesAPreviewOnlyWhenDispatchedAsOne()
|
||||
{
|
||||
// nuget-publish.yml never pushes to nuget.org, so release.yml is
|
||||
// the only path there for a preview too. It must default to the
|
||||
// stable channel, accept a preview only when the dispatcher asked
|
||||
// for one, and require the candidate's recorded channel to agree.
|
||||
string workflow = File.ReadAllText(ReleaseWorkflowPath);
|
||||
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.That(
|
||||
workflow,
|
||||
Does.Match(@"channel:\s*\n(?:\s+.*\n)*?\s+default: stable\s*\n"),
|
||||
"The channel input must default to 'stable'.");
|
||||
Assert.That(
|
||||
workflow,
|
||||
Does.Contain("-AllowPreview:($channel -ceq 'preview')"),
|
||||
"Only a preview dispatch may relax the release-branch version gate.");
|
||||
Assert.That(
|
||||
workflow,
|
||||
Does.Contain("Test-PromotablePreviewPackageVersion -Version $expectedBaseVersion"),
|
||||
"A preview dispatch must require a public release-line preview version.");
|
||||
Assert.That(
|
||||
workflow,
|
||||
Does.Contain("$manifest.channel -cne $channel"),
|
||||
"The candidate's recorded channel must match the dispatched channel.");
|
||||
Assert.That(
|
||||
workflow,
|
||||
Does.Contain("$channel -ceq 'preview' -and $manifest.schemaVersion -ne 2"),
|
||||
"A preview must come from a manifest that records its channel.");
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task GetPreviewPackageBuildNumberMatchesCommittedPropsFileAsync()
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user