Repository navigation
[ci] Replace custom NuGet.org publishing with 1ES.PublishNuget - #36568
Conversation
|
Azure Pipelines: 1 pipeline(s) were filtered out due to trigger conditions. There may be pipelines that require an authorized user to comment /azp run to run. |
Review follow-up: keep open, but not merge-readyThe 1ES publishing approach looks reasonable and I did not find an equivalent change on
Before merge, please:
|
Co-authored-by: rmarinho <1235097+rmarinho@users.noreply.github.com>
Co-authored-by: rmarinho <1235097+rmarinho@users.noreply.github.com>
Co-authored-by: rmarinho <1235097+rmarinho@users.noreply.github.com>
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 150fe76a-707e-48ca-9e9d-38bfccead829
10751c0 to
a28c1d4
Compare
|
🚀 Dogfood this PR with:
curl -fsSL https://raw.githubusercontent.com/dotnet/maui/main/eng/scripts/get-maui-pr.sh | bash -s -- 36568Or
iex "& { $(irm https://raw.githubusercontent.com/dotnet/maui/main/eng/scripts/get-maui-pr.ps1) } 36568" |
Skill Validation Results
✅ Skill Validation Results —
|
|
Azure Pipelines: Successfully started running 1 pipeline(s). There may be pipelines that require an authorized user to comment /azp run to run. |
There was a problem hiding this comment.
Pull request overview
This PR modernizes the MAUI internal release pipeline by replacing the repo-owned NuGet.org publishing logic with the approved 1ES.PublishNuget@1 path, making eng/pipelines/ci-official-release.yml the canonical release pipeline and removing legacy/internal duplication.
Changes:
- Replace
push_nuget_org.ps1/ raw API-key publishing with1ES.PublishNuget@1(service connection–backed) publishing jobs. - Add a guarded helper (
nuget_release_packages.ps1) + Pester tests to filter already-published packages and verify NuGet.org availability with retry/deadline semantics. - Remove obsolete
maui-release-internal.ymland update release documentation accordingly.
Reviewed changes
Copilot reviewed 8 out of 8 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
| eng/pipelines/ci-official-release.yml | New prepare stage producing SBOM-backed artifacts + production releaseJob publishing via 1ES.PublishNuget@1 with pre/post validation. |
| eng/scripts/push_nuget_org.ps1 | Removed legacy custom NuGet push + quota-rotation script. |
| eng/scripts/nuget_release_packages.ps1 | New helper to filter already-published packages and verify expected packages become available on NuGet.org. |
| eng/scripts/nuget_release_packages.Tests.ps1 | Pester coverage for filtering, retry behavior, normalization, and validation-lag handling. |
| eng/pipelines/maui-release-internal.yml | Removed obsolete duplicate internal release pipeline. |
| docs/ReleaseProcess.md | Updated canonical release + recovery procedure documentation to match the new flow. |
| .github/workflows/powershell-script-tests.yml | Expanded Pester gate to include the new release helper tests. |
| .github/skills/azdo-build-investigator/SKILL.md | Updated reference to reflect the single canonical release pipeline. |
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 150fe76a-707e-48ca-9e9d-38bfccead829
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 9 out of 9 changed files in this pull request and generated no new comments.
Suppressed comments (2)
eng/scripts/nuget_release_packages.ps1:116
Invoke-WebRequest -SkipHttpErrorCheckonly exists in newer PowerShell versions (introduced in 7.3). If this script is run on a host that still uses pwsh 7.2 (common in CI), this will fail at parameter binding before any retry/handling runs. Consider making the call version-tolerant by only using-SkipHttpErrorCheckwhen available, and otherwise mapping the thrown HTTP response into a StatusCode so 404/200 continue to work as intended.
try {
$response = Invoke-WebRequest -Uri $url -Method Head -SkipHttpErrorCheck -TimeoutSec 30
$statusCode = [int] $response.StatusCode
eng/scripts/nuget_release_packages.ps1:33
FilterExistingcan make a large number of NuGet.org HEAD requests, butMaxDurationMinutes/$verificationDeadlineis only enabled forVerify. If NuGet.org is slow/unresponsive,FilterExistingcan run effectively unbounded (per-package 30s timeout × attempts), delaying approvals and publishing without a clear cutoff. Consider applying the same deadline toFilterExistingso the job fails deterministically within the configured window.
$verificationDeadline = if ($Action -eq 'Verify') {
[DateTime]::UtcNow.AddMinutes($MaxDurationMinutes)
}
else {
$null
}
Reject duplicate package file names before staging artifacts and clarify the NuGet.org recovery guidance so documentation spellcheck succeeds. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 150fe76a-707e-48ca-9e9d-38bfccead829
This comment has been minimized.
This comment has been minimized.
MauiBot
left a comment
There was a problem hiding this comment.
AI Review Summary
@copilot — new AI review results are available based on this last commit:
adf2ad7.
🗂️ Review Sessions — click to expand
🚦 Gate — Test Before & After Fix
Gate Result: ⚠️ INCONCLUSIVE
Platform: ANDROID
⚠️ verify-tests-fail.ps1exited before writing a verification report. Diagnostics below.
Exit code: 3
Likely cause:
- Test detection failed — no runnable tests were found in the PR diff.
Gate output log (last 60 lines)
📁 Output directory: CustomAgentLogsTmp/PRState/36568/PRAgent/gate/verify-tests-fail
🔍 Detecting base branch and merge point...
No PR detected, scanning remote branches for closest base...
✅ Base branch: main (via closest-merge-base)
✅ Merge base commit: 927d0117
(1 commits ahead of main)
╔═══════════════════════════════════════════════════════════╗
║ FULL VERIFICATION MODE ║
╠═══════════════════════════════════════════════════════════╣
║ Fix files detected - will verify: ║
║ 1. Tests FAIL without fix ║
║ 2. Tests PASS with fix ║
╚═══════════════════════════════════════════════════════════╝
✅ Fix files (6):
- eng/pipelines/ci-official-release.Tests.ps1
- eng/pipelines/ci-official-release.yml
- eng/pipelines/maui-release-internal.yml
- eng/scripts/nuget_release_packages.Tests.ps1
- eng/scripts/nuget_release_packages.ps1
- eng/scripts/push_nuget_org.ps1
🔍 Auto-detecting test filter from changed test files...
⚠️ No tests detected in this PR.
Searched for: UI tests, unit tests, XAML tests, device tests
Consider adding tests via write-tests-agent.
📋 Pre-Flight — Context & Validation
Issue: #36567 - [ci] Replace custom NuGet.org publishing with 1ES.PublishNuget
PR: #36568 - [ci] Replace custom NuGet.org publishing with 1ES.PublishNuget
Platforms Affected: Release infrastructure only; requested test platform android has no runtime path for this PR.
Files Changed: 7 implementation/docs/pipeline, 2 test
Key Findings
- The PR makes
eng/pipelines/ci-official-release.ymlthe canonical release pipeline, removes the legacy custompush_nuget_org.ps1raw API-key path, and publishes through 1ES release jobs. - The implementation preserves fail-fast BAR package gathering, separate pack/manifest artifacts, pack-before-manifest verification, duplicate identity/file-name validation, and recovery filters for partial NuGet.org publishes.
- Prior review feedback about duplicate package file names was addressed in
eng/pipelines/ci-official-release.ymland covered byeng/pipelines/ci-official-release.Tests.ps1. - Gate was already reported inconclusive by the caller and was not rerun. Public discussion also notes that full release readiness requires internal 1ES/service-connection validation.
Code Review Summary
Verdict: NEEDS_DISCUSSION
Confidence: low
Errors: 0 | Warnings: 0 | Suggestions: 0
Key code review findings:
- ℹ No code-level defects were found by the independent code-review pass.
- ℹ
maui-expert-reviewerreturned[]. - ℹ Confidence remains low because this is high-blast-radius release infrastructure and required CI/internal release validation was pending or unavailable in this environment.
Fix Candidates
| # | Source | Approach | Test Result | Files Changed | Notes |
|---|---|---|---|---|---|
| PR | PR #36568 | Replace custom NuGet.org publishing with 1ES release jobs plus helper-based pre/post NuGet.org verification. | eng/pipelines/ci-official-release.yml, eng/scripts/nuget_release_packages.ps1, tests/docs |
Original PR |
🔬 Code Review — Deep Analysis
Code Review — PR #36568
Independent Assessment
What this changes: Replaces MAUI’s custom NuGet.org release publishing script/pipeline with a single ci-official-release.yml path using prepared package artifacts, 1ES release jobs, NuGet.org availability filtering, and post-publish verification.
Inferred motivation: Remove raw API-key publishing/quota rotation and make partial-publish recovery deterministic.
Reconciliation with PR Narrative
Author claims: Canonicalize release publishing through 1ES.PublishNuget@1, preserve package filtering, add recovery/verification, remove legacy release YAML/script, and document internal validation/known NuGet.org ownership blocker.
Agreement/disagreement: Matches the code. The documented external ownership blocker is not a code defect but means production readiness depends on service-connection/package ownership outside this PR.
Prior Review Reconciliation
No prior ❌ Error findings found.
Non-error prior feedback about duplicate package file names is addressed in eng/pipelines/ci-official-release.yml:272-275 and covered by eng/pipelines/ci-official-release.Tests.ps1:33-41.
Blast Radius Assessment
- Runs for all instances: No app/runtime impact; affects manually run official release pipeline only.
- Startup impact: No.
- Static/shared state: No app static state; release pipeline artifacts/variables are per run.
- Release infrastructure impact: High blast radius because this changes NuGet.org release publishing and recovery.
CI Status
- Required-check result:
gh pr checks --requiredunavailable due missing auth. Public check-runs show PR CI still pending/queued (maui-prqueued, Build Analysis/integration build checks in progress). - Classification: undetermined/pending.
- Action taken: Invoked
azdo-build-investigator;ci-analysisskill unavailable in this environment. Confidence capped low; cannot returnLGTMwhile CI is pending.
Findings
No ❌ Error,
maui-expert-reviewer result: [].
Failure-Mode Probing
commitHash: skip: Onlyskip_releaseruns; no gather, approval, service connection, or publish stages.- Dry run (
pushPackages: false): Prepares artifacts but excludes production publish/approval stages. - Partial publish/duplicate recovery: Already-visible packages are removed before task input; recovery filters remove accepted-but-not-visible packages, while final verify still checks the complete expected set.
- Duplicate normalized filenames: Explicitly rejected before copy/manifest emission.
- NuGet transient/unexpected statuses: transient statuses retry; non-200/404 terminal responses fail closed.
External Output Contract
| Consumer token/pattern | Producer location | Producer emission condition | Consumer assumption | Ordinary negative case | Downstream effect |
|---|---|---|---|---|---|
^[^/]+$ asset filter |
Darc/BAR asset names consumed by darc gather-drop |
NuGet package assets are slash-free; symbol/blob assets use paths | slash-free assets are release package inputs | symbol blob path such as assets/symbols/...nupkg |
excluded from gather while package gather stays fail-fast |
*.Manifest*.nupkg, \.Manifest-(?<major>\d+)\. |
Gathered package file names | workload manifest packages include .Manifest-<major>. |
select manifests and derive workload channel | ordinary workload pack without Manifest |
pack goes to pack artifact, not manifest channel logic |
| HTTP 200/404/transient status | NuGet flat-container HEAD endpoint | 200 visible, 404 missing/not indexed, 408/429/5xx transient | decide publish/verify state | 403/auth or unexpected status | fails closed instead of publishing/marking verified |
Verdict: NEEDS_DISCUSSION
Confidence: low — release-infrastructure blast radius plus pending/undetermined CI.
Summary: I found no code-level correctness issue in the current diff, and expert review produced no inline findings. Final readiness still needs settled CI and the documented NuGet.org service-connection ownership blocker resolved/accepted by release owners.
🛠️ Fix — Analysis & Comparison
Fix Candidates
| # | Source | Approach | Test Result | Files Changed | Notes |
|---|---|---|---|---|---|
| 1 | maui-expert-reviewer | Task-native idempotency: remove pre-publish filtering and rely on 1ES.PublishNuget@1 allowPackageConflicts. |
eng/pipelines/ci-official-release.yml |
Requires internal external-feed duplicate validation; existing PR evidence says 409 still fails. | |
| 2 | maui-expert-reviewer | Harden helper with operation-wide deadline and PowerShell-version-tolerant HTTP status handling. | ✅ PASS (isolated Pester 18/18) | eng/scripts/nuget_release_packages.ps1 |
Best available candidate, but incremental rather than a wholly different release design. |
| 3 | maui-expert-reviewer | Deterministic sequential batch publishing with per-batch 1ES publish/verify checkpoints. | eng/pipelines/ci-official-release.yml |
Potentially useful only if quota/partial-failure blast radius is a known constraint; much more complex. | |
| PR | PR #36568 | Explicit helper-based NuGet.org filtering/verification, 1ES release jobs, SBOM-backed pack/manifest artifacts. | 9 files | Original PR; independent review found no code-level defects. |
Cross-Pollination
| Model | Round | New Ideas? | Details |
|---|---|---|---|
| gpt-5.5 / maui-expert-reviewer | 1 | Yes | Proposed task-native idempotency, hardened helper/staging-plan direction, and deterministic batching. |
| gpt-5.5 / maui-expert-reviewer | 2 | No | After attempts 1-3, expert reviewer returned NO NEW IDEAS; remaining variants collapse into helper hardening or require larger unvalidated release-design changes. |
Exhausted: Yes
Selected Fix: Candidate #2 as an optional hardening candidate — it passed the available isolated regression suite and addresses concrete review concerns. It is not demonstrably better than the PR as a replacement for the PR's core design because it builds on that design and still needs internal 1ES/service-connection validation.
🧭 Next Steps — review latest findings
No alternative fix was selected for this run. Review the session findings and CI results before merging.
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!
Root Cause
MAUI's release flow implemented NuGet.org authentication, retries, conflict handling, quota rotation, and publishing in
eng/scripts/push_nuget_org.ps1. Gather and push failures could also continue without failing the release. The repository additionally carried a duplicate internal release YAML.Description of Change
This makes
eng/pipelines/ci-official-release.ymlthe canonical release pipeline and moves package publication to the approved 1ES release path:darc gather-drop, limited to BAR NuGet package assets rather than unrelated symbol/blob assets.MauiPacksForNuGetandMauiManifestsForNuGetartifacts.releaseJobjobs with1ES.PublishNuget@1,useDotNetTask: false, and the protectednuget.org (dotnetframework)service connection.maui-release-internal.yml,push_nuget_org.ps1, raw API-key inputs, and the arbitrary feed URL.No public API changes are involved.
Duplicate and Partial-Publish Recovery
Internal build 3050066 proved that the service connection authenticates, but also proved that the external-feed-backed task fails on HTTP 409 even with
allowPackageConflicts: true. Task-level retries are unsafe after a partial batch because the retry can immediately encounter duplicates created by the first attempt.The final implementation therefore:
expected-packages.jsonidentity manifest for each immutable artifact..nupkgfiles before publication.nugetAlreadyAttemptedPackFiltersandnugetAlreadyAttemptedManifestFiltersparameters for packages accepted but not yet visible during NuGet.org validation lag.1ES.PublishNuget@1only when packages remain and does not use task-level retries.Internal Validation
copilot/replace-custom-nuget-publishingadf2ad774cd3a2092450df153e6b46a09a127552dotnet-maui-releasedefinition already executes the canonical YAML, so no pipeline-definition migration is needed.nuget.org (dotnetframework)connection authenticates: it publishedMicrosoft.AspNetCore.Components.WebView.Maui.10.0.100, then NuGet.org rejectedMicrosoft.AspNetCore.Components.WebView.Maui.Msi.arm64.10.0.100with HTTP 403. The first package listsdotnetframeworkas an owner; the MSI package lists onlyMicrosoftandXamarin. This is a NuGet.org package-ownership/service-connection blocker, not a pipeline task-wiring failure.Before production use, either the service-connection identity must be granted ownership of every MAUI package (including Xamarin-owned MSI packages), or the release pipeline must be given an approved Xamarin-scoped NuGet.org service connection. Once that external credential issue is resolved, rerun the same commit and BAR selection; the recovery logic will omit the package already published by build 3050279 and continue with the remaining set.
What Not to Do
allowPackageConflictsfor an external NuGet.org feed; build 3050066 proved it does not make 409 responses successful.--continue-on-errorgather behavior; exclude non-release symbol/blob assets and keep publishable package gathering fail-fast.Issues Fixed
Fixes #36567