From 6073f5375fafb5a4e540580543f881867214bac3 Mon Sep 17 00:00:00 2001 From: agneszitte Date: Fri, 1 May 2026 13:48:51 -0400 Subject: [PATCH 1/6] fix(sdk): correct broken AndroidX package entries and add CI validation guard MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Fix Xamarin.AndroidX.Car.App → Xamarin.AndroidX.Car.App.App (wrong package ID) - Fix Xamarin.AndroidX.Wear net10.0 override 1.3.0.17 → 1.4.0.1 (non-existent version) - Fix Xamarin.AndroidX.Wear.Tiles base 1.4.0.2 → 1.4.0.1, net10.0 1.4.0.3 → 1.4.1 (non-existent versions) - Add validate-packages-json.ps1 CI guard that checks all NuGet entries exist - Integrate guard into Setup/Validations stage of Azure DevOps pipeline These broken entries were introduced in PR #23157 and block downstream consumers (e.g. uno.toolkit.ui PR #2072) from resolving packages on net10.0-android. --- build/ci/.azure-devops-stages.yml | 1 + build/ci/scripts/validate-packages-json.ps1 | 113 ++++++++++++++++++ .../.azure-devops-setup-validate-packages.yml | 7 ++ src/Uno.Sdk/packages.json | 8 +- 4 files changed, 125 insertions(+), 4 deletions(-) create mode 100644 build/ci/scripts/validate-packages-json.ps1 create mode 100644 build/ci/setup/.azure-devops-setup-validate-packages.yml diff --git a/build/ci/.azure-devops-stages.yml b/build/ci/.azure-devops-stages.yml index 5606244df3d0..f048b94ba5a6 100644 --- a/build/ci/.azure-devops-stages.yml +++ b/build/ci/.azure-devops-stages.yml @@ -21,6 +21,7 @@ stages: - template: templates/gitversion-run.yml - template: setup/.azure-devops-setup-commitsar.yml + - template: setup/.azure-devops-setup-validate-packages.yml - template: setup/.azure-devops-setup-doc-validations.yml - job: DetermineScope diff --git a/build/ci/scripts/validate-packages-json.ps1 b/build/ci/scripts/validate-packages-json.ps1 new file mode 100644 index 000000000000..db73bd2279e4 --- /dev/null +++ b/build/ci/scripts/validate-packages-json.ps1 @@ -0,0 +1,113 @@ +#!/usr/bin/env pwsh +<# +.SYNOPSIS + Validates that all package IDs and versions in src/Uno.Sdk/packages.json exist on NuGet.org. + +.DESCRIPTION + Parses packages.json, skips groups with placeholder versions (e.g. "DefaultUnoVersion"), + and verifies each package ID + version (including versionOverride entries) against NuGet.org. + Exits with code 1 if any package/version is missing. + +.PARAMETER PackagesJsonPath + Path to the packages.json file. Defaults to src/Uno.Sdk/packages.json relative to repo root. +#> +param( + [string]$PackagesJsonPath +) + +$ErrorActionPreference = 'Stop' + +# Resolve path +if (-not $PackagesJsonPath) { + $repoRoot = Split-Path -Parent (Split-Path -Parent (Split-Path -Parent $PSScriptRoot)) + $PackagesJsonPath = Join-Path $repoRoot 'src/Uno.Sdk/packages.json' +} + +if (-not (Test-Path $PackagesJsonPath)) { + Write-Error "packages.json not found at: $PackagesJsonPath" + exit 1 +} + +Write-Host "Validating packages.json: $PackagesJsonPath" -ForegroundColor Cyan + +$json = Get-Content $PackagesJsonPath -Raw | ConvertFrom-Json +$errors = @() +$checked = 0 + +# Placeholder versions that should not be validated against NuGet +$placeholderVersions = @('DefaultUnoVersion') + +foreach ($group in $json) { + $groupName = $group.group + $baseVersion = $group.version + $packages = $group.packages + + # Skip groups with placeholder versions + if ($placeholderVersions -contains $baseVersion) { + Write-Host " [SKIP] $groupName (placeholder version: $baseVersion)" -ForegroundColor DarkGray + continue + } + + # Collect all version+package combinations to check + $versionsToCheck = @() + + # Base version + $versionsToCheck += @{ Version = $baseVersion; Label = 'base' } + + # Version overrides (TFM-specific) + if ($group.PSObject.Properties['versionOverride'] -and $null -ne $group.versionOverride) { + $overrides = $group.versionOverride + foreach ($prop in $overrides.PSObject.Properties) { + $versionsToCheck += @{ Version = $prop.Value; Label = "override($($prop.Name))" } + } + } + + foreach ($pkg in $packages) { + foreach ($vEntry in $versionsToCheck) { + $version = $vEntry.Version + $label = $vEntry.Label + + # Also skip overrides that reference a placeholder + if ($placeholderVersions -contains $version) { + continue + } + + $checked++ + $url = "https://api.nuget.org/v3-flatcontainer/$($pkg.ToLowerInvariant())/$($version.ToLowerInvariant())/$($pkg.ToLowerInvariant()).nuspec" + + try { + $response = Invoke-WebRequest -Uri $url -Method Head -UseBasicParsing -ErrorAction Stop + if ($response.StatusCode -eq 200) { + Write-Host " [OK] $pkg $version ($label)" -ForegroundColor Green + } + } + catch { + $statusCode = $_.Exception.Response.StatusCode.value__ + if ($statusCode -eq 404) { + Write-Host " [MISSING] $pkg $version ($label) - NOT FOUND on NuGet" -ForegroundColor Red + $errors += "Group '$groupName': $pkg $version ($label) does not exist on NuGet.org" + } + else { + Write-Host " [ERROR] $pkg $version ($label) - HTTP $statusCode" -ForegroundColor Yellow + $errors += "Group '$groupName': $pkg $version ($label) - HTTP error $statusCode" + } + } + } + } +} + +Write-Host "" +Write-Host "Checked $checked package/version combinations." -ForegroundColor Cyan + +if ($errors.Count -gt 0) { + Write-Host "" + Write-Host "VALIDATION FAILED - $($errors.Count) error(s):" -ForegroundColor Red + foreach ($err in $errors) { + Write-Host " - $err" -ForegroundColor Red + } + exit 1 +} +else { + Write-Host "All packages validated successfully." -ForegroundColor Green + exit 0 +} diff --git a/build/ci/setup/.azure-devops-setup-validate-packages.yml b/build/ci/setup/.azure-devops-setup-validate-packages.yml new file mode 100644 index 000000000000..1b16ce05a487 --- /dev/null +++ b/build/ci/setup/.azure-devops-setup-validate-packages.yml @@ -0,0 +1,7 @@ +steps: +- task: PowerShell@2 + displayName: Validate packages.json (NuGet existence check) + inputs: + pwsh: true + filePath: build/ci/scripts/validate-packages-json.ps1 + arguments: '-PackagesJsonPath "$(Build.SourcesDirectory)/src/Uno.Sdk/packages.json"' diff --git a/src/Uno.Sdk/packages.json b/src/Uno.Sdk/packages.json index b5085b8e98e9..4190f78de795 100644 --- a/src/Uno.Sdk/packages.json +++ b/src/Uno.Sdk/packages.json @@ -281,7 +281,7 @@ "group": "AndroidXCarApp", "version": "1.4.0.2", "packages": [ - "Xamarin.AndroidX.Car.App" + "Xamarin.AndroidX.Car.App.App" ], "versionOverride": { "net10.0": "1.4.0.3" @@ -294,17 +294,17 @@ "Xamarin.AndroidX.Wear" ], "versionOverride": { - "net10.0": "1.3.0.17" + "net10.0": "1.4.0.1" } }, { "group": "AndroidXWearTiles", - "version": "1.4.0.2", + "version": "1.4.0.1", "packages": [ "Xamarin.AndroidX.Wear.Tiles" ], "versionOverride": { - "net10.0": "1.4.0.3" + "net10.0": "1.4.1" } }, { From 45bf12ab26e3a439bb795870bcb99520da1d747d Mon Sep 17 00:00:00 2001 From: agneszitte Date: Fri, 1 May 2026 13:55:57 -0400 Subject: [PATCH 2/6] fix: address PR review - add sparse checkout path and handle null HTTP response --- build/ci/.azure-devops-stages.yml | 1 + build/ci/scripts/validate-packages-json.ps1 | 19 +++++++++++++------ 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/build/ci/.azure-devops-stages.yml b/build/ci/.azure-devops-stages.yml index f048b94ba5a6..03885badad17 100644 --- a/build/ci/.azure-devops-stages.yml +++ b/build/ci/.azure-devops-stages.yml @@ -18,6 +18,7 @@ stages: README.md README/** build/** + src/Uno.Sdk/** - template: templates/gitversion-run.yml - template: setup/.azure-devops-setup-commitsar.yml diff --git a/build/ci/scripts/validate-packages-json.ps1 b/build/ci/scripts/validate-packages-json.ps1 index db73bd2279e4..643b373c9c25 100644 --- a/build/ci/scripts/validate-packages-json.ps1 +++ b/build/ci/scripts/validate-packages-json.ps1 @@ -82,14 +82,21 @@ foreach ($group in $json) { } } catch { - $statusCode = $_.Exception.Response.StatusCode.value__ - if ($statusCode -eq 404) { - Write-Host " [MISSING] $pkg $version ($label) - NOT FOUND on NuGet" -ForegroundColor Red - $errors += "Group '$groupName': $pkg $version ($label) does not exist on NuGet.org" + $httpResponse = $_.Exception.Response + if ($null -ne $httpResponse) { + $statusCode = $httpResponse.StatusCode.value__ + if ($statusCode -eq 404) { + Write-Host " [MISSING] $pkg $version ($label) - NOT FOUND on NuGet" -ForegroundColor Red + $errors += "Group '$groupName': $pkg $version ($label) does not exist on NuGet.org" + } + else { + Write-Host " [ERROR] $pkg $version ($label) - HTTP $statusCode" -ForegroundColor Yellow + $errors += "Group '$groupName': $pkg $version ($label) - HTTP error $statusCode" + } } else { - Write-Host " [ERROR] $pkg $version ($label) - HTTP $statusCode" -ForegroundColor Yellow - $errors += "Group '$groupName': $pkg $version ($label) - HTTP error $statusCode" + Write-Host " [ERROR] $pkg $version ($label) - $($_.Exception.Message)" -ForegroundColor Yellow + $errors += "Group '$groupName': $pkg $version ($label) - network error: $($_.Exception.Message)" } } } From e7e3aab6f7869613358a7fd6cba461cc6505ecff Mon Sep 17 00:00:00 2001 From: agneszitte Date: Fri, 1 May 2026 14:11:22 -0400 Subject: [PATCH 3/6] fix: add WarningOnly mode for release branches in packages validation - Pass BUILD_SOURCEBRANCH env var from pipeline to script - Auto-detect release/* branches and switch to warning-only mode - Warnings shown in yellow but build not failed on stable branches --- build/ci/scripts/validate-packages-json.ps1 | 27 ++++++++++++++++--- .../.azure-devops-setup-validate-packages.yml | 2 ++ 2 files changed, 26 insertions(+), 3 deletions(-) diff --git a/build/ci/scripts/validate-packages-json.ps1 b/build/ci/scripts/validate-packages-json.ps1 index 643b373c9c25..3c1548e2a146 100644 --- a/build/ci/scripts/validate-packages-json.ps1 +++ b/build/ci/scripts/validate-packages-json.ps1 @@ -12,11 +12,22 @@ Path to the packages.json file. Defaults to src/Uno.Sdk/packages.json relative to repo root. #> param( - [string]$PackagesJsonPath + [string]$PackagesJsonPath, + [switch]$WarningOnly ) $ErrorActionPreference = 'Stop' +# Auto-detect: on release/* branches, switch to warning-only mode +# (stable builds test with versions not yet public on NuGet.org) +if (-not $WarningOnly) { + $branch = $env:BUILD_SOURCEBRANCH + if ($branch -and $branch -like 'refs/heads/release/*') { + Write-Host "Detected release branch ($branch) - running in warning-only mode." -ForegroundColor Yellow + $WarningOnly = $true + } +} + # Resolve path if (-not $PackagesJsonPath) { $repoRoot = Split-Path -Parent (Split-Path -Parent (Split-Path -Parent $PSScriptRoot)) @@ -108,9 +119,19 @@ Write-Host "Checked $checked package/version combinations." -ForegroundColor Cya if ($errors.Count -gt 0) { Write-Host "" - Write-Host "VALIDATION FAILED - $($errors.Count) error(s):" -ForegroundColor Red + if ($WarningOnly) { + Write-Host "VALIDATION WARNINGS - $($errors.Count) package(s) not found on NuGet.org (non-fatal):" -ForegroundColor Yellow + } + else { + Write-Host "VALIDATION FAILED - $($errors.Count) error(s):" -ForegroundColor Red + } foreach ($err in $errors) { - Write-Host " - $err" -ForegroundColor Red + Write-Host " - $err" -ForegroundColor $(if ($WarningOnly) { 'Yellow' } else { 'Red' }) + } + if ($WarningOnly) { + Write-Host "" + Write-Host "Running in warning-only mode (stable branch) - not failing the build." -ForegroundColor Yellow + exit 0 } exit 1 } diff --git a/build/ci/setup/.azure-devops-setup-validate-packages.yml b/build/ci/setup/.azure-devops-setup-validate-packages.yml index 1b16ce05a487..2f492228c1be 100644 --- a/build/ci/setup/.azure-devops-setup-validate-packages.yml +++ b/build/ci/setup/.azure-devops-setup-validate-packages.yml @@ -5,3 +5,5 @@ steps: pwsh: true filePath: build/ci/scripts/validate-packages-json.ps1 arguments: '-PackagesJsonPath "$(Build.SourcesDirectory)/src/Uno.Sdk/packages.json"' + env: + BUILD_SOURCEBRANCH: $(Build.SourceBranch) From ea1eacdd758d93bf07aba5f6dbcc4d81f97dfbbf Mon Sep 17 00:00:00 2001 From: agneszitte Date: Fri, 1 May 2026 14:16:33 -0400 Subject: [PATCH 4/6] fix: align AndroidX.Car.App.App ID in implicit targets and harden validation script - Update Uno.Implicit.Packages.ProjectSystem.Android.targets to use Xamarin.AndroidX.Car.App.App (matching packages.json fix) - Update Sdk.props.buildschema.json description accordingly - Add -TimeoutSec 30, -MaximumRetryCount 3, -RetryIntervalSec 2 to NuGet HTTP requests in validate-packages-json.ps1 - De-duplicate package+version pairs to avoid redundant API calls --- build/ci/scripts/validate-packages-json.ps1 | 12 +++++++++++- src/Uno.Sdk/Sdk/Sdk.props.buildschema.json | 2 +- ...o.Implicit.Packages.ProjectSystem.Android.targets | 2 +- 3 files changed, 13 insertions(+), 3 deletions(-) diff --git a/build/ci/scripts/validate-packages-json.ps1 b/build/ci/scripts/validate-packages-json.ps1 index 3c1548e2a146..baee77960e2d 100644 --- a/build/ci/scripts/validate-packages-json.ps1 +++ b/build/ci/scripts/validate-packages-json.ps1 @@ -44,6 +44,7 @@ Write-Host "Validating packages.json: $PackagesJsonPath" -ForegroundColor Cyan $json = Get-Content $PackagesJsonPath -Raw | ConvertFrom-Json $errors = @() $checked = 0 +$validatedPairs = @{} # De-duplicate package+version pairs across groups # Placeholder versions that should not be validated against NuGet $placeholderVersions = @('DefaultUnoVersion') @@ -83,11 +84,20 @@ foreach ($group in $json) { continue } + # De-duplicate: skip if we already validated this package+version + $pairKey = "$($pkg.ToLowerInvariant())|$($version.ToLowerInvariant())" + if ($validatedPairs.ContainsKey($pairKey)) { + Write-Host " [OK] $pkg $version ($label) - already validated" -ForegroundColor DarkGreen + continue + } + $validatedPairs[$pairKey] = $true + $checked++ $url = "https://api.nuget.org/v3-flatcontainer/$($pkg.ToLowerInvariant())/$($version.ToLowerInvariant())/$($pkg.ToLowerInvariant()).nuspec" try { - $response = Invoke-WebRequest -Uri $url -Method Head -UseBasicParsing -ErrorAction Stop + $response = Invoke-WebRequest -Uri $url -Method Head -UseBasicParsing ` + -TimeoutSec 30 -MaximumRetryCount 3 -RetryIntervalSec 2 -ErrorAction Stop if ($response.StatusCode -eq 200) { Write-Host " [OK] $pkg $version ($label)" -ForegroundColor Green } diff --git a/src/Uno.Sdk/Sdk/Sdk.props.buildschema.json b/src/Uno.Sdk/Sdk/Sdk.props.buildschema.json index c502976a54a4..c8c1be97c250 100644 --- a/src/Uno.Sdk/Sdk/Sdk.props.buildschema.json +++ b/src/Uno.Sdk/Sdk/Sdk.props.buildschema.json @@ -590,7 +590,7 @@ "helpUrl": "https://aka.platform.uno/feature-android-tv" }, "AndroidAuto": { - "description": "Configures the Android head for Android Auto / Automotive OS (adds Xamarin.AndroidX.Car.App).", + "description": "Configures the Android head for Android Auto / Automotive OS (adds Xamarin.AndroidX.Car.App.App).", "helpUrl": "https://aka.platform.uno/feature-android-auto" }, "AndroidWear": { diff --git a/src/Uno.Sdk/targets/Uno.Implicit.Packages.ProjectSystem.Android.targets b/src/Uno.Sdk/targets/Uno.Implicit.Packages.ProjectSystem.Android.targets index 789df9cee89a..0e43b27dfa35 100644 --- a/src/Uno.Sdk/targets/Uno.Implicit.Packages.ProjectSystem.Android.targets +++ b/src/Uno.Sdk/targets/Uno.Implicit.Packages.ProjectSystem.Android.targets @@ -27,7 +27,7 @@ - <_UnoProjectSystemPackageReference Include="Xamarin.AndroidX.Car.App" ProjectSystem="true" /> + <_UnoProjectSystemPackageReference Include="Xamarin.AndroidX.Car.App.App" ProjectSystem="true" /> From aa1d6d1d294473d99072d6b1367ddaf45ee251be Mon Sep 17 00:00:00 2001 From: agneszitte Date: Fri, 1 May 2026 14:33:21 -0400 Subject: [PATCH 5/6] fix(docs): update android-auto.md to correct Car.App.App package name --- doc/articles/features/android-auto.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/articles/features/android-auto.md b/doc/articles/features/android-auto.md index 41e9551c13a2..b233006365ed 100644 --- a/doc/articles/features/android-auto.md +++ b/doc/articles/features/android-auto.md @@ -14,7 +14,7 @@ For projects using the `Uno.Sdk`, add the `AndroidAuto` feature to your project' $(UnoFeatures);AndroidAuto ``` -This automatically adds a reference to the `Xamarin.AndroidX.Car.App` package. New projects created from the Uno Platform templates with the **Android Auto** option enabled will also have the required manifest entries and the `automotive_app_desc.xml` resource stamped automatically. +This automatically adds a reference to the `Xamarin.AndroidX.Car.App.App` package. New projects created from the Uno Platform templates with the **Android Auto** option enabled will also have the required manifest entries and the `automotive_app_desc.xml` resource stamped automatically. ## What gets configured From 76d6ae6275cc3aff82a827e40b789c4011d1ffae Mon Sep 17 00:00:00 2001 From: agneszitte Date: Fri, 1 May 2026 14:47:25 -0400 Subject: [PATCH 6/6] fix(ci): narrow sparse checkout and detect release PRs via target branch --- build/ci/.azure-devops-stages.yml | 2 +- build/ci/scripts/validate-packages-json.ps1 | 7 +++++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/build/ci/.azure-devops-stages.yml b/build/ci/.azure-devops-stages.yml index 03885badad17..6c883c8666b6 100644 --- a/build/ci/.azure-devops-stages.yml +++ b/build/ci/.azure-devops-stages.yml @@ -18,7 +18,7 @@ stages: README.md README/** build/** - src/Uno.Sdk/** + src/Uno.Sdk/packages.json - template: templates/gitversion-run.yml - template: setup/.azure-devops-setup-commitsar.yml diff --git a/build/ci/scripts/validate-packages-json.ps1 b/build/ci/scripts/validate-packages-json.ps1 index baee77960e2d..4bb05a26f952 100644 --- a/build/ci/scripts/validate-packages-json.ps1 +++ b/build/ci/scripts/validate-packages-json.ps1 @@ -22,8 +22,11 @@ $ErrorActionPreference = 'Stop' # (stable builds test with versions not yet public on NuGet.org) if (-not $WarningOnly) { $branch = $env:BUILD_SOURCEBRANCH - if ($branch -and $branch -like 'refs/heads/release/*') { - Write-Host "Detected release branch ($branch) - running in warning-only mode." -ForegroundColor Yellow + $targetBranch = $env:SYSTEM_PULLREQUEST_TARGETBRANCH + if (($branch -and $branch -like 'refs/heads/release/*') -or + ($targetBranch -and $targetBranch -like 'refs/heads/release/*')) { + $detected = if ($targetBranch -like 'refs/heads/release/*') { $targetBranch } else { $branch } + Write-Host "Detected release branch ($detected) - running in warning-only mode." -ForegroundColor Yellow $WarningOnly = $true } }