diff --git a/build/ci/.azure-devops-stages.yml b/build/ci/.azure-devops-stages.yml index 5606244df3d0..6c883c8666b6 100644 --- a/build/ci/.azure-devops-stages.yml +++ b/build/ci/.azure-devops-stages.yml @@ -18,9 +18,11 @@ stages: README.md README/** build/** + src/Uno.Sdk/packages.json - 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..4bb05a26f952 --- /dev/null +++ b/build/ci/scripts/validate-packages-json.ps1 @@ -0,0 +1,154 @@ +#!/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, + [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 + $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 + } +} + +# 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 +$validatedPairs = @{} # De-duplicate package+version pairs across groups + +# 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 + } + + # 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 ` + -TimeoutSec 30 -MaximumRetryCount 3 -RetryIntervalSec 2 -ErrorAction Stop + if ($response.StatusCode -eq 200) { + Write-Host " [OK] $pkg $version ($label)" -ForegroundColor Green + } + } + catch { + $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) - $($_.Exception.Message)" -ForegroundColor Yellow + $errors += "Group '$groupName': $pkg $version ($label) - network error: $($_.Exception.Message)" + } + } + } + } +} + +Write-Host "" +Write-Host "Checked $checked package/version combinations." -ForegroundColor Cyan + +if ($errors.Count -gt 0) { + Write-Host "" + 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 $(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 +} +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..2f492228c1be --- /dev/null +++ b/build/ci/setup/.azure-devops-setup-validate-packages.yml @@ -0,0 +1,9 @@ +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"' + env: + BUILD_SOURCEBRANCH: $(Build.SourceBranch) 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 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/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" } }, { 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" />