Skip to content

[ci] Enable non-workload NuGet releases - #37658

Merged
kubaflo merged 11 commits into
mainfrom
jonathanpeppers-release-android-libraries
Sep 3, 2026
Merged

kubaflo merged 11 commits into
mainfrom
jonathanpeppers-release-android-libraries

Conversation

@jonathanpeppers

@jonathanpeppers jonathanpeppers commented Aug 19, 2026 •

Copy link
Copy Markdown
Member

Note

Are you waiting for the changes in this PR to be merged?
It would be very helpful if you could test the resulting artifacts from this PR and let us know in a comment if this change resolves your issue. Thank you!

Description of Change

Generalizes the existing Darc-based official release pipeline so it can publish shipping NuGet packages from allowlisted non-workload repositories, initially dotnet/android-libraries, while preserving the existing MAUI workload-release path.

  • Keeps the existing free-text repository and commit inputs.
  • Infers workload behavior for dotnet/android, dotnet/macios, and dotnet/maui.
  • Preserves the existing first-match BAR resolution behavior for all repositories, then validates the resolved repository and gathers using the dynamically returned BAR build ID.
  • Keeps workload gathering, workload-set promotion, pack-before-manifest ordering, approvals, and publishing behavior isolated from the non-workload path.
  • Gathers only shipping NuGet assets and rejects malformed packages, duplicate filenames, duplicate ID/version identities, unexpected packages, and workload manifests on non-workload releases.
  • Supports include/exclude package filters for both workload and non-workload releases.
  • Filters exact ID/version pairs already on NuGet.org during preparation and again immediately before publishing, preventing reruns and packages published between those phases from causing HTTP 409 conflicts.
  • Supports repeating the same non-workload BAR release after partial publication: Darc may no longer download assets whose BAR location moved to NuGet.org, so missing selected assets are accepted only when their exact identity is confirmed on NuGet.org. Other missing or unavailable packages fail closed.
  • Publishes the exact selected and staged package lists as the NuGetReleaseAudit artifact before manual approval.
  • Uses the existing 1ES.PublishNuget@1 task and nuget.org (dotnetframework) service connection, then verifies every expected package on NuGet.org.
  • Supports a non-mutating dry run with pushPackages: false.

Dry-run inputs

ghOwner: dotnet
ghRepo: android-libraries
commitHash: <ANDROID_LIBRARIES_COMMIT_WITH_BAR_BUILD>
pushWorkloadSet: false
pushNugetOrg: true
pushPackages: false

Queue dotnet-maui-release against this PR branch after it appears in the internal dotnet-maui mirror. The run performs BAR resolution, gathering, package validation/filtering, and audit artifact publication without promotion or NuGet publishing.

External setup

Before enabling a real push, release owners must verify that the existing nuget.org (dotnetframework) service connection owns every package ID in the audit artifact and has sufficient scope/quota. Android-libraries BAR builds contain per-build deltas rather than the union of channel assets, so each pending BAR build must be released in commit order.

Validation

  • Successful dotnet/android-libraries NuGet.org release run: BAR resolution, gathering, filtering, audit publication, package push, and NuGet.org verification all succeeded.
    • The overall build is partiallySucceeded only because 1ES production branch/source validation reported non-blocking errors for running the test from this PR branch instead of an approved production branch.
  • Focused release pipeline and NuGet availability Pester suites.
  • YAML parsing and whitespace validation.

Issues Fixed

N/A

Generalize the Darc-based official release pipeline to publish shipping NuGet packages for allowlisted non-workload repositories while preserving inferred MAUI workload behavior.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

Copilot-Session: 2933e733-3bed-4524-ae14-f7ad1c7959e6
@github-actions

Copy link
Copy Markdown
Contributor

🚀 Dogfood this PR with:

⚠️ WARNING: Do not do this without first carefully reviewing the code of this PR to satisfy yourself it is safe.

curl -fsSL https://raw.githubusercontent.com/dotnet/maui/main/eng/scripts/get-maui-pr.sh | bash -s -- 37658

Or

  • Run remotely in PowerShell:
iex "& { $(irm https://raw.githubusercontent.com/dotnet/maui/main/eng/scripts/get-maui-pr.ps1) } 37658"

@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines:
Successfully started running 1 pipeline(s).
There may be pipelines that require an authorized user to comment /azp run to run.

@jonathanpeppers

Copy link
Copy Markdown
Member Author

I will need to manually test this and bring it out of draft. I also need to merge this first:

Avoid strict-mode failures when non-workload releases use the default skip values for all workload-only filters.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

Copilot-Session: 2933e733-3bed-4524-ae14-f7ad1c7959e6
Align non-workload releases with workload releases by removing packages that reached NuGet.org after preparation and before the publish task.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

Copilot-Session: 2933e733-3bed-4524-ae14-f7ad1c7959e6
Permit the pre-publish recheck to process package files removed during preparation, while still failing when a missing file is not available on NuGet.org.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

Copilot-Session: 2933e733-3bed-4524-ae14-f7ad1c7959e6
Apply the existing include and exclude package filters to non-workload NuGet release sets while continuing to reject workload-only recovery inputs.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

Copilot-Session: 2933e733-3bed-4524-ae14-f7ad1c7959e6
Comment thread eng/pipelines/ci-official-release.yml Outdated
Resolve the requested commit to exactly one matching BAR build and use its returned ID for gathering without imposing an Android-libraries channel requirement.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

Copilot-Session: 2933e733-3bed-4524-ae14-f7ad1c7959e6
Allow Darc to continue when previously released assets are no longer downloadable, then require every missing selected identity to exist on NuGet.org while preserving fail-fast workload gathering.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

Copilot-Session: 2933e733-3bed-4524-ae14-f7ad1c7959e6
@kubaflo

This comment has been minimized.

@github-actions github-actions Bot added the s/agent-review-in-progress AI review is currently running for this PR label Aug 30, 2026

@MauiBot MauiBot left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Expert Review — 4 findings

See inline comments for details.

Comment thread eng/pipelines/ci-official-release.yml Outdated
Comment thread eng/pipelines/ci-official-release.yml
Comment thread eng/pipelines/ci-official-release.yml
Comment thread eng/pipelines/common/non-workload-publish.yml Outdated

@MauiBot MauiBot left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AI Review Summary

@jonathanpeppers — new AI review results are available based on commit adec5b8.

Gate No Tests Confidence Low Platform Windows


🗂️ Review Sessions — click to expand
🚦 Gate — Test Before & After Fix

Gate Result: ⚠️ SKIPPED

No tests were detected in this PR.

Recommendation: Add tests to verify the fix using the write-tests-agent.


📋 Pre-Flight — Context & Validation

PR #37658 Pre-Flight

Context

  • Title: [ci] Enable non-workload NuGet releases
  • State: Open draft
  • Base / head: main (f578901012885b2845763e1587f7c230b25b0e8e) / jonathanpeppers-release-android-libraries (adec5b8d7230acaf8996a0961915a5f2fdc97f7a)
  • Local review commit: ec075411c8531566cbfd0155af8a7f2ed5e322ba, a squash of the PR onto abdfbef0e91622ef4b1cceb4897c236f7452f428
  • Scope: 7 files, +462/-93. The PR generalizes the Darc-based official release pipeline to publish shipping NuGet packages from allowlisted non-workload repositories, initially dotnet/android-libraries, while retaining the workload path for dotnet/android, dotnet/macios, and dotnet/maui.
  • Linked issue: None. The author notes that a real pipeline dry run is still required and depends on dotnet/android-libraries#1530.
  • Gate: Previously completed and skipped because no tests were detected. It must not be rerun and gate/content.md must not be changed.

Existing PR approach

The PR infers workload mode from the requested owner/repository, validates an allowlist, resolves exactly one BAR build by repository and commit, and gathers shipping NuGet assets by the returned BAR ID. Workload packages retain the existing release path. Non-workload packages are selected against BAR asset identities, filtered against NuGet.org, staged with an audit artifact, approved manually, published through 1ES.PublishNuget@1, and verified afterward. The publish stage is factored into a new eng/pipelines/common/non-workload-publish.yml template.

An earlier hard-coded .NET 10 channel name/ID approach was removed after review. Alternatives must not reintroduce repository-specific channel IDs and must remain distinct from the current “single BAR build + separate non-workload publish template” design.

Changed files

  • .github/workflows/powershell-script-tests.yml
  • docs/ReleaseProcess.md
  • eng/pipelines/ci-official-release.Tests.ps1
  • eng/pipelines/ci-official-release.yml
  • eng/pipelines/common/non-workload-publish.yml (new)
  • eng/scripts/nuget_release_packages.Tests.ps1
  • eng/scripts/nuget_release_packages.ps1

The working tree already contains harness/trusted-script overlays, including modifications to the two nuget_release_packages files. These pre-existing changes must not be manually reverted or overwritten. Each try-fix invocation must rely exclusively on EstablishBrokenBaseline.ps1 and its baseline allow-list.

Focused verification

The PR adds/updates two focused Pester suites. Use only these suites; do not run the repository-wide PowerShell test directory or any full test suite:

pwsh -NoProfile -Command '$ErrorActionPreference = "Stop"; Import-Module Pester -RequiredVersion 5.9.0; $config = New-PesterConfiguration; $config.Run.Path = @("eng/pipelines/ci-official-release.Tests.ps1", "eng/scripts/nuget_release_packages.Tests.ps1"); $config.Run.PassThru = $true; $config.Output.Verbosity = "Detailed"; $result = Invoke-Pester -Configuration $config; if ($result.FailedCount -gt 0 -or $result.TotalCount -lt 1) { throw "Focused Pester validation failed." }'

No additional mandatory regression tests were supplied. The target platform is Windows.

Try-fix constraints

Each attempt may perform one implementation/test pass and at most one focused correction/retest. It must perform the try-fix skill's inline expert self-review, capture all required artifacts, and restore only with:

pwsh .github/scripts/EstablishBrokenBaseline.ps1 -Restore

The PR adds eng/pipelines/common/non-workload-publish.yml. If .github/.baseline-state.json reports this or any other path in NewFiles, the try-fix skill requires the attempt to be recorded as Blocked before editing because the restore mechanism cannot safely restore added production files.


🔬 Code Review — Deep Analysis

Expert Code Review - PR #37658

Initial Verdict: NEEDS_CHANGES

Confidence: low for overall merge safety because this changes shared production-release infrastructure and the trusted Gate was skipped; confidence is high in the blocking BAR-resolution finding.

Independent Assessment

What this changes: The submitted PR generalizes the official Darc-based release pipeline so allowlisted non-workload repositories can publish one audited set of shipping NuGet packages, while retaining separate workload pack/manifest publication. It resolves a repository and commit to BAR, gathers shipping assets, validates identities, filters packages already on NuGet.org, publishes through 1ES.PublishNuget@1, and verifies availability.

Inferred motivation: dotnet/android-libraries needs a controlled release path without pretending its packages are a MAUI workload or weakening the existing service-connection and approval boundaries.

Approach assessment: The non-workload path is generally coherent and fail-closed. Its allowlist, immutable pipeline artifact, script hash checks, manifest-to-directory validation, repeat availability filtering, and missing-file/HTTP-404 guard preserve the intended release set. However, changing the shared workload lookup from selecting one returned BAR build to rejecting every multi-match result introduces a blocking operational regression.

Prior Review Reconciliation

Prior feedback Status Evidence
The initial repository-specific channel ID/name was too specific. Addressed The submitted diff removes the fixed channel policy and resolves the requested repository and commit dynamically.
The repository allowlist is intentional. Confirmed The runtime input is normalized and checked against four explicit GitHub repository URLs before Darc runs.

No prior error-severity finding remains to reconcile.

CI and Gate

  • Public head adec5b8d7230acaf8996a0961915a5f2fdc97f7a reported 32 successful checks, one skipped check, and no failing checks when reviewed.
  • The trusted Gate result supplied to this phase is SKIPPED - no tests detected in this PR; it must not be treated as approval evidence.
  • The submitted focused Pester tests cover the helper's missing-file behavior, but they do not exercise a real Azure DevOps release expansion or BAR query.

Findings

Error - exact-one BAR lookup regresses workload release recovery

eng/pipelines/ci-official-release.yml:239 now rejects a repository/commit query unless Darc returns exactly one BAR build. This is shared by the established workload path. Re-queued official builds can register more than one BAR build for the same source commit, so a retry can now stop before preparation with no operator-selectable build ID. Preserve deterministic selection without making an otherwise valid workload release impossible, preferably with an explicit BAR build override rather than silently choosing an arbitrary match.

Warning - missing BAR GitHub metadata produces an opaque runtime failure

eng/pipelines/ci-official-release.yml:242 calls GetLeftPart on the cast gitHubRepository value without first checking it. An AzDO-only BAR record can therefore fail with a null-method error rather than a diagnostic naming the BAR build and expected repository. Add an explicit null/empty guard while retaining fail-closed behavior.

Suggestion - synthesized identities assume BAR versions are NuGet-normalized

eng/pipelines/ci-official-release.yml:368-369 uses the raw BAR version as both normalizedVersion and part of fileName when a package was not downloaded. A non-canonical but equivalent NuGet version can make the flat-container availability probe and package filters diverge from the downloaded-package path. Normalize the BAR version before synthesizing the identity, or establish and test the BAR contract that guarantees it is already normalized.

Suggestion - HasPackages is calculated but does not suppress a no-op approval

eng/pipelines/common/non-workload-publish.yml:23 binds HasPackages but never consumes it. A fully published release still waits at ManualValidation@0 before doing no publish work. Either gate the approval/stage with this output or remove the misleading unused variable.

Failure-Mode Probing

  • A non-workload gather that misses an unpublished package fails: the manifest retains the selected BAR identity and FilterExisting throws when both the file is absent and NuGet.org returns 404.
  • A package published between preparation and approval is removed by the second filter and is not sent to 1ES.PublishNuget@1.
  • A corrupt or unexpected gathered package fails identity/archive validation or the manifest-directory consistency check.
  • A workload manifest in the non-workload path is rejected before artifact publication.
  • Two BAR builds for one workload repository/commit fail unconditionally at the new exact-one guard, which is the blocking regression.

External Output Contract

Consumer token/pattern Producer location Producer emission condition Consumer assumption Ordinary negative case Downstream effect
Darc JSON result count darc get-build --output-format json Emits every build matching repository and commit More than one match is invalid A legitimate official rebuild registers a second BAR build for the same commit Preparation aborts and no workload release can be selected
Darc process exit code darc gather-drop --continue-on-error Nonzero when one or more gather operations fail Non-workload misses may be tolerated only if every selected missing identity is already public A partial gather has ordinary missing assets The manifest plus NuGet.org probes either account for every missing identity or fail closed
HTTP 200/404 NuGet.org flat-container HEAD 200 for an indexed normalized ID/version, 404 when unavailable 200 means safe to omit; 404 requires a staged file A package was published after preparation The second filter removes it and the publish task avoids a conflict

Verdict

NEEDS_CHANGES. The new non-workload design is careful overall, but the exact-one lookup changes the established workload release path and can strand a routine release retry. The null diagnostic should be fixed in the same resolution block; version normalization and no-op approval handling are worthwhile hardening opportunities.


🛠️ Try-Fix — Analysis & Comparison

Try-Fix Candidate 1 — PR #37658

Result: Blocked (no implementation, no test)
Attempt artifacts: CustomAgentLogsTmp/PRState/37658/PRAgent/try-fix/attempt-1

Approach (designed, not implemented)

Asset-first, single-path publish driven by a declarative release manifest.

  1. eng/scripts/nuget_release_packages.ps1 would emit a declarative release manifest — repo, BAR build id(s), per-package identity/version, shipping classification, NuGet.org presence — produced from a darc asset-level query over the build drop, instead of returning a single resolved build that downstream YAML re-interprets. The asset set, not the build, becomes the unit of resolution.
  2. Repository eligibility becomes data, not control flow: the allowlist and per-repo release semantics (workload vs packagesOnly) live in pipeline parameters/variable-group data consumed by ci-official-release.yml. Adding dotnet/android-libraries (or the next repo) is a manifest edit, not a new code branch. No repository-specific channel id/name policy is reintroduced — there are no channel constants at all.
  3. ci-official-release.yml keeps exactly one publish stage that iterates manifest rows. Workload rows additionally run the existing workload-insertion steps; package rows skip them. eng/pipelines/common/non-workload-publish.yml is unnecessary because staging audit artifact, manual approval, 1ES.PublishNuget@1 publish, and post-publish verification are shared.

Exact BAR repository/commit resolution, auditable staging, manual approval, NuGet publishing, and post-publish verification are all preserved; the workload release path is untouched in substance.

Prior approach avoided

  • The PR's current design: infer workload mode from requested owner/repo, validate an allowlist, resolve exactly one BAR build by repository + commit, gather shipping assets by that BAR id, then hand off non-workload releases to a dedicated non-workload-publish.yml template. Failure mechanism: duplication of the release contract. Publish semantics now exist in two places selected by a boolean computed deep in script logic, so future divergence between the two paths (a missing audit artifact, a skipped verification step) is invisible to the focused Pester suites, which can only assert YAML/script shape rather than cross-path agreement.
  • The earlier, already-removed design: hard-coded .NET 10 channel name/ID policy. Failure mechanism: release-cycle-specific policy embedded in repository code, which goes stale each band and requires a code change per release. Explicitly not reintroduced.

Mechanism-level difference

The PR resolves a build and then decides which pipeline path to take — divergence risk is created at the instant the workload boolean is computed, and it propagates into a second template. In the candidate approach, resolution yields a list of classified assets, and the boolean never selects a code path; it only gates whether the extra workload-insertion steps run inside the single publish stage. Because there is exactly one staging → approval → publish → verify implementation, a non-workload release structurally cannot miss a step the workload path has. That is a different root-cause hypothesis (the risk is contract duplication, not resolution granularity), not merely a different file location.

Files changed

None. The attempt was blocked before any edit.

Captured diff

Empty (fix.diff is 0 bytes; reviewer-findings.diff is 0 bytes). No tracked or untracked file was created, modified, or deleted by this attempt. All ~57 pre-existing dirty harness/trusted-script files were preserved untouched.

Test result

No test executed. The permitted focused command
pwsh -NoProfile -Command '... Invoke-Pester -Configuration $config ...' over
eng/pipelines/ci-official-release.Tests.ps1 and eng/scripts/nuget_release_packages.Tests.ps1 was intentionally not run: with no implementation applied it would have measured the unmodified harness-overlay state, producing a meaningless result. test-output.log records this reason verbatim.

Failure / block analysis

Step 2 (Establish Baseline) failed. pwsh .github/scripts/EstablishBrokenBaseline.ps1 exited 1 with:

ERROR: DIRTY WORKING DIRECTORY - Cannot establish baseline
...
EstablishBrokenBaseline.ps1 failed: Working directory is not clean. Clean up before establishing baseline.

It listed ~57 pre-existing modified files under .github/scripts, .github/skills, and eng/scripts — including both eng/scripts/nuget_release_packages.ps1 and eng/scripts/nuget_release_packages.Tests.ps1, i.e. two of the seven target files. As a result .github/.baseline-state.json was never created (Test-Path → False), so there is no RevertedFiles allow-list.

The try-fix baseline-file boundary rule permits editing only files in RevertedFiles and mandates reporting Blocked before editing when the state file is absent, because nothing could be safely restored afterward. The block is unresolvable from inside the attempt: the pre-existing dirty files must be preserved, and every cleanup mechanism that would satisfy the script's guard (git checkout/restore/reset/clean/stash, Remove-Item) is prohibited. The pre-flight brief separately anticipated a Blocked outcome via NewFiles containing the PR's added eng/pipelines/common/non-workload-publish.yml; that check was never reached because the dirty-tree guard fired first.

Result

Blocked — recorded in result.txt.

Self-review finding count

0 findings (reviewer-findings.json = []). The inline expert self-review was performed against .github/agents/maui-expert-reviewer.md (Overarching Principles + always-active dimensions: Logic and Correctness, Regression Prevention, Complexity Reduction). With an empty diff there is no changed code to violate any rule, so [] is the correct and honest output. No maui-expert-reviewer sub-agent was launched.

Restoration outcome

Ran the exact mandated command and waited for completion:

pwsh .github/scripts/EstablishBrokenBaseline.ps1 -Restore
→ No baseline state found. Nothing to restore.
→ Message: No baseline state found / Restored: False

Restored False is the expected and accepted result for this verified no-state Blocked path: baseline state was never created and no attempt edits were made. The worktree is byte-identical to its pre-attempt state; all pre-existing dirty/untracked harness inputs remain in place. No git history-altering or filesystem-deletion command was used at any point.

Try-Fix Candidate 2 — PR #37658

Result: Blocked (no implementation, no test)
Attempt artifacts: CustomAgentLogsTmp/PRState/37658/PRAgent/try-fix/attempt-2

Approach (designed, not implemented)

Approval-bound content-addressed release capsule. For an allowlisted non-workload repository, preparation would resolve the exact BAR build by normalized repository plus commit, select shipping NuGet assets, and seal the packages with BAR provenance and per-file SHA-256 hashes into one immutable capsule. Manual approval would review that capsule's audit. The production release job would independently repeat the Darc repository/commit lookup, require the same BAR ID, verify every package hash, extract into a fresh directory, publish via 1ES.PublishNuget@1, and verify every approved identity/version on NuGet.org. Existing workload stages remain unchanged. The non-workload capsule job would be inline in ci-official-release.yml, not a separate template and not part of a unified workload/package stage.

Prior approaches avoided

  • Current PR: one BAR resolution during preparation followed by a dedicated non-workload-publish.yml path. Its handoff authenticates the helper script but does not cryptographically bind every approved package byte plus BAR provenance to the production job.
  • Candidate 1: an asset-first declarative repository manifest driving one unified publish stage. It was blocked before editing; this candidate does not use a repository manifest or unify workload and non-workload publication.
  • Removed historical design: repository-specific .NET 10 channel names/IDs. This candidate uses no channel names or IDs.

Mechanism-level difference

The root-cause hypothesis is an approval-to-publish time-of-check/time-of-use gap, rather than the PR's release-path split or candidate 1's contract-duplication concern. Approval is bound to content hashes and BAR provenance. A second independent Darc lookup proves that repository/commit still maps to the approved BAR ID, while package hashes prove that the exact reviewed bytes enter 1ES.PublishNuget@1. This adds an independently checked provenance boundary without merely relocating checks, without a separate publish template, and without merging both release types into one stage.

Files changed

None. Baseline establishment failed before editing, so no production target was modified.

Full captured diff

The captured fix.diff is empty (0 bytes).

Exact test result and output summary

No test executed. test-output.log records:

BLOCKED BEFORE IMPLEMENTATION OR TESTING. The mandatory baseline command failed because approximately 57 pre-existing harness/trusted-script files are modified. .github/.baseline-state.json was not created, so no RevertedFiles modification allow-list exists. Per the try-fix baseline boundary, no production file could be edited safely. Running the focused Pester command without an implementation would test the pre-existing overlay rather than candidate 2 and was therefore not permitted or meaningful. No test command was executed.

The sole permitted focused Pester command was not invoked, so there is no Pester pass/fail count.

Failure / block analysis

pwsh .github/scripts/EstablishBrokenBaseline.ps1 exited 1 after rejecting approximately 57 pre-existing modified harness/trusted-script files, including eng/scripts/nuget_release_packages.ps1 and eng/scripts/nuget_release_packages.Tests.ps1. .github/.baseline-state.json did not exist afterward. With no RevertedFiles allow-list, the skill required a Blocked result before editing. The pre-existing modifications could not be cleaned because they must be preserved and manual git/filesystem cleanup is prohibited.

Result

Blocked — recorded in result.txt.

Self-review finding count

0 findings. The mandatory inline expert self-review applied all overarching principles and the always-active Logic and Correctness, Regression Prevention, and Complexity Reduction dimensions. Since candidate 2 produced no code diff, reviewer-findings.json correctly contains []. No reviewer or child agent was launched.

Restoration outcome

The exact required command ran and completed:

pwsh .github/scripts/EstablishBrokenBaseline.ps1 -Restore
No baseline state found. Nothing to restore.
Message: No baseline state found
Restored: False

Restored False is accepted for this verified no-state path: baseline state was never created and no attempt production edits occurred. All pre-existing dirty and untracked inputs remain untouched.


🏁 Report — Final Recommendation

⚠️ Final Recommendation: REQUEST CHANGES

Winning candidate: pr-plus-reviewer

The submitted PR has a sound non-workload release design, but its exact-one BAR lookup also changes the established workload path: a repository/commit with multiple BAR builds cannot be released. pr-plus-reviewer retains strict default resolution while adding an explicit positive barBuildId disambiguator that must match the same repository/commit query. It also adds a clear invalid/missing gitHubRepository diagnostic and uses the existing HasPackages output to skip a production stage and manual approval when preparation proves there is nothing to publish.

Candidate ranking

Rank Candidate Implementation Required validation Assessment
1 pr-plus-reviewer Complete consolidated patch Passed: 25/25 focused Pester tests Best balance of release safety, backward compatibility, operator recovery, and limited scope.
2 pr Complete submitted fix Public CI was green; trusted Gate was skipped Strong fail-closed package handling, but the shared exact-one lookup can strand ordinary workload release retries and repository metadata failures are opaque.
3 try-fix-2 Blocked; design only Not run The content-addressed capsule could strengthen provenance, but it produced no diff or empirical evidence and duplicates resolution in preparation and publication.
4 try-fix-1 Blocked; design only Not run The unified declarative-manifest redesign is substantially broader than the problem, produced no diff, and replaces mature workload stages without validation.

No candidate failed a regression test. The two try-fix-* candidates were blocked before implementation and testing, so neither can outrank an implemented candidate whose required regression suite passed.

Expert findings applied

  1. Blocking BAR ambiguity: Added optional barBuildId input. The default still requires exactly one result; an override is parsed as a positive integer and must uniquely match the builds returned for the requested repository and commit. This avoids silently selecting an unrelated build.
  2. Repository metadata diagnostics: Replaced the null-sensitive URI chain with Uri.TryCreate and a BAR-ID-specific fail-closed error.
  3. No-op manual approval: Gated publish_non_workload on prepareRelease.HasPackages, so an already-complete release does not wait for approval or request production publication.
  4. Submitted helper behavior retained: Restored the PR's missing-file logic and its two tests over the trusted baseline overlay: missing packages are accepted only when NuGet.org returns 200 and rejected when it returns 404.

The reviewer suggestion to normalize synthesized BAR versions was not added because this pass did not establish that BAR package asset versions can be non-normalized or identify an existing NuGet normalization API available in the release PowerShell host. The risk remains documented rather than adding an unproven custom SemVer implementation.

Validation and residual uncertainty

The exact required Windows command ran once from D:\a\_temp\pr-37658-pr-plus-reviewer and passed all 25 focused tests with zero failures or skips. git diff --check cf9e410bc389a32f9a302e1bda6f165d0ea448c2 also passed before validation.

This remains production-release infrastructure. The focused suite verifies PowerShell behavior and YAML shape, but it does not expand the pipeline in Azure DevOps, query a live multi-build BAR result, exercise the protected NuGet service connection, or perform the documented dry run. The trusted Gate was skipped because it detected no tests, so these gaps must remain explicit.

Required change

Port the BAR-disambiguation, repository-validation, stage-gating, tests, and documentation hunks from CustomAgentLogsTmp/PRState/37658/PRAgent/pr-plus-reviewer/reviewer.patch, then complete the documented internal YAML-preview and dry-run checks before a production release. The helper-script hunks in that patch only reconstruct behavior already present in the submitted PR over the trusted validation overlay and should not be applied a second time. Because the winning behavior is not present in the submitted PR HEAD, the recommendation is REQUEST CHANGES.


🧭 Next Steps — reviewer changes required

The reviewer-enhanced candidate identified changes that are not yet in the submitted PR.

Why: pr-plus-reviewer preserves the submitted non-workload release flow while adding deterministic BAR-build disambiguation, clear repository metadata validation, and no-op approval suppression. It is the only alternative with an implementation, and all 25 required focused Pester tests passed.

Address the actionable findings in this review before merging.

@MauiBot MauiBot added s/agent-changes-requested AI agent recommends changes - found a better alternative or issues s/agent-fix-win AI found a better alternative fix than the PR s/agent-reviewed PR was reviewed by AI agent workflow (full 4-phase review) and removed s/agent-review-in-progress AI review is currently running for this PR labels Aug 30, 2026
Use the first BAR build returned for repository and commit releases across workload and non-workload paths, and remove the unused non-workload package output.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

Copilot-Session: 2933e733-3bed-4524-ae14-f7ad1c7959e6
Prevent AzureCLI@2 from failing non-workload preparation after a tolerated partial gather and successful package reconciliation.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

Copilot-Session: 2933e733-3bed-4524-ae14-f7ad1c7959e6
@jonathanpeppers
jonathanpeppers marked this pull request as ready for review September 2, 2026 19:44
Copilot AI lite review requested due to automatic review settings September 2, 2026 19:44
@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines:
Successfully started running 1 pipeline(s).
There may be pipelines that require an authorized user to comment /azp run to run.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

The updated release documentation still describes the preparation gather as “fail-fast” even though non-workload releases now use a continue-on-error gather path, which could mislead release operators.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Pull request overview

This PR generalizes the existing official release pipeline so it can publish shipping NuGet packages from an allowlisted non-workload repository (initially dotnet/android-libraries), while keeping the existing MAUI/workload release flow intact.

Changes:

  • Extends eng/pipelines/ci-official-release.yml to support a non-workload prepare + publish path (including audit artifact creation and package filtering).
  • Updates nuget_release_packages.ps1 (and adds tests) to tolerate packages that are already on NuGet.org even if their .nupkg file is no longer present locally.
  • Adds a dedicated non-workload publish template, updates Pester tests, and refreshes the documented release process.
File summaries
File Description
eng/scripts/nuget_release_packages.Tests.ps1 Adds Pester coverage for the new “already published but file missing” and “unpublished + missing file” behaviors.
eng/scripts/nuget_release_packages.ps1 Adjusts FilterExisting to not require a local .nupkg when NuGet reports the package is already published.
eng/pipelines/common/non-workload-publish.yml Introduces a non-workload publish stage with manual approval, pre-push filtering, push, and verification.
eng/pipelines/ci-official-release.yml Adds inferred workload vs non-workload branching, allowlisting, non-workload audit emission, and wires in the new publish template.
eng/pipelines/ci-official-release.Tests.ps1 Expands pipeline Pester assertions to cover inference, audit, non-workload template wiring, and gather behavior.
docs/ReleaseProcess.md Documents non-workload releases, audit artifact behavior, and updated parameter semantics.
.github/workflows/powershell-script-tests.yml Adds the new YAML template to the PowerShell regression-test path filters.
Review details
  • Files reviewed: 7/7 changed files
  • Comments generated: 1
  • Review effort level: Lite

Comment thread docs/ReleaseProcess.md Outdated

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

The tests do not explicitly protect the new repository allowlist entry, and the release documentation incorrectly describes non-workload gathering as fail-fast.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Review details

Suppressed comments (1)

docs/ReleaseProcess.md:71

  • This description contradicts the non-workload behavior documented above and implemented with --continue-on-error: only workload gathering is fail-fast, while non-workload gathering can return failure and then validates every missing selected identity against NuGet.org. Please distinguish those paths so operators do not expect all gather failures to stop preparation immediately.
The preparation stage resolves the BAR build once and runs one fail-fast `darc gather-drop`, filtered to BAR NuGet package assets. Symbol and other path-based blob assets are not NuGet.org release inputs and are not downloaded. The stage rejects a failed or incomplete package gather. Workload releases apply the include and exclude filters and require non-empty pack and manifest sets; non-workload releases use the single package set described above.
  • Files reviewed: 7/7 changed files
  • Comments generated: 1
  • Review effort level: Balanced

Comment thread eng/pipelines/ci-official-release.Tests.ps1 Outdated
Clarify non-workload gather reconciliation and pin every allowed repository in the pipeline regression tests.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

Copilot-Session: 2933e733-3bed-4524-ae14-f7ad1c7959e6
Copilot AI review requested due to automatic review settings September 3, 2026 13:15

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

The approval instructions and release documentation inaccurately describe the staged package list and missing-package identity handling.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Review details
  • Files reviewed: 7/7 changed files
  • Comments generated: 2
  • Review effort level: Balanced

Comment thread docs/ReleaseProcess.md Outdated
Comment thread eng/pipelines/common/non-workload-publish.yml Outdated
Document BAR-derived identities for unavailable artifacts and explain that the approval audit is an upper bound after the final NuGet.org check.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

Copilot-Session: 2933e733-3bed-4524-ae14-f7ad1c7959e6
Copilot AI review requested due to automatic review settings September 3, 2026 16:31

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 Needs a closer look

It changes production release infrastructure, and the latest validation remains incomplete.

Review details
  • Files reviewed: 7/7 changed files
  • Comments generated: 0 new
  • Review effort level: Balanced

Comment thread eng/pipelines/ci-official-release.yml
@kubaflo

kubaflo commented Sep 3, 2026

Copy link
Copy Markdown
Contributor

Multi-model review summary

Verdict: NEEDS_CHANGES (comment-only) · Confidence: Low

Independent passes used GPT-5.6 Terra for MAUI dimensions, GPT-5.6 Luna for the release state machine, and GPT-5.5 for producer/consumer contracts. Candidates were then challenged across tracks. One finding survived cross-pollination:

  • The downloaded/BAR identity join compares raw nuspec versions with normalized BAR versions. Android-libraries normalizes staged filenames and BAR manifest versions, while the copied package can retain a non-normalized nuspec version. A valid package such as raw 1.2.3.0 / normalized 1.2.3 is therefore rejected during preparation. The minimal fix is to compare downloadedIdentity.normalizedVersion with asset.version and add a focused behavioral regression test. This fails closed; it cannot publish the wrong package.
Surface Cross-check
Darc/BAR Extended get-build returns a list; existing first-result behavior is retained. gather-drop --continue-on-error continues downloads but reports a nonzero exit, matching the reconciliation/reset path.
NuGet Flat-container HEAD supports exact 200/404 checks with lowercased normalized identities. The status, retry, and post-publish verification paths otherwise align.
Azure/1ES Output-variable stageDependencies, artifact handoff, and public packagesToPush/packageParentPath usage align. The proprietary 1ES.PublishNuget@1 implementation itself is not publicly inspectable.
State machine Skip/dry-run and workload pack-before-manifest conditions are coherent. A proposed skip-path defect was rejected after parsing the outer template-expression nesting.

Prior feedback: all existing inline threads are resolved. The earlier exact-one-BAR-build bug was fixed, and the finding above is distinct from the resolved synthetic-version fallback discussion.

CI: the PR-specific release and NuGet Pester tests pass. The red PowerShell Script Tests run is caused by 13 unrelated Invoke-CiScanReconcile.Tests.ps1 failures; the same 13/333 failures reproduce on the unchanged base and in an unrelated run. Required maui-pr build 1581205 is still pending; CLA has passed.

The title and description match the implementation and include the required testing note. No other high-confidence findings remain. I did not submit an approval or request-changes review event.

@jonathanpeppers

Copy link
Copy Markdown
Member Author

Follow-up on the surviving finding above: after tracing the producer implementation, I don't believe a code change is needed.

Arcade's NupkgInfoFactory reads the nuspec <version> value verbatim into the package artifact model; BAR does not independently normalize it. Darc then names the gathered file ${asset.Name}.${asset.Version}.nupkg in GatherDropOperation. The current version comparison therefore verifies that the gathered package's nuspec agrees with BAR. Comparing normalizedVersion would only compare Darc's BAR-derived filename back to the same BAR value and weaken that integrity check.

I replied with this evidence and resolved the inline thread.

@kubaflo
kubaflo merged commit c478f6b into main Sep 3, 2026
34 of 39 checks passed
@kubaflo
kubaflo deleted the jonathanpeppers-release-android-libraries branch September 3, 2026 22:11
@github-actions github-actions Bot added this to the .NET 10 SR11 milestone Sep 3, 2026
@github-actions github-actions Bot locked and limited conversation to collaborators Oct 4, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

s/agent-changes-requested AI agent recommends changes - found a better alternative or issues s/agent-fix-win AI found a better alternative fix than the PR s/agent-reviewed PR was reviewed by AI agent workflow (full 4-phase review)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants