diff --git a/.pipelines/UpdateVersions.ps1 b/.pipelines/UpdateVersions.ps1
index 4e68663236..eae8a281bb 100644
--- a/.pipelines/UpdateVersions.ps1
+++ b/.pipelines/UpdateVersions.ps1
@@ -13,7 +13,31 @@ Param(
# Root folder Path for processing
[Parameter(Mandatory=$False,Position=4)]
- [string]$sourceLink = "https://microsoft.pkgs.visualstudio.com/ProjectReunion/_packaging/Project.Reunion.nuget.internal/nuget/v3/index.json"
+ [string]$sourceLink = "https://microsoft.pkgs.visualstudio.com/ProjectReunion/_packaging/Project.Reunion.nuget.internal/nuget/v3/index.json",
+
+ # Use Azure Pipeline artifact as source for metapackage
+ [Parameter(Mandatory=$False,Position=5)]
+ [boolean]$useArtifactSource = $False,
+
+ # Azure DevOps organization URL
+ [Parameter(Mandatory=$False,Position=6)]
+ [string]$azureDevOpsOrg = "https://dev.azure.com/microsoft",
+
+ # Azure DevOps project name
+ [Parameter(Mandatory=$False,Position=7)]
+ [string]$azureDevOpsProject = "ProjectReunion",
+
+ # Pipeline build ID (or "latest" for latest build)
+ [Parameter(Mandatory=$False,Position=8)]
+ [string]$buildId = "",
+
+ # Artifact name containing the NuGet packages
+ [Parameter(Mandatory=$False,Position=9)]
+ [string]$artifactName = "WindowsAppSDK_Nuget_And_MSIX",
+
+ # Metapackage name to look for in artifact
+ [Parameter(Mandatory=$False,Position=10)]
+ [string]$metaPackageName = "Microsoft.WindowsAppSDK"
)
@@ -114,6 +138,222 @@ function Add-NuGetSourceAndMapping {
}
}
+function Download-ArtifactFromPipeline {
+ param (
+ [string]$Organization,
+ [string]$Project,
+ [string]$BuildId,
+ [string]$ArtifactName,
+ [string]$OutputDir
+ )
+
+ Write-Host "Downloading artifact '$ArtifactName' from build $BuildId..."
+ New-Item -ItemType Directory -Path $OutputDir -Force | Out-Null
+
+ try {
+ # Use az CLI to download artifact
+ $azArgs = "pipelines runs artifact download --organization $Organization --project $Project --run-id $BuildId --artifact-name `"$ArtifactName`" --path `"$OutputDir`""
+ Invoke-Expression "az $azArgs"
+
+ if ($LASTEXITCODE -eq 0) {
+ Write-Host "Successfully downloaded artifact to $OutputDir"
+ return $true
+ } else {
+ Write-Warning "Failed to download artifact. Exit code: $LASTEXITCODE"
+ return $false
+ }
+ } catch {
+ Write-Warning "Error downloading artifact: $_"
+ return $false
+ }
+}
+
+function Get-NuspecDependencies {
+ param (
+ [string]$NupkgPath,
+ [string]$TargetFramework = ""
+ )
+
+ $tempDir = Join-Path $env:TEMP "nuspec_parse_$(Get-Random)"
+
+ try {
+ # Extract .nupkg (it's a zip file)
+ Expand-Archive -Path $NupkgPath -DestinationPath $tempDir -Force
+
+ # Find .nuspec file
+ $nuspecFile = Get-ChildItem -Path $tempDir -Filter "*.nuspec" -Recurse | Select-Object -First 1
+
+ if (-not $nuspecFile) {
+ Write-Warning "No .nuspec file found in $NupkgPath"
+ return @{}
+ }
+
+ [xml]$nuspec = Get-Content $nuspecFile.FullName
+
+ # Extract package info
+ $packageId = $nuspec.package.metadata.id
+ $version = $nuspec.package.metadata.version
+ Write-Host "Parsing $packageId version $version"
+
+ # Parse dependencies
+ $dependencies = @{}
+ $depGroups = $nuspec.package.metadata.dependencies.group
+
+ if ($depGroups) {
+ # Dependencies are grouped by target framework
+ foreach ($group in $depGroups) {
+ $fx = $group.targetFramework
+ Write-Host " Target Framework: $fx"
+
+ foreach ($dep in $group.dependency) {
+ $depId = $dep.id
+ $depVer = $dep.version
+ # Remove version range brackets if present (e.g., "[2.0.0]" -> "2.0.0")
+ $depVer = $depVer -replace '[\[\]]', ''
+ $dependencies[$depId] = $depVer
+ Write-Host " - $depId : $depVer"
+ }
+ }
+ } else {
+ # No grouping, direct dependencies
+ $deps = $nuspec.package.metadata.dependencies.dependency
+ if ($deps) {
+ foreach ($dep in $deps) {
+ $depId = $dep.id
+ $depVer = $dep.version
+ $depVer = $depVer -replace '[\[\]]', ''
+ $dependencies[$depId] = $depVer
+ Write-Host " - $depId : $depVer"
+ }
+ }
+ }
+
+ return $dependencies
+ }
+ catch {
+ Write-Warning "Failed to parse nuspec: $_"
+ return @{}
+ }
+ finally {
+ Remove-Item $tempDir -Recurse -Force -ErrorAction SilentlyContinue
+ }
+}
+
+function Resolve-ArtifactBasedDependencies {
+ param (
+ [string]$ArtifactDir,
+ [string]$MetaPackageName,
+ [string]$SourceUrl,
+ [string]$OutputDir
+ )
+
+ Write-Host "Resolving dependencies from artifact-based metapackage..."
+ New-Item -ItemType Directory -Path $OutputDir -Force | Out-Null
+
+ # Find the metapackage in artifact
+ $metaNupkg = Get-ChildItem -Path $ArtifactDir -Recurse -Filter "$MetaPackageName.*.nupkg" |
+ Where-Object { $_.Name -notmatch "Runtime" } |
+ Select-Object -First 1
+
+ if (-not $metaNupkg) {
+ Write-Warning "Metapackage $MetaPackageName not found in artifact"
+ return @{}
+ }
+
+ # Extract version from filename
+ if ($metaNupkg.Name -match "$MetaPackageName\.(.+)\.nupkg") {
+ $metaVersion = $Matches[1]
+ Write-Host "Found metapackage: $MetaPackageName version $metaVersion"
+ } else {
+ Write-Warning "Could not extract version from $($metaNupkg.Name)"
+ return @{}
+ }
+
+ # Parse dependencies from metapackage
+ $dependencies = Get-NuspecDependencies -NupkgPath $metaNupkg.FullName
+
+ # Copy metapackage to output directory
+ Copy-Item $metaNupkg.FullName -Destination $OutputDir -Force
+ Write-Host "Copied metapackage to $OutputDir"
+
+ # Copy Runtime package from artifact (it's not in feed)
+ $runtimeNupkg = Get-ChildItem -Path $ArtifactDir -Recurse -Filter "$MetaPackageName.Runtime.*.nupkg" | Select-Object -First 1
+ if ($runtimeNupkg) {
+ Copy-Item $runtimeNupkg.FullName -Destination $OutputDir -Force
+ Write-Host "Copied Runtime package to $OutputDir"
+ }
+
+ # Prepare package versions hashtable
+ $packageVersions = @{ $MetaPackageName = $metaVersion }
+
+ # Download other dependencies from feed (excluding Runtime as it's already copied)
+ # Create temp nuget.config that includes both local packages and remote feed
+ # This allows NuGet to find packages already copied from artifact
+ $tempConfig = Join-Path $env:TEMP "nuget_artifact_$(Get-Random).config"
+ $tempConfigContent = @"
+
+
+
+
+
+
+
+
+"@
+ Set-Content -Path $tempConfig -Value $tempConfigContent
+
+ try {
+ foreach ($depId in $dependencies.Keys) {
+ # Skip Runtime as it's already copied from artifact
+ if ($depId -like "*Runtime*") {
+ $packageVersions[$depId] = $dependencies[$depId]
+ Write-Host "Skipping $depId (already in artifact)"
+ continue
+ }
+
+ $depVersion = $dependencies[$depId]
+ Write-Host "Downloading dependency: $depId version $depVersion from feed..."
+
+ $nugetArgs = "install $depId -Version $depVersion -ConfigFile `"$tempConfig`" -OutputDirectory `"$OutputDir`" -NonInteractive -NoCache"
+ Invoke-Expression "nuget $nugetArgs"
+
+ if ($LASTEXITCODE -eq 0) {
+ $packageVersions[$depId] = $depVersion
+ Write-Host " Successfully downloaded $depId"
+ } else {
+ Write-Warning " Failed to download $depId version $depVersion"
+ }
+ }
+ }
+ finally {
+ Remove-Item $tempConfig -Force -ErrorAction SilentlyContinue
+ }
+
+ # Parse all downloaded packages to get actual versions
+ $directories = Get-ChildItem -Path $OutputDir -Directory
+ $allLocalPackages = @()
+ foreach ($dir in $directories) {
+ if ($dir.Name -match "^(.+?)\.(\d+\..*)$") {
+ $pkgId = $Matches[1]
+ $pkgVer = $Matches[2]
+ $allLocalPackages += $pkgId
+ $packageVersions[$pkgId] = $pkgVer
+ }
+ }
+
+ # Update nuget.config
+ $nugetConfig = Join-Path $rootPath "nuget.config"
+ $configData = Read-FileWithEncoding -Path $nugetConfig
+ [xml]$xml = $configData.Content
+
+ Add-NuGetSourceAndMapping -Xml $xml -Key "localpackages" -Value $OutputDir -Patterns $allLocalPackages
+
+ $xml.Save($nugetConfig)
+ Write-Host "Updated nuget.config with localpackages mapping."
+
+ return $packageVersions
+}
+
function Resolve-WinAppSdkSplitDependencies {
Write-Host "Version $WinAppSDKVersion detected. Resolving split dependencies..."
$installDir = Join-Path $rootPath "localpackages\output"
@@ -172,52 +412,101 @@ function Resolve-WinAppSdkSplitDependencies {
}
}
-# Execute nuget list and capture the output
-if ($useExperimentalVersion) {
- # The nuget list for experimental versions will cost more time
- # So, we will not use -AllVersions to wast time
- # But it can only get the latest experimental version
- Write-Host "Fetching WindowsAppSDK with experimental versions"
- $nugetOutput = nuget list Microsoft.WindowsAppSDK `
- -Source $sourceLink `
- -Prerelease
- # Filter versions based on the specified version prefix
- $escapedVersionNumber = [regex]::Escape($winAppSdkVersionNumber)
- $filteredVersions = $nugetOutput | Where-Object { $_ -match "Microsoft.WindowsAppSDK $escapedVersionNumber\." }
- $latestVersions = $filteredVersions
-} else {
- Write-Host "Fetching stable WindowsAppSDK versions for $winAppSdkVersionNumber"
- $nugetOutput = nuget list Microsoft.WindowsAppSDK `
- -Source $sourceLink `
- -AllVersions
- # Filter versions based on the specified version prefix
- $escapedVersionNumber = [regex]::Escape($winAppSdkVersionNumber)
- $filteredVersions = $nugetOutput | Where-Object { $_ -match "Microsoft.WindowsAppSDK $escapedVersionNumber\." }
- $latestVersions = $filteredVersions | Sort-Object { [version]($_ -split ' ')[1] } -Descending | Select-Object -First 1
-}
+# Main logic: choose between artifact-based or feed-based approach
+if ($useArtifactSource) {
+ Write-Host "=== Using Artifact-Based Source ===" -ForegroundColor Cyan
+ Write-Host "Organization: $azureDevOpsOrg"
+ Write-Host "Project: $azureDevOpsProject"
+ Write-Host "Build ID: $buildId"
+ Write-Host "Artifact: $artifactName"
-Write-Host "Latest versions found: $latestVersions"
-# Extract the latest version number from the output
-$latestVersion = $latestVersions -split "`n" | `
- Select-String -Pattern 'Microsoft.WindowsAppSDK\s*([0-9]+\.[0-9]+\.[0-9]+-*[a-zA-Z0-9]*)' | `
- ForEach-Object { $_.Matches[0].Groups[1].Value } | `
- Sort-Object -Descending | `
- Select-Object -First 1
+ if ([string]::IsNullOrEmpty($buildId)) {
+ Write-Host "Error: buildId parameter is required when using artifact source"
+ exit 1
+ }
-if ($latestVersion) {
- $WinAppSDKVersion = $latestVersion
- Write-Host "Extracted version: $WinAppSDKVersion"
+ # Download artifact
+ $artifactDir = Join-Path $rootPath "localpackages\artifact"
+ $downloadSuccess = Download-ArtifactFromPipeline `
+ -Organization $azureDevOpsOrg `
+ -Project $azureDevOpsProject `
+ -BuildId $buildId `
+ -ArtifactName $artifactName `
+ -OutputDir $artifactDir
+
+ if (-not $downloadSuccess) {
+ Write-Host "Failed to download artifact"
+ exit 1
+ }
+
+ # Resolve dependencies from artifact
+ $installDir = Join-Path $rootPath "localpackages\output"
+ $packageVersions = Resolve-ArtifactBasedDependencies `
+ -ArtifactDir $artifactDir `
+ -MetaPackageName $metaPackageName `
+ -SourceUrl $sourceLink `
+ -OutputDir $installDir
+
+ if ($packageVersions.Count -eq 0) {
+ Write-Host "Failed to resolve dependencies from artifact"
+ exit 1
+ }
+
+ # Extract WinAppSDK version
+ $WinAppSDKVersion = $packageVersions[$metaPackageName]
+ Write-Host "WinAppSDK Version: $WinAppSDKVersion"
Write-Host "##vso[task.setvariable variable=WinAppSDKVersion]$WinAppSDKVersion"
+
} else {
- Write-Host "Failed to extract version number from nuget list output"
- exit 1
+ Write-Host "=== Using Feed-Based Source ===" -ForegroundColor Cyan
+
+ # Execute nuget list and capture the output
+ if ($useExperimentalVersion) {
+ # The nuget list for experimental versions will cost more time
+ # So, we will not use -AllVersions to wast time
+ # But it can only get the latest experimental version
+ Write-Host "Fetching WindowsAppSDK with experimental versions"
+ $nugetOutput = nuget list Microsoft.WindowsAppSDK `
+ -Source $sourceLink `
+ -Prerelease
+ # Filter versions based on the specified version prefix
+ $escapedVersionNumber = [regex]::Escape($winAppSdkVersionNumber)
+ $filteredVersions = $nugetOutput | Where-Object { $_ -match "Microsoft.WindowsAppSDK $escapedVersionNumber\." }
+ $latestVersions = $filteredVersions
+ } else {
+ Write-Host "Fetching stable WindowsAppSDK versions for $winAppSdkVersionNumber"
+ $nugetOutput = nuget list Microsoft.WindowsAppSDK `
+ -Source $sourceLink `
+ -AllVersions
+ # Filter versions based on the specified version prefix
+ $escapedVersionNumber = [regex]::Escape($winAppSdkVersionNumber)
+ $filteredVersions = $nugetOutput | Where-Object { $_ -match "Microsoft.WindowsAppSDK $escapedVersionNumber\." }
+ $latestVersions = $filteredVersions | Sort-Object { [version]($_ -split ' ')[1] } -Descending | Select-Object -First 1
+ }
+
+ Write-Host "Latest versions found: $latestVersions"
+ # Extract the latest version number from the output
+ $latestVersion = $latestVersions -split "`n" | `
+ Select-String -Pattern 'Microsoft.WindowsAppSDK\s*([0-9]+\.[0-9]+\.[0-9]+-*[a-zA-Z0-9]*)' | `
+ ForEach-Object { $_.Matches[0].Groups[1].Value } | `
+ Sort-Object -Descending | `
+ Select-Object -First 1
+
+ if ($latestVersion) {
+ $WinAppSDKVersion = $latestVersion
+ Write-Host "Extracted version: $WinAppSDKVersion"
+ Write-Host "##vso[task.setvariable variable=WinAppSDKVersion]$WinAppSDKVersion"
+ } else {
+ Write-Host "Failed to extract version number from nuget list output"
+ exit 1
+ }
+
+ # Resolve dependencies for 1.8+
+ $packageVersions = @{ "Microsoft.WindowsAppSDK" = $WinAppSDKVersion }
+
+ Resolve-WinAppSdkSplitDependencies
}
-# Resolve dependencies for 1.8+
-$packageVersions = @{ "Microsoft.WindowsAppSDK" = $WinAppSDKVersion }
-
-Resolve-WinAppSdkSplitDependencies
-
# Update Directory.Packages.props file
Get-ChildItem -Path $rootPath -Recurse "Directory.Packages.props" | ForEach-Object {
$file = Read-FileWithEncoding -Path $_.FullName
diff --git a/.pipelines/v2/ci-using-the-latest-winappsdk.yml b/.pipelines/v2/ci-using-the-latest-winappsdk.yml
index 16639f44c0..a78c4a0305 100644
--- a/.pipelines/v2/ci-using-the-latest-winappsdk.yml
+++ b/.pipelines/v2/ci-using-the-latest-winappsdk.yml
@@ -37,6 +37,23 @@ parameters:
- name: useExperimentalVersion
type: boolean
default: false
+ # Artifact mode parameters (optional)
+ - name: useArtifactSource
+ type: boolean
+ displayName: "Use Artifact Source (instead of feed)"
+ default: false
+ - name: buildId
+ type: string
+ displayName: "Windows App SDK Build ID (for artifact mode)"
+ default: ''
+ - name: azureDevOpsProject
+ type: string
+ displayName: "Source Project (for artifact mode)"
+ default: 'ProjectReunion'
+ - name: artifactName
+ type: string
+ displayName: "Artifact Name (for artifact mode)"
+ default: 'WindowsAppSDK_Nuget_And_MSIX'
extends:
template: templates/pipeline-ci-build.yml
@@ -49,3 +66,7 @@ extends:
useLatestWinAppSDK: ${{ parameters.useLatestWinAppSDK }}
winAppSDKVersionNumber: ${{ parameters.winAppSDKVersionNumber }}
useExperimentalVersion: ${{ parameters.useExperimentalVersion }}
+ useArtifactSource: ${{ parameters.useArtifactSource }}
+ buildId: ${{ parameters.buildId }}
+ azureDevOpsProject: ${{ parameters.azureDevOpsProject }}
+ artifactName: ${{ parameters.artifactName }}
diff --git a/.pipelines/v2/templates/job-build-project.yml b/.pipelines/v2/templates/job-build-project.yml
index e41bfbc0ad..c5cd2ffd0c 100644
--- a/.pipelines/v2/templates/job-build-project.yml
+++ b/.pipelines/v2/templates/job-build-project.yml
@@ -74,6 +74,25 @@ parameters:
- name: useExperimentalVersion
type: boolean
default: false
+ # Artifact mode parameters
+ - name: useArtifactSource
+ type: boolean
+ default: false
+ - name: azureDevOpsOrg
+ type: string
+ default: 'https://dev.azure.com/microsoft'
+ - name: azureDevOpsProject
+ type: string
+ default: 'ProjectReunion'
+ - name: buildId
+ type: string
+ default: ''
+ - name: artifactName
+ type: string
+ default: 'WindowsAppSDK_Nuget_And_MSIX'
+ - name: metaPackageName
+ type: string
+ default: 'Microsoft.WindowsAppSDK'
- name: csProjectsToPublish
type: object
default:
@@ -226,6 +245,12 @@ jobs:
parameters:
versionNumber: ${{ parameters.winAppSDKVersionNumber }}
useExperimentalVersion: ${{ parameters.useExperimentalVersion }}
+ useArtifactSource: ${{ parameters.useArtifactSource }}
+ azureDevOpsOrg: ${{ parameters.azureDevOpsOrg }}
+ azureDevOpsProject: ${{ parameters.azureDevOpsProject }}
+ buildId: ${{ parameters.buildId }}
+ artifactName: ${{ parameters.artifactName }}
+ metaPackageName: ${{ parameters.metaPackageName }}
- ${{ if eq(parameters.useLatestWinAppSDK, false)}}:
- template: .\steps-restore-nuget.yml
diff --git a/.pipelines/v2/templates/pipeline-ci-build.yml b/.pipelines/v2/templates/pipeline-ci-build.yml
index a56c575399..f4ac4e161f 100644
--- a/.pipelines/v2/templates/pipeline-ci-build.yml
+++ b/.pipelines/v2/templates/pipeline-ci-build.yml
@@ -34,6 +34,25 @@ parameters:
- name: useExperimentalVersion
type: boolean
default: false
+ # Artifact mode parameters
+ - name: useArtifactSource
+ type: boolean
+ default: false
+ - name: azureDevOpsOrg
+ type: string
+ default: 'https://dev.azure.com/microsoft'
+ - name: azureDevOpsProject
+ type: string
+ default: 'ProjectReunion'
+ - name: buildId
+ type: string
+ default: ''
+ - name: artifactName
+ type: string
+ default: 'WindowsAppSDK_Nuget_And_MSIX'
+ - name: metaPackageName
+ type: string
+ default: 'Microsoft.WindowsAppSDK'
stages:
- ${{ each platform in parameters.buildPlatforms }}:
@@ -65,6 +84,12 @@ stages:
${{ if eq(parameters.useLatestWinAppSDK, true) }}:
winAppSDKVersionNumber: ${{ parameters.winAppSDKVersionNumber }}
useExperimentalVersion: ${{ parameters.useExperimentalVersion }}
+ useArtifactSource: ${{ parameters.useArtifactSource }}
+ azureDevOpsOrg: ${{ parameters.azureDevOpsOrg }}
+ azureDevOpsProject: ${{ parameters.azureDevOpsProject }}
+ buildId: ${{ parameters.buildId }}
+ artifactName: ${{ parameters.artifactName }}
+ metaPackageName: ${{ parameters.metaPackageName }}
timeoutInMinutes: 90
- stage: Build_SDK
diff --git a/.pipelines/v2/templates/steps-update-winappsdk-and-restore-nuget.yml b/.pipelines/v2/templates/steps-update-winappsdk-and-restore-nuget.yml
index 566c8045c4..3b42520fef 100644
--- a/.pipelines/v2/templates/steps-update-winappsdk-and-restore-nuget.yml
+++ b/.pipelines/v2/templates/steps-update-winappsdk-and-restore-nuget.yml
@@ -5,6 +5,25 @@ parameters:
- name: useExperimentalVersion
type: boolean
default: false
+ # Artifact mode parameters
+ - name: useArtifactSource
+ type: boolean
+ default: false
+ - name: azureDevOpsOrg
+ type: string
+ default: 'https://dev.azure.com/microsoft'
+ - name: azureDevOpsProject
+ type: string
+ default: 'ProjectReunion'
+ - name: buildId
+ type: string
+ default: ''
+ - name: artifactName
+ type: string
+ default: 'WindowsAppSDK_Nuget_And_MSIX'
+ - name: metaPackageName
+ type: string
+ default: 'Microsoft.WindowsAppSDK'
steps:
- task: NuGetAuthenticate@1
@@ -18,6 +37,12 @@ steps:
-winAppSdkVersionNumber ${{ parameters.versionNumber }}
-useExperimentalVersion $${{ parameters.useExperimentalVersion }}
-rootPath "$(build.sourcesdirectory)"
+ -useArtifactSource $${{ parameters.useArtifactSource }}
+ -azureDevOpsOrg "${{ parameters.azureDevOpsOrg }}"
+ -azureDevOpsProject "${{ parameters.azureDevOpsProject }}"
+ -buildId "${{ parameters.buildId }}"
+ -artifactName "${{ parameters.artifactName }}"
+ -metaPackageName "${{ parameters.metaPackageName }}"
# - task: NuGetCommand@2
# displayName: 'Restore NuGet packages (slnx)'
diff --git a/nuget.config b/nuget.config
index 6b8d13a023..39fe232c39 100644
--- a/nuget.config
+++ b/nuget.config
@@ -3,6 +3,7 @@
+