mirror of
https://github.com/OPCFoundation/UA-.NETStandard.git
synced 2026-10-27 04:59:55 +08:00
# Description Remove stock phrasing and correct misleading or inconsistent technical documentation. The initial two passes covered 367 Markdown files, followed by an interactive review of 169 editorial flags. The U5 interpretation originally deferred in that review has since received the upstream Mantis answer recorded by #4843; remaining server/spec follow-ups stay explicit in the CTT log. - Correct package responsibilities, migration steps, API names, ownership, protocol descriptions and links. Keep bundled migration guides synchronized. Preserve historical conclusions as historical records with separate re-check notes. - Provide at least two rendered TOC levels throughout `docs/`. Convert appropriate architecture figures to Mermaid without changing code examples, transcripts or historical quotations. - Address the October 19 feedback with concrete setup/migration instructions, corrected certificate paths and PubSub names, shorter repeated caveats, consistent XML comments, and a full review of the Concepts guide. Retain #4839's API corrections. - Integrate #4841's runtime/performance guidance and review incoming/recent prose. Preserve dated measurements rather than claiming new benchmark runs; clarify allocation ratios, startup-only processor settings, JIT warm-up and the benchmark commit's scope. Missing raw measurement artifacts and repeat ranges remain tracked by #4842. - Merge master through `e8c5d800a` in `32c6394b7`. Resolve the CTT log conflict using the updated Mantis states and OPC answers, retaining historical framing and re-checks for C28/C32/C43. - Address the latest naming/index feedback in `adb5bf693`: rename the guides to `docs/WotCon.md` and `docs/WotNodeSetConversion.md`; move the provider and DataType-declaration contracts into the conversion guide and remove `docs/WoT.md`. Rename `samples/DI` to `samples/Di` and `samples/Isa95` to `samples/ISA95`. Add content-index READMEs to Di, FluentApi, ISA95, Lds and PubSub; update solution entries, project references, Docker/CI paths, test fixture paths and documentation links with exact casing. **WoT guide scope.** This branch retains the detailed conversion guide that #4758 had replaced with an anchor stub. Conversion limits, verified document sets, preservation rules and provider ownership now live together in `WotNodeSetConversion.md`; binding/runtime guidance remains in `WotBindings.md` and `WotCon.md`. The design-rationale/test-ledger prose was shortened, the oversized argument-table cell moved into its section, and existing anchors preserved. **SDK policy is a separate, explicit user-directed change.** `global.json` sets a stable minimum of `10.0.400`, `rollForward: latestMajor`, and `allowPrerelease: false`. Newer major SDKs are permitted; CI's explicit SDK pins are unchanged. README, GettingStarted, DeveloperGuide and Recycling instructions reflect the possible analyzer differences. The image-contract regression fix accepts compatible stable SDK images instead of requiring exact equality with the floor. The latest four rename/index threads are resolved after publication. Three older threads remain open: universal obsolete-removal timing, unconditional nonce-strength wording, and a blanket Vision NativeAOT claim. Existing implementation/evidence constraints are explained in their inline replies. The two-level TOCs remain as requested. ## Validation - `git diff --check` and staged diff checks: passed. The untouched upstream AutoID NodeSet retains its provenance bytes, including two existing license-header trailing spaces. - `.\.azurepipelines\validate-migration-plugin.ps1`: passed; 15 bundled migration documents and manifests synchronized. - Documentation/path validation: **101** documents retain two rendered TOC levels (**2,228** entries), with idempotent formatting. **52** new relative links and their anchors passed exact-case checks. No tracked references to the old guide/sample paths remain. Both relocated provider-contract sections are preserved in the destination. - `dotnet sln UA.slnx list`: passed; shows the renamed Di/ISA95 projects and the existing FluentApi/Lds/PubSub projects. Updated solution and pump ProjectReference paths also passed exact-case existence checks. - Latest focused commands, run from the repository root: ```powershell # Passed: 61 channel send-order and reconnect tests. dotnet test tests\Opc.Ua.Core.Tests\Opc.Ua.Core.Tests.csproj -p:CustomTestTarget=net10.0 --filter 'FullyQualifiedName~UaSCBinaryChannelSendOrderTests|FullyQualifiedName~TcpReconnectKeyAgreementTests' --verbosity minimal # Passed: 5 pump datasheet tests; builds the renamed pump sample. dotnet test tests\Opc.Ua.Di.Tests\Opc.Ua.Di.Tests.csproj -p:CustomTestTarget=net10.0 --filter 'FullyQualifiedName~PumpDatasheetConformanceTests' --verbosity minimal # Passed: 4 repository NodeSet path/round-trip tests, including the moved pump models. dotnet test tests\Opc.Ua.Types.Tests\Opc.Ua.Types.Tests.csproj -p:CustomTestTarget=net10.0 --filter 'FullyQualifiedName~RepositorySourceSurvivesSerializedLinkedPartitionsAsync' --verbosity minimal # Passed: build, zero warnings/errors. dotnet build samples\ISA95\MinimalIsa95Server\MinimalIsa95Server.csproj -p:CustomTestTarget=net10.0 -f net10.0 --verbosity minimal # Passed: dependent sample-test project build, zero warnings/errors. dotnet build tests\Opc.Ua.WotCon.Samples.Tests\Opc.Ua.WotCon.Samples.Tests.csproj -p:CustomTestTarget=net10.0 -f net10.0 --verbosity minimal ``` Earlier focused integration checks passed 2,620 tests across AutoID, event queues/scheduling, source generation, MCP/package profiles and WoT. Both reviewed Mermaid diagrams rendered locally without network access. These checks are not a full solution run. The full solution has **not** been rerun for this review. Earlier `dotnet test UA.slnx -p:CustomTestTarget=net10.0 --verbosity quiet` did not pass: three AOT projects rejected VSTest and 20 Vision tests failed during graphics initialization with `OutOfMemoryException`. Remaining long-running tests were stopped at the user's request. A focused Vision rerun passed 558 tests and reproduced those 20 failures. The earlier `dotnet test UA.slnx -p:CustomTestTarget=net48 --verbosity quiet` was blocked by the former SDK pin and was not retried after the policy change. The user approved proceeding with those limitations documented; current remote CI is not claimed green. ## Related Issues - Follow-up #4842 tracks the requested 2.0.2+ benchmark/tuning and diagnostic-tool documentation refresh. #4841 supplies documentation and earlier results, not all newly requested measurements. - #4758 explains the conversion guide's earlier consolidation; the current provider contracts are now consolidated into `WotNodeSetConversion.md`. - #4839 supplies the reviewed API corrections; #4843 supplies the updated CTT issue record. - The original prose cleanup and SDK policy were user-requested without a source issue. No new runtime architecture is introduced by these documentation edits. ## Checklist _Put an `x` in the boxes that apply. You can complete these step by step after opening the PR._ - [ ] 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. - [ ] 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. --------- Co-authored-by: Roman Ettlinger <romanett98@gmail.com> Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
851 lines
44 KiB
Markdown
851 lines
44 KiB
Markdown
# Release process
|
|
|
|
This is the authoritative procedure for every release operation on this
|
|
repository: cutting a release line, shipping a patch or minor version,
|
|
backporting a fix, promoting a stable candidate, and recovering from a
|
|
failed or partial release. Follow it exactly as written; it is designed to
|
|
be followed by a developer or an agent working from the repository alone,
|
|
without any other context.
|
|
|
|
> **Read this before**: changing a release version, cutting or retiring a
|
|
> release branch, backporting a fix, running `release.yml`, or recovering
|
|
> from a failed release.
|
|
|
|
## Contents
|
|
|
|
- [Release process](#release-process)
|
|
- [Terms](#terms)
|
|
- [Common preflight](#common-preflight)
|
|
- [Which procedure do I need?](#which-procedure-do-i-need)
|
|
- [Branch and channel model](#branch-and-channel-model)
|
|
- [First stable release](#first-stable-release)
|
|
- [Patch release](#patch-release)
|
|
- [Minor release](#minor-release)
|
|
- [Routine backport](#routine-backport)
|
|
- [Emergency hotfix](#emergency-hotfix)
|
|
- [Candidate and dry run](#candidate-and-dry-run)
|
|
- [Approved promotion](#approved-promotion)
|
|
- [Preview release](#preview-release)
|
|
- [Failed or partial release](#failed-or-partial-release)
|
|
- [Post-release verification](#post-release-verification)
|
|
- [Release handoff template](#release-handoff-template)
|
|
- [Historical `release/2.0.0` branch](#historical-release200-branch)
|
|
- [Maintaining this document](#maintaining-this-document)
|
|
|
|
## Terms
|
|
|
|
- **Root version** - the package version computed from the committed
|
|
`version.json` for the commit being built (see
|
|
[Versioning](DeveloperGuide.md#versioning)). Every package in the solution
|
|
shares the same root version except the **preview-only families**.
|
|
- **Preview-only families** - the XRegistry, WoT Connectivity,
|
|
Robotics, CranesHoists, Redundancy, Positioning, OpenUSD, and AI package
|
|
families, the Vision.OpenUsd renderer, the Robotics and CranesHoists MCP extensions, and the OpenUSD connector
|
|
tools. `version.targets` keeps these on a `-preview.N` suffix even when the
|
|
root version is an exact stable release. The full, single source of truth
|
|
for which package IDs this covers is `_IsPreviewPackage` in
|
|
`version.targets` (build-time) and `Test-PreviewPackageId` in
|
|
`.azurepipelines/package-version-policy.ps1` (validation-time) - both must
|
|
always list the same families.
|
|
Debug variants of these families follow the same preview-only classification.
|
|
The Vision, MachineVision, MachineTool (including bundled WMTP), and GMS
|
|
model/client/server packages and the Vision, MachineTool and GMS MCP packages
|
|
follow the root version instead. WMTP has no separate package or profile.
|
|
- **Channel** - `stable` when the root version has no prerelease label or
|
|
build metadata (e.g. `2.0.0`); `preview` otherwise (e.g. `2.0.0-preview.6`,
|
|
`2.0.1-preview.4+gabc123`). `validate-nuget-package-set.ps1` writes this as
|
|
`channel` in its manifest; `Test-StablePackageVersion` in
|
|
`package-version-policy.ps1` is the single source of truth.
|
|
- **Canonical release branch** - a branch named exactly
|
|
`release/<major>.<minor>` (e.g. `release/2.0`, `release/2.1`) - the only
|
|
branch shape `version.json`'s `publicReleaseRefSpec` and
|
|
`Test-CanonicalReleaseBranchRef` recognize as eligible to produce a
|
|
`stable` channel package set. The name is matched **case-sensitively**
|
|
(`Release/2.0` is not a release line) and each component must be a plain
|
|
decimal number with no leading zero (`release/02.0` is not a second
|
|
spelling of `release/2.0`, it is simply not a release line). The
|
|
historical three-component naming (`release/2.0.0`) is retired and not
|
|
canonical; see
|
|
[Historical `release/2.0.0` branch](#historical-release200-branch).
|
|
- **Candidate** - a signed, validated package set produced by a
|
|
`nuget-publish.yml` run, retained as its `opcua-packages-<run id>` GitHub
|
|
Actions artifact.
|
|
- **Debug packages** - Debug-configuration packages use the `.Debug` suffix,
|
|
or `.Debug.<rid>` for a platform-specific tool payload. The shared
|
|
`Test-DebugPackageId` policy recognizes both forms. They are retained on
|
|
GitHub Packages and excluded from nuget.org publication and verification.
|
|
|
|
## Common preflight
|
|
|
|
Every procedure below starts here.
|
|
|
|
1. **Resolve the repository and remotes.**
|
|
```powershell
|
|
git rev-parse --show-toplevel
|
|
git remote -v
|
|
```
|
|
Confirm you are in a clone of `OPCFoundation/UA-.NETStandard` (or a fork
|
|
with `origin`/`upstream` pointing at it) before continuing.
|
|
|
|
2. **Require a clean worktree.**
|
|
```powershell
|
|
git status --short
|
|
```
|
|
*Completion evidence*: no output. Commit, stash, or discard changes before
|
|
continuing - every procedure below assumes a clean starting point.
|
|
|
|
3. **Fetch and confirm the source/target branches exist.**
|
|
```powershell
|
|
git fetch origin --prune
|
|
git branch --all --list 'origin/release/*'
|
|
```
|
|
*Completion evidence*: the branch(es) the procedure you are about to run
|
|
names are present (or, for a branch-creation step, deliberately absent).
|
|
|
|
4. **Confirm required tooling and authentication.**
|
|
```powershell
|
|
dotnet --version
|
|
gh auth status
|
|
```
|
|
*Completion evidence*: a .NET 10 SDK version, and `gh` reports an
|
|
authenticated account with access to `OPCFoundation/UA-.NETStandard`.
|
|
Promotion (`release.yml`) additionally requires the `release` GitHub
|
|
Environment's approval; you cannot grant that to yourself; a repository
|
|
maintainer with environment-reviewer access must approve the run.
|
|
|
|
5. **Discover the currently published preview numbers before touching
|
|
`preview-version.props`.** Every procedure that changes the root version
|
|
to an exact stable release must confirm the committed
|
|
`PreviewPackageBuildNumber` (see `preview-version.props`) is still greater
|
|
than every already-published `-preview.N` (or legacy `-preview.YYYYMMDD`)
|
|
identifier for the preview-only families, on **both** feeds:
|
|
```powershell
|
|
Invoke-RestMethod 'https://api.nuget.org/v3-flatcontainer/opcfoundation.netstandard.opc.ua.xregistry/index.json' |
|
|
Select-Object -ExpandProperty versions
|
|
gh api '/orgs/OPCFoundation/packages/nuget/OPCFoundation.NetStandard.Opc.Ua.XRegistry/versions' --paginate |
|
|
ConvertFrom-Json | ForEach-Object name
|
|
```
|
|
Repeat for a representative sample of the preview-only families (at
|
|
minimum XRegistry, Robotics, and one MCP/connector package); they are versioned
|
|
together but published independently, so check more than one. If any
|
|
published number is greater than or equal to the committed
|
|
`PreviewPackageBuildNumber`, raise it (in the same PR that changes the
|
|
root version to stable) before proceeding.
|
|
|
|
This manual sampling is only a headroom estimate. The authoritative check
|
|
is `.azurepipelines/validate-preview-package-ordering.ps1`, which queries
|
|
**every** preview-family package ID in a candidate's manifest against both
|
|
feeds. `nuget-publish.yml` and `release.yml` both run it automatically for
|
|
stable candidates, and both fail if any already-published version is not
|
|
strictly below the candidate's `-preview.N`. Note that ordinary `master`
|
|
development keeps publishing `<base>-preview.{height}` packages with a
|
|
growing height, so a number that was correct last week can go stale on its
|
|
own - always re-run the check against the **actual merged candidate**
|
|
([Candidate and dry run](#candidate-and-dry-run) step 3) before approving
|
|
a promotion.
|
|
|
|
## Which procedure do I need?
|
|
|
|
| Situation | Procedure |
|
|
| --- | --- |
|
|
| Shipping the very first stable `2.0.0` | [First stable release](#first-stable-release) |
|
|
| Shipping the next patch on an existing line (e.g. `2.0.1`, `2.0.2`) | [Patch release](#patch-release) |
|
|
| Shipping the next minor line (e.g. `2.1.0`) | [Minor release](#minor-release) |
|
|
| Porting an already-reviewed `master` fix onto a release branch | [Routine backport](#routine-backport) |
|
|
| 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) |
|
|
|
|
## Branch and channel model
|
|
|
|
- `master` always carries a `-preview.{height}` root version for the next
|
|
minor release under development (e.g. `2.0.0-preview.{height}` while `2.0`
|
|
is being prepared, `2.1.0-preview.{height}` once `2.0` has shipped).
|
|
Every `master` build is `channel: preview` and publishes automatically to
|
|
GitHub Packages; it never produces a stable release.
|
|
- Each maintained minor line lives on its own canonical release branch
|
|
(`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`. 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
|
|
automatically.
|
|
- Patch numbers only ever increase by exactly one per release on their line
|
|
(`2.0.0` → `2.0.1` → `2.0.2`, ...); a minor bump resets the patch to zero
|
|
(`2.1.0`, not `2.1.<height>`). Nothing about git height, commit count, or
|
|
CI retries changes these numbers - they are explicit, committed decisions
|
|
in `version.json`.
|
|
- A release is **never** produced from `master`, a tag, a PR branch, or any
|
|
branch other than its own canonical `release/<major>.<minor>` line. This is
|
|
enforced in multiple independent places: `version.json`'s
|
|
`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, 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
|
|
release `2.0.p` only, while `release/2.1` may release `2.1.p` only.
|
|
`Test-CanonicalReleaseBranchForPackageVersion` enforces this in both the
|
|
candidate and promotion workflows, so a valid-looking `2.1.0` package set
|
|
cannot be released from `release/2.0`.
|
|
- Container image tags follow the same line model, enforced by the
|
|
`Determine release line precedence` step in
|
|
`.github/workflows/docker-image.yml`. Every build gets its exact version
|
|
tag; a stable release additionally gets `<major>.<minor>` and the plain
|
|
`<major>.<minor>.<patch>`. The unqualified `latest` and `release` aliases
|
|
are claimed **only** by the newest canonical `release/<major>.<minor>`
|
|
branch that exists on `origin`, so a later `2.0.x` maintenance release
|
|
publishes `latest-2.0` instead and cannot move `latest`/`release` back
|
|
from an already published `2.1.x`. The check fails closed: if the newest
|
|
line cannot be determined, the build is treated as superseded and only
|
|
publishes line-specific aliases. Non-release builds keep their existing
|
|
branch-suffixed `latest-<branch>` alias, and the master-only pump image
|
|
publishes `preview` rather than `latest`.
|
|
|
|
## First stable release
|
|
|
|
Ships the very first stable version on a brand-new minor line (in this
|
|
repository's history, `2.0.0`).
|
|
|
|
**Owner**: a repository maintainer with permission to create protected
|
|
branches and merge to `master`.
|
|
|
|
**Preconditions**: the [common preflight](#common-preflight) is complete; the
|
|
preparation work for the release (dependency/version-model changes, this
|
|
document) is already merged to `master`; `master`'s root version is
|
|
`2.0.0-preview.{height}`.
|
|
|
|
1. Confirm `master`'s current committed version:
|
|
```powershell
|
|
git show HEAD:version.json
|
|
```
|
|
*Completion evidence*: `"version": "2.0.0-preview.{height}"`.
|
|
|
|
2. Choose the exact `master` commit to branch from (normally its current
|
|
`HEAD`, after its required checks have passed) and create the canonical
|
|
release branch from it:
|
|
```powershell
|
|
git fetch origin master
|
|
git branch release/2.0 origin/master
|
|
git push origin release/2.0
|
|
```
|
|
*Completion evidence*: `gh api repos/OPCFoundation/UA-.NETStandard/branches/release/2.0`
|
|
returns the expected commit SHA.
|
|
|
|
3. Immediately advance `master` to the next minor line's preview version so
|
|
no further commit is mistaken for `2.0` servicing work. Open a PR (do not
|
|
push directly to the protected branch):
|
|
```powershell
|
|
git switch -c bump-version-2.1-preview origin/master
|
|
```
|
|
Edit `version.json`'s `"version"` to `"2.1.0-preview.{height}"`. Commit,
|
|
push, and open a PR titled something like "Advance master to 2.1.0
|
|
preview". Merge once required checks pass.
|
|
*Completion evidence*: `master`'s `version.json` shows
|
|
`2.1.0-preview.{height}`.
|
|
|
|
4. On `release/2.0`, open a **second** PR that sets the stable version and
|
|
completes the release preflight (do not combine this with step 3 - they
|
|
target different branches):
|
|
```powershell
|
|
git switch -c release-2.0.0 origin/release/2.0
|
|
```
|
|
Edit `version.json`'s `"version"` to `"2.0.0"` (no prerelease label).
|
|
Re-run the preview-number check from
|
|
[Common preflight](#common-preflight) step 5 against the actual current
|
|
published state and adjust `preview-version.props` if needed. Commit,
|
|
push, and open a PR against `release/2.0`.
|
|
*Completion evidence*: the PR's diff changes only `version.json` (and, if
|
|
needed, `preview-version.props`); required checks pass.
|
|
|
|
5. Merge the PR from step 4 once its checks pass and it has the required
|
|
approval. This is the release commit.
|
|
|
|
6. Continue with [Candidate and dry run](#candidate-and-dry-run) using the
|
|
`nuget-publish.yml` run triggered by that merge, then
|
|
[Approved promotion](#approved-promotion).
|
|
|
|
7. After promotion, reopen `release/2.0` for the next patch's development by
|
|
repeating step 3's pattern on `release/2.0` itself, setting `version.json`
|
|
to `"2.0.1-preview.{height}"` via a PR.
|
|
|
|
## Patch release
|
|
|
|
Ships the next patch on an already-shipped line (for example `2.0.1` after
|
|
`2.0.0`, or `2.0.2` after `2.0.1`).
|
|
|
|
**Owner**: a repository maintainer.
|
|
|
|
**Preconditions**: the [common preflight](#common-preflight) is complete;
|
|
`release/<major>.<minor>` already exists and its current committed
|
|
`version.json` is the next patch's `-preview.{height}` version (see step 7 of
|
|
[First stable release](#first-stable-release) or step 3 below, from a prior
|
|
run of this same procedure).
|
|
|
|
1. Confirm the last released version on this line and the next patch number:
|
|
```powershell
|
|
gh api repos/OPCFoundation/UA-.NETStandard/releases --paginate --jq '.[].tag_name' |
|
|
Where-Object { $_ -match '^2\.0\.\d+$' } | Sort-Object { [version]$_ }
|
|
```
|
|
The next patch is exactly one more than the highest result - never skip a
|
|
number and never reuse one.
|
|
|
|
2. Collect and backport the fixes intended for this patch (see
|
|
[Routine backport](#routine-backport) for each one) before continuing.
|
|
|
|
3. Open a PR against `release/<major>.<minor>` that sets `version.json`'s
|
|
`"version"` to the exact next patch (e.g. `"2.0.1"`, no prerelease label),
|
|
re-verifying the preview-number check from
|
|
[Common preflight](#common-preflight) step 5. Merge once checks pass. This
|
|
is the release commit.
|
|
|
|
4. Continue with [Candidate and dry run](#candidate-and-dry-run), then
|
|
[Approved promotion](#approved-promotion).
|
|
|
|
5. After promotion, open a PR against the same release branch advancing
|
|
`version.json` to the *following* patch's preview version (e.g.
|
|
`"2.0.2-preview.{height}"`) before accepting further servicing work.
|
|
|
|
## Minor release
|
|
|
|
Ships the next minor line (for example `2.1.0` after `2.0.x`).
|
|
|
|
**Owner**: a repository maintainer.
|
|
|
|
**Preconditions**: the [common preflight](#common-preflight) is complete;
|
|
`master`'s current committed root version is the new minor's preview version
|
|
(e.g. `2.1.0-preview.{height}`, set when the *previous* minor line was cut -
|
|
see step 3 of [First stable release](#first-stable-release)).
|
|
|
|
This procedure is identical to [First stable release](#first-stable-release)
|
|
with two substitutions: create `release/<new-minor>` (e.g. `release/2.1`)
|
|
instead of `release/2.0`, and advance `master` to the minor *after* that
|
|
(e.g. `2.2.0-preview.{height}`) instead of `2.1.0-preview.{height}`. Every
|
|
older maintained line (e.g. `release/2.0`) is unaffected and continues to
|
|
receive its own patches independently through
|
|
[Patch release](#patch-release); do not merge the new minor's branch into or
|
|
out of an older line.
|
|
|
|
## Routine backport
|
|
|
|
Ports an already-merged, already-tested fix from `master` onto a release
|
|
branch for an upcoming patch.
|
|
|
|
**Owner**: any contributor.
|
|
|
|
**Preconditions**: the fix already has a merged commit on `master`; the
|
|
[common preflight](#common-preflight) is complete; you know the target
|
|
`release/<major>.<minor>` branch and the commit SHA to port.
|
|
|
|
1. Branch from the release line, not from `master`:
|
|
```powershell
|
|
git fetch origin release/2.0
|
|
git switch -c backport-<short-description> origin/release/2.0
|
|
```
|
|
|
|
2. Cherry-pick the fix with traceability to its source commit. Never
|
|
cherry-pick a version-only or release-preparation commit (that would
|
|
collide with the target branch's own version state):
|
|
```powershell
|
|
git cherry-pick -x <source-commit-sha>
|
|
```
|
|
Resolve conflicts so only the intended fix lands - do not pull in
|
|
unrelated `master`-only changes, and do not alter `version.json` or
|
|
`preview-version.props` in this PR.
|
|
|
|
3. Push and open a PR **against the release branch**
|
|
(`release/<major>.<minor>`, not `master`). Reference the original `master`
|
|
PR/commit in the description.
|
|
*Completion evidence*: required checks pass on the release branch build;
|
|
the PR is reviewed and merged.
|
|
|
|
4. Confirm the fix is included in the next
|
|
[Patch release](#patch-release) for that line.
|
|
|
|
## Emergency hotfix
|
|
|
|
For a production-only defect that cannot wait for the normal
|
|
`master`-first flow (for example a security fix needed on an older
|
|
maintained line before it can land anywhere else).
|
|
|
|
**Owner**: a repository maintainer, in coordination with whoever authors the
|
|
fix.
|
|
|
|
**Preconditions**: the [common preflight](#common-preflight) is complete; you
|
|
have identified every maintained release line the defect affects (there may
|
|
be more than one) and the current state of `master`.
|
|
|
|
1. Start on the **oldest** affected maintained release branch. Branch, fix,
|
|
and open a PR against that release branch exactly as in
|
|
[Routine backport](#routine-backport) steps 1-3, except the fix itself is
|
|
authored fresh here rather than cherry-picked from an existing commit.
|
|
|
|
2. Once merged there, forward-port the same fix (cherry-pick with `-x` from
|
|
the release-branch commit this time) into every newer affected maintained
|
|
release line, in ascending order, each through its own PR against that
|
|
line.
|
|
|
|
3. Forward-port the same fix into `master` last, through an ordinary PR.
|
|
|
|
4. Track the forward-port chain to completion explicitly (for example in the
|
|
original issue or PR description): list every affected line, and do not
|
|
consider the hotfix done until each one has a merged PR. A hotfix that
|
|
never reaches `master` will keep reappearing on every future release of
|
|
the older line.
|
|
|
|
5. Include the fix in the next [Patch release](#patch-release) for each
|
|
affected line.
|
|
|
|
## Candidate and dry run
|
|
|
|
Produces (or locates) a validated, signed package set from `nuget-publish.yml`
|
|
and inspects it without publishing anything. Every release and every dry run
|
|
starts here.
|
|
|
|
**Owner**: any contributor can run this read-only inspection; only a
|
|
maintainer can subsequently approve [Approved promotion](#approved-promotion).
|
|
|
|
**Preconditions**: the [common preflight](#common-preflight) is complete.
|
|
|
|
1. Find the `nuget-publish.yml` run for the commit you care about:
|
|
```powershell
|
|
gh run list --workflow nuget-publish.yml --branch release/2.0 --limit 5
|
|
```
|
|
*Completion evidence*: a `run id` with `status: completed` and
|
|
`conclusion: success`. If none exists yet for your commit, either wait for
|
|
the automatic run (pushes to `master` and canonical release branches
|
|
trigger it) or start one manually:
|
|
```powershell
|
|
gh workflow run nuget-publish.yml --ref release/2.0
|
|
```
|
|
|
|
2. Download that run's `opcua-packages-<run id>` artifact and inspect its
|
|
manifest:
|
|
```powershell
|
|
gh run download <run-id> --name opcua-packages-<run-id> --dir ./candidate
|
|
Get-Content ./candidate/release-manifest.json | ConvertFrom-Json |
|
|
Select-Object basePackageVersion, channel, packageCount, debugPackageCount, ref, commit
|
|
```
|
|
*Completion evidence*: `basePackageVersion`, `channel`, and `ref` match
|
|
what you expect (a stable version only from a canonical `release/*`
|
|
branch; a preview version from `master` or an in-progress release
|
|
branch). `debugPackageCount` is greater than zero and less than
|
|
`packageCount` (both Release and Debug package IDs are present).
|
|
|
|
3. To prove the set is eligible for promotion **without publishing anything**,
|
|
run the exact validation `release.yml` itself performs:
|
|
```powershell
|
|
. ./.azurepipelines/package-version-policy.ps1
|
|
$manifest = Get-Content ./candidate/release-manifest.json | ConvertFrom-Json
|
|
./.azurepipelines/validate-nuget-package-set.ps1 `
|
|
-PackageDirectory ./candidate `
|
|
-ManifestPath ./candidate/current-package-set.json `
|
|
-ExpectedVersion $manifest.basePackageVersion `
|
|
-RequireDebug `
|
|
-VerifySignatures
|
|
Test-CanonicalReleaseBranchForPackageVersion `
|
|
-Ref $manifest.ref -Version $manifest.basePackageVersion
|
|
```
|
|
*Completion evidence*: the validation script succeeds with no thrown
|
|
error, and `Test-CanonicalReleaseBranchForPackageVersion` prints `True`
|
|
for a release you intend to promote.
|
|
|
|
Use the version-aware predicate rather than the branch-shape-only
|
|
`Test-CanonicalReleaseBranchRef`: `release.yml` calls both, and only this
|
|
one rejects the mismatch that a shape check cannot see - a stable `2.1.0`
|
|
candidate built from `release/2.0`. A `False` here means either the ref is
|
|
not a canonical `release/<major>.<minor>` branch, the version is not an
|
|
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. 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
|
|
script both workflows run:
|
|
```powershell
|
|
$env:GITHUB_TOKEN = (gh auth token)
|
|
./.azurepipelines/validate-preview-package-ordering.ps1 `
|
|
-ManifestPath ./candidate/current-package-set.json `
|
|
-GitHubPackagesOwner OPCFoundation
|
|
```
|
|
*Completion evidence*: the script prints that every preview-family package
|
|
ID sorts above its published versions and exits `0`. It returns early
|
|
(also `0`) when the manifest's `channel` is not `stable`, because a
|
|
preview candidate carries no synthesized `-preview.N` number to compare.
|
|
A non-zero exit lists every offending package ID and published version -
|
|
see [Failed or partial release](#failed-or-partial-release) step 2 for
|
|
how to recover.
|
|
|
|
Also prove that no version this candidate occupies is already held by a
|
|
different build, using the gate `release.yml` runs before either push:
|
|
```powershell
|
|
$env:GITHUB_TOKEN = (gh auth token)
|
|
./.azurepipelines/assert-published-packages-match.ps1 `
|
|
-PackageDirectory ./candidate `
|
|
-ManifestPath ./candidate/current-package-set.json `
|
|
-Feed 'nuget.org' -ExcludeDebugPackages
|
|
./.azurepipelines/assert-published-packages-match.ps1 `
|
|
-PackageDirectory ./candidate `
|
|
-ManifestPath ./candidate/current-package-set.json `
|
|
-Feed 'GitHub Packages'
|
|
```
|
|
*Completion evidence*: each invocation prints how many already-published
|
|
packages are byte-identical to this candidate and how many are not yet
|
|
published, and exits `0`. On a first release every package is "not yet
|
|
published"; on a recovery re-run the already-pushed ones must report as
|
|
identical. A non-zero exit means the version is taken by other bytes -
|
|
see [Failed or partial release](#failed-or-partial-release) step 3.
|
|
|
|
4. Alternatively, dry-run the actual promotion workflow itself (still no
|
|
publication occurs):
|
|
```powershell
|
|
gh workflow run release.yml --ref release/<major>.<minor> `
|
|
-f release_run_id=<run-id> -f dry_run=true
|
|
```
|
|
`--ref` is required and must name the same canonical release branch the
|
|
candidate was built from: `release.yml` refuses to run when its own
|
|
`GITHUB_REF` differs from the candidate run's branch. The job deliberately
|
|
checks out nothing until it has resolved the candidate, then performs its
|
|
one and only checkout at that run's exact commit - so no repository code
|
|
runs with the `release` environment's credentials before the workspace is
|
|
bound to the bytes being promoted, and the validation policy and promotion
|
|
code are source-bound to them even if the release branch has advanced since
|
|
the candidate was built. Without `--ref`, `gh` dispatches from the default
|
|
branch and the run stops at "Validate selected publish run".
|
|
|
|
*Completion evidence*: the run succeeds through "Validate promotion
|
|
manifest and package bytes", "Verify preview package ordering" and
|
|
"Verify already-published package identity" - the last two are read-only,
|
|
so a dry run proves the same feed-state preconditions the real promotion
|
|
will re-check; the summary reports the expected version and package count;
|
|
no "Push to nuget.org" or "Push to GitHub Packages" step ran (both are
|
|
skipped under `dry_run`).
|
|
|
|
## Approved promotion
|
|
|
|
Publishes an already-built, already-signed stable candidate to nuget.org and
|
|
GitHub Packages. nuget.org receives only non-`.Debug` package IDs (and their
|
|
symbol packages); GitHub Packages receives the complete candidate, including
|
|
`.Debug` package IDs. This is the **only** path that ever publishes a stable
|
|
release; nothing else in this repository's CI does so automatically.
|
|
|
|
**Owner**: a repository maintainer with `release` GitHub Environment
|
|
reviewer access.
|
|
|
|
**Preconditions**: [Candidate and dry run](#candidate-and-dry-run) is
|
|
complete and its manifest shows `channel: stable` and the exact expected
|
|
version; the run's source branch is the canonical `release/<major>.<minor>`
|
|
line you intend to release; the change has the required approvals.
|
|
|
|
`release.yml` re-runs `validate-preview-package-ordering.ps1` itself, before
|
|
it authenticates to any feed, so an ordering violation that appeared after
|
|
your dry run stops the promotion before the first public write rather than
|
|
midway through it. It then runs `assert-published-packages-match.ps1` against
|
|
both feeds before **and after** publication: `dotnet nuget push
|
|
--skip-duplicate` treats an existing id/version as a successful no-op without
|
|
looking at it, so the preflight proves that every version the candidate already
|
|
occupies holds this candidate's own content. The mandatory post-push check waits
|
|
for feed indexing and then proves every expected package is present with the
|
|
candidate's content, closing the race where another publisher claims an
|
|
immutable id/version between the preflight and the push. Comparison ignores the
|
|
`.signature.p7s` part,
|
|
because nuget.org repository-signs packages at ingestion and the bytes it
|
|
serves therefore legitimately differ from the bytes that were pushed;
|
|
everything else must match exactly. A version claimed by a different build
|
|
fails the promotion instead of being silently skipped.
|
|
|
|
1. Trigger the promotion for real, from the same canonical release branch the
|
|
candidate was built from:
|
|
```powershell
|
|
gh workflow run release.yml --ref release/<major>.<minor> `
|
|
-f release_run_id=<run-id> -f dry_run=false
|
|
```
|
|
2. Approve the pending deployment to the `release` environment when
|
|
prompted (in the GitHub UI, or `gh run watch <new-run-id>` from the CLI).
|
|
*Completion evidence*: the run proceeds past "Push to nuget.org" and
|
|
"Push to GitHub Packages" without error. Each step ends by reporting that
|
|
its post-push publication is byte-identical to the candidate. The nuget.org
|
|
log explicitly says it is publishing only non-Debug package IDs; the GitHub
|
|
Packages loop covers the complete candidate, including its `.Debug`
|
|
packages.
|
|
|
|
3. Confirm the non-Debug packages are 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 expected exact stable version (e.g. `2.0.0`)
|
|
appears. Do not expect any `.Debug` package ID on nuget.org; inspect
|
|
GitHub Packages for those IDs instead.
|
|
|
|
4. The successful promotion automatically creates the version tag and GitHub
|
|
Release, both bound to the exact source SHA recorded in the promotion
|
|
manifest as `commit`. On a recovery re-run, it verifies that any existing
|
|
tag and Release already point to that same candidate, and fails rather than
|
|
moving either one.
|
|
|
|
5. Record the [release handoff](#release-handoff-template) in the GitHub
|
|
Release description or the tracking issue.
|
|
|
|
6. Return to the calling procedure ([First stable release](#first-stable-release),
|
|
[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.
|
|
|
|
5. Confirm the promotion created a prerelease GitHub Release and version tag
|
|
for the exact candidate commit:
|
|
```powershell
|
|
git rev-parse 2.0.0-preview.6
|
|
gh release view 2.0.0-preview.6 --json targetCommitish,isPrerelease
|
|
```
|
|
|
|
## Failed or partial release
|
|
|
|
Recovers from an error anywhere in [Candidate and dry run](#candidate-and-dry-run)
|
|
or [Approved promotion](#approved-promotion).
|
|
|
|
**Owner**: a repository maintainer.
|
|
|
|
1. **Determine whether any public write already happened.** Check nuget.org
|
|
for a non-Debug package and GitHub Packages for the complete candidate
|
|
(including its `.Debug` package IDs):
|
|
```powershell
|
|
Invoke-RestMethod 'https://api.nuget.org/v3-flatcontainer/opcfoundation.netstandard.opc.ua.core/index.json' |
|
|
Select-Object -ExpandProperty versions
|
|
gh api '/orgs/OPCFoundation/packages/nuget/OPCFoundation.NetStandard.Opc.Ua.Core/versions' --paginate |
|
|
ConvertFrom-Json | ForEach-Object name
|
|
```
|
|
|
|
2. **If nothing published yet** (the failure was in validation, signing, or
|
|
before any `dotnet nuget push`): fix the underlying problem (a code fix
|
|
via [Routine backport](#routine-backport)/[Emergency hotfix](#emergency-hotfix),
|
|
or a version/config fix via a normal PR against the release branch), which
|
|
produces a **new** `nuget-publish.yml` run and a new candidate. Start over
|
|
at [Candidate and dry run](#candidate-and-dry-run) with that new run - do
|
|
not attempt to reuse or repair the failed one.
|
|
|
|
Two failures are specific to the preview-ordering gate, and both stop the
|
|
run before any public write:
|
|
|
|
| Symptom | Cause and safe next action |
|
|
| --- | --- |
|
|
| `validate-preview-package-ordering.ps1` lists package IDs whose published versions are not below the candidate's `-preview.N` | Ordinary `master` development published a higher `<base>-preview.{height}` while this release was being prepared. Raise `PreviewPackageBuildNumber` in `preview-version.props` above every listed version, via a normal PR against the release branch, then build and validate a **new** candidate. Never lower a published version or delete packages to make room. |
|
|
| The script throws while querying nuget.org or GitHub Packages (for example `401`/`403`, or any non-`404` error status) | The gate deliberately fails closed: it cannot prove ordering, so it refuses to let the release proceed. Do **not** bypass it. Confirm the workflow passes a token with `packages: read`/`packages: write` for the owner being queried, and that the feed is reachable, then re-run the same workflow. An unpublished package ID returns `404`, which the script treats as "nothing to compare" and allows. |
|
|
|
|
3. **If nuget.org already has some non-Debug packages, or GitHub Packages has
|
|
some expected packages** for the version (a partial push - `dotnet nuget
|
|
push` is per-package and can fail partway through): re-run
|
|
[Approved promotion](#approved-promotion) against the **same**
|
|
`release_run_id`. `--skip-duplicate` makes re-pushing the packages that
|
|
already succeeded a no-op; only the missing ones are actually written.
|
|
`.Debug` package IDs are intentionally absent from nuget.org, so their
|
|
absence there is not a partial-release symptom.
|
|
|
|
The re-run is safe because `assert-published-packages-match.ps1` runs
|
|
before and after each push. The preflight proves that every version this
|
|
candidate already occupies really holds this candidate's own bytes; the
|
|
post-push check proves that every package now present is still this
|
|
candidate, including a duplicate accepted by `--skip-duplicate` (see the
|
|
gate table below). Because that proof exists,
|
|
`validate-preview-package-ordering.ps1` deliberately does **not** treat
|
|
the candidate's own already-published `-preview.N` as a violation -
|
|
otherwise the recovery in this step would be unreachable. Any *higher*
|
|
or more-qualified preview version still blocks.
|
|
|
|
Never build a new candidate for a version that already has *any* packages
|
|
published - that would risk two different byte sequences under the same
|
|
immutable version. Investigate why the previous attempt stopped before
|
|
retrying.
|
|
|
|
| Symptom | Cause and safe next action |
|
|
| --- | --- |
|
|
| `assert-published-packages-match.ps1` reports a feed that "already serves ... with different content" | A different build permanently claimed this candidate's immutable version. NuGet versions cannot be overwritten, so this candidate can never be published under it. Do **not** bypass the gate. Move to step 4 and release a new version. |
|
|
| The script throws while downloading a package for comparison (any non-`404` error status) | It fails closed: it cannot decide whether the version is taken, so it refuses to push. Confirm feed reachability and the token, then re-run. An unpublished version returns `404`, which the script treats as "nothing to prove" and allows. |
|
|
|
|
4. **If the version needs to change entirely** (e.g. the approved candidate
|
|
turned out to be wrong before any push happened): do not reuse the
|
|
version number. Treat this as returning to the relevant release procedure
|
|
([First stable release](#first-stable-release)/[Patch release](#patch-release)/[Minor release](#minor-release))
|
|
with a corrected `version.json`, producing a fresh candidate for a
|
|
version nothing has ever published under.
|
|
|
|
5. **If you cannot determine what happened** (for example the workflow log
|
|
is inconclusive about whether a push completed): stop and escalate to
|
|
another maintainer before taking any further action. Never guess at a
|
|
feed's state by re-running a push blind - check it first as in step 1.
|
|
|
|
## Post-release verification
|
|
|
|
Confirms a completed [Approved promotion](#approved-promotion) left the
|
|
repository and both feeds consistent.
|
|
|
|
1. Confirm nuget.org has the exact expected non-Debug version and GitHub
|
|
Packages has the complete candidate (repeat
|
|
[Approved promotion](#approved-promotion) step 3 for GitHub Packages too):
|
|
```powershell
|
|
gh api '/orgs/OPCFoundation/packages/nuget/OPCFoundation.NetStandard.Opc.Ua.Core/versions' --paginate |
|
|
ConvertFrom-Json | ForEach-Object name | Select-Object -Last 3
|
|
```
|
|
2. Confirm the release tag and GitHub Release point at the exact commit
|
|
recorded in the promotion manifest:
|
|
```powershell
|
|
git rev-parse 2.0.0
|
|
gh release view 2.0.0 --json targetCommitish
|
|
```
|
|
3. Confirm the preview-only families sorted above every previously published
|
|
preview for the same base version (spot-check at least one, e.g.
|
|
XRegistry):
|
|
```powershell
|
|
Invoke-RestMethod 'https://api.nuget.org/v3-flatcontainer/opcfoundation.netstandard.opc.ua.xregistry/index.json' |
|
|
Select-Object -ExpandProperty versions | Select-Object -Last 5
|
|
```
|
|
4. Confirm the release branch was advanced to the next servicing preview
|
|
version (the final step of [First stable release](#first-stable-release)/[Patch release](#patch-release)/[Minor release](#minor-release)):
|
|
```powershell
|
|
git show origin/release/2.0:version.json
|
|
```
|
|
5. Confirm the stable container image aliases (if this release includes
|
|
image-tagged samples - see [Container support](ContainerReferenceServer.md))
|
|
now point at the released commit. For the newest line expect `latest`,
|
|
`release`, `<major>.<minor>` and the exact version; for a superseded
|
|
maintenance line expect `latest-<major>.<minor>`, `<major>.<minor>` and
|
|
the exact version, with `latest`/`release` still on the newer minor (see
|
|
[Branch and channel model](#branch-and-channel-model)).
|
|
6. Confirm every backport tracked for this release (see
|
|
[Routine backport](#routine-backport)/[Emergency hotfix](#emergency-hotfix))
|
|
shows as merged on every line it was required for.
|
|
|
|
*Completion evidence*: all of the above match; if any do not, treat it as a
|
|
[failed or partial release](#failed-or-partial-release) and escalate rather
|
|
than silently continuing.
|
|
|
|
## Release handoff template
|
|
|
|
Record this in the GitHub Release description, the tracking issue, or the
|
|
release PR - not in an untracked local note - whenever a release is prepared,
|
|
promoted, or handed off between people (or between an agent and a
|
|
maintainer).
|
|
|
|
```markdown
|
|
## Release handoff: <version>
|
|
|
|
- Release branch: release/<major>.<minor>
|
|
- Source commit: <sha>
|
|
- Candidate run: nuget-publish.yml run <run id>
|
|
- Promotion run: release.yml run <run id> (or: not yet promoted)
|
|
- Approvals: <who approved the release environment deployment>
|
|
- Backports included: <PR links, or "none required">
|
|
- Gates completed: [ ] candidate validated [ ] dry run [ ] promoted
|
|
[ ] tagged [ ] GitHub Release published
|
|
[ ] next preview version PR opened
|
|
- Blockers: <none, or describe>
|
|
- Next allowed action: <e.g. "approve release.yml run 12345">
|
|
```
|
|
|
|
## Historical `release/2.0.0` branch
|
|
|
|
The three-component `release/2.0.0` branch predates this release model and is
|
|
retired: `version.json`'s `publicReleaseRefSpec` no longer matches that
|
|
naming, so it can never again produce a `channel: stable` package set, and
|
|
`nuget-publish.yml`/`release.yml` both refuse to publish from or promote it.
|
|
It is kept, unmodified, as historical record of the earlier `2.0.0-preview.*`
|
|
line; do not delete it, force-push to it, or attempt to bring it back into
|
|
the maintained-line model above. All current and future `2.0.x` work happens
|
|
on `release/2.0`.
|
|
|
|
## Maintaining this document
|
|
|
|
Update this file, and the worked examples above, in the same pull request as
|
|
any change to `version.json`, `version.props`, `version.targets`,
|
|
`preview-version.props`, `.azurepipelines/package-version-policy.ps1`,
|
|
`.azurepipelines/validate-nuget-package-set.ps1`,
|
|
`.azurepipelines/validate-preview-package-ordering.ps1`,
|
|
`.azurepipelines/assert-published-packages-match.ps1`,
|
|
`.github/workflows/nuget-publish.yml`, `.github/workflows/release.yml`, or
|
|
the Docker/container tagging workflows. A release procedure that no longer
|
|
matches the scripts it describes is worse than no documentation at all.
|