Files

718 lines
29 KiB
PowerShell

#!/bin/env pwsh
#Requires -Version 7
[CmdletBinding()]
param(
[string] $PublishTarget,
[int] $BuildId,
[string] $OutputPath,
[string] $ServerName,
[switch] $IncludeNative,
[switch] $TestPipeline,
[switch] $CI,
[bool] $CommonCodeBuildsAll = $true
)
. "$PSScriptRoot/../common/scripts/common.ps1"
. "$PSScriptRoot/helpers/BuildHelpers.ps1"
. "$PSScriptRoot/helpers/VsixVersionHelpers.ps1"
$RepoRoot = $RepoRoot.Path.Replace('\', '/')
$isPipelineRun = $CI -or $env:TF_BUILD -eq 'true'
$isPullRequestBuild = $env:BUILD_REASON -eq 'PullRequest'
$exitCode = 0
$architectures = @('x64', 'arm64')
# Supported Azure clouds for where the MCP tools may operate. This is used to determine which clouds to run tests against.
# The set of valid values in this list should align with the ones in eng\common\TestResources\New-TestResources.ps1 line 66.
$azureSupportedClouds = @('AzureCloud', 'AzureUSGovernment', 'AzureChinaCloud')
# Get-OperatingSystems returns an array of objects with properties: name, nodeName, dotnetName, extension
$operatingSystems = Get-OperatingSystems
# Platform names, e.g. windows-arm64, from the standard $operatingSystems X $architectures combinations that should not be built.
$excludedPlatforms = @(
# Currently, all standard platforms are included
)
# Platforms outside of the standard combinations that should also be built. Setting a "specialPurpose" allows then to
# be targeted or excluded in packaging scripts
$additionalPlatforms = @(
# Add additional builds for Docker as they use the musl runtime variant which differs from the default Linux builds.
# In the future we can evaluate if these can be moved to the standard Linux builds to remove two jobs from our
# standard pipeline.
@{
name = 'linux-musl-x64-docker'
operatingSystem = 'linux'
architecture = 'musl-x64'
native = $false
trimmed = $true
specialPurpose = 'docker'
}
@{
name = 'linux-musl-arm64-docker'
operatingSystem = 'linux'
architecture = 'musl-arm64'
native = $false
trimmed = $true
specialPurpose = 'docker'
}
)
if ($IncludeNative) {
# We currently only want to build linux-x64 native
# When native builds are shipped, we still may want to build only linux-x64 native in pull requests for pipeline performance
$additionalPlatforms += @{
name = 'linux-x64-native'
operatingSystem = 'linux'
architecture = 'x64'
native = $true
trimmed = $false
specialPurpose = 'native'
}
}
if ($BuildId -eq 0) {
if ($isPipelineRun) {
LogError 'A non-zero BuildId is required when running in a pipeline.'
$exitCode = 1
}
else {
$BuildId = 99999
}
}
if ($isPipelineRun -and !$PublishTarget) {
LogError 'PublishTarget parameter is required when running in a pipeline.'
$exitCode = 1
}
if (!$OutputPath) {
$OutputPath = "$RepoRoot/.work/build_info.json"
}
$serverDirectories = Get-ChildItem "$RepoRoot/servers" -Directory
$toolDirectories = Get-ChildItem "$RepoRoot/tools" -Directory
$coreDirectories = Get-ChildItem "$RepoRoot/core" -Directory
# Public releases always use the version from the repo without a dynamic prerelease suffix, except for test pipelines
# which always use a dynamic prerelease suffix to allow for multiple releases from the same commit
$dynamicPrereleaseVersion = $PublishTarget -ne 'public' -or $TestPipeline
function CheckVariable($name) {
$value = [Environment]::GetEnvironmentVariable($name)
if (-not $value) {
if ($isPipelineRun) {
LogError "Environment variable $name is not set."
$script:exitCode = 1
return ""
}
else {
return "Missing-$name"
}
}
return $value
}
$windowsPool = CheckVariable 'WINDOWSPOOL'
$linuxPool = CheckVariable 'LINUXPOOL'
$linuxArm64Pool = CheckVariable 'LINUXARM64POOL'
$macPool = CheckVariable 'MACPOOL'
$windowsVmImage = CheckVariable 'WINDOWSVMIMAGE'
$linuxVmImage = CheckVariable 'LINUXVMIMAGE'
$linuxArm64VmImage = CheckVariable 'LINUXARM64VMIMAGE'
$macVmImage = CheckVariable 'MACVMIMAGE'
<# This function takes a semicolon or comma delimited string and splits it into an array,
trimming whitespace and removing empty entries.
For example, the NpmPackageKeywords property may be defined in a csproj as:
<NpmPackageKeywords>keyword1; keyword2, keyword3</NpmPackageKeywords>
This function will split that string into an array: @('keyword1', 'keyword2', 'keyword3')
#>
function Split-PropertyGroup {
param([string]$propertyGroup)
return @($propertyGroup -split '[;,]' | ForEach-Object { $_.Trim() } | Where-Object { $_ -ne '' })
}
function Get-PathsToTest {
Write-Host "Getting paths to test"
# While there is a "core" directory at the repo root, we consider the "core" path to be all of the repo outside of the
# "tools" directory.
# This lets us make simple statements like:
# - Changes in eng/ are "core" changes
# - Changes in core/ are "core" changes
# - Changes to tools/Azure.Mcp.Tools.Redis are "Azure.Mcp.Tools.Redis" changes
# - If you change any "core" files, we need to test all of the "core" path as well as a few canary tools
# - If you change just tool files, we need to test the tools you changed
# If the caller passed in a ServiceName, then only the tools that the server depends on are in scope to test
# Otherwise, all tools in the tools/ directory are in scope
$paths = if ($ServerName) {
Write-Host "Filtering list of test paths using project references for $serverName"
$serverProject = "$RepoRoot/servers/$ServerName/src/$ServerName.csproj"
if (-not (Test-Path $serverProject)) {
LogError "No project for $ServerName found at $serverProject"
$script:exitCode = 1
return @()
}
$projectReferences = (dotnet build $serverProject -getItem:ProjectReference | ConvertFrom-Json).Items.ProjectReference.FullPath
# We can put full paths here because they'll be reduced to relative project directory paths in the "reduce down" step below
@() + $serverProject + $projectReferences
}
else {
@() + $coreDirectories + $serverDirectories + $toolDirectories
}
# Reduce down to paths like:
# tools/Azure.Mcp.Tools.Storage
# core/Fabric.Mcp.Core
# servers/Azure.Mcp.Server
$projectDirectoryPattern = '^(tools|servers|core)/[^/]+'
$normalizedPaths = $paths
| Get-RepoRelativePath -NormalizeSeparators
| Where-Object { $_ -match $projectDirectoryPattern }
| ForEach-Object { $Matches[0] }
| Sort-Object -Unique
if ($isPullRequestBuild) {
# Set of files that don't require build or test when changed
$skipFiles = @(
'CHANGELOG.md',
'README.md',
'SUPPORT.md',
'TROUBLESHOOTING.md',
'CONTRIBUTING.md',
'CODE_OF_CONDUCT.md',
'SECURITY.md',
'NOTICE.txt',
'LICENSE'
)
# If we're in a pull request, use the set of changed files to narrow down the set of paths to test.
$changedFiles = Get-ChangedFiles
# When common code builds all, track whether engineering, the Core libraries, or shared build changed. If so, build everything.
$coreChanged = $CommonCodeBuildsAll -and ($changedFiles | Where-Object { $_ -match '^core/(Azure|Fabric|Microsoft).Mcp.Core/src/' }).Count -gt 0
$engChanged = $CommonCodeBuildsAll -and ($changedFiles | Where-Object { $_ -match '^eng/' }).Count -gt 0
$sharedBuildChanged = $CommonCodeBuildsAll -and ($changedFiles | Where-Object { $_ -match '^Directory.(Build|Packages).props' }).Count -gt 0
if ($coreChanged -or $engChanged -or $sharedBuildChanged) {
Write-Host "Core, engineering, or shared build changes detected. Building everything." -ForegroundColor Yellow
$pathsToTest = @()
}
else {
# Assuming $changedFiles = [
# tools/Azure.Mcp.Tools.Storage/src/someFile.cs <- "Azure.Mcp.Tools.Storage"
# tools/Azure.Mcp.Tools.Monitoring/README.md <- "Azure.Mcp.Tools.Monitoring"
# core/src/commonClass.cs <- "Core"
# eng/scripts/SomeScript.ps1 <- "Core"
# ]
Write-Host ''
# Currently, we don't exclude non-code files from the changed files list.
# For example, updating a markdown file in a service path will still trigger tests for that path.
# Updating a file outside of the defined paths will be seen as a change to the core path.
$changedPaths = @($changedFiles
| Where-Object { $skipFiles -notcontains (Split-Path $_ -Leaf) }
| ForEach-Object { $_ -match $projectDirectoryPattern -and $normalizedPaths -contains $Matches[0] ? $Matches[0] : 'core/Microsoft.Mcp.Core' }
| Sort-Object -Unique)
<# This makes $changedPaths = @(
'tools/Azure.Mcp.Tools.Storage',
'tools/Azure.Mcp.Tools.Monitoring',
'core/Microsoft.Mcp.Core'
) #>
if ($changedPaths.Count -eq 0) {
Write-Host "No changed, testable paths detected. Defaulting to core." -ForegroundColor Yellow
$changedPaths = @('core/Microsoft.Mcp.Core')
}
else {
Write-Host "Changed paths detected: $($changedPaths -join ', ')"
}
if ($pathsToTest -notcontains 'core/Microsoft.Mcp.Core') {
$pathsToTest = $changedPaths
}
# Always include Azure.Mcp.Server to run ConsolidatedModeTests.cs in all PRs
if ($pathsToTest -notcontains 'servers/Azure.Mcp.Server') {
Write-Host "Adding servers/Azure.Mcp.Server to test paths for PR validation" -ForegroundColor Cyan
$pathsToTest += 'servers/Azure.Mcp.Server'
}
$normalizedPaths = @($pathsToTest | Sort-Object -Unique)
<# Making $paths = @(
'tools/Azure.Mcp.Tools.Storage',
'tools/Azure.Mcp.Tools.Monitoring',
'core/Microsoft.Mcp.Core',
'tools/Azure.Mcp.Tools.KeyVault' <-- from Microsoft.Mcp.Core's server canary list
) #>
}
}
$pathsToTest = $normalizedPaths | ForEach-Object -ThrottleLimit 5 -Parallel {
$path = $_
$azureSupportedClouds = $using:azureSupportedClouds
Write-Progress -Activity "Checking for test resources" -Status $path
$projectName = (Get-Item $path).Name
$testResourcesPath = "$path/tests"
$rootedTestResourcesPath = "$($using:RepoRoot)/$testResourcesPath"
$hasTestResources = Test-Path "$rootedTestResourcesPath/test-resources.bicep"
$hasTestsProject = Test-Path "$rootedTestResourcesPath/$projectName.Tests/$projectName.Tests.csproj"
$testProjectDetails = $hasTestsProject ? (& "$($using:PSScriptRoot)/Get-ProjectProperties.ps1" -Path "$rootedTestResourcesPath/$projectName.Tests/$projectName.Tests.csproj") : $null
$result = $false
# Need to parse $testProjectDetails.HasLiveTests and HasUnitTests as they're based on JSON values, therefore will not be a PowerShell boolean
$hasUnitTests = $hasTestsProject -and [bool]::TryParse($testProjectDetails.HasUnitTests, [ref]$result) -and $result
$hasLiveTests = $hasTestsProject -and [bool]::TryParse($testProjectDetails.HasLiveTests, [ref]$result) -and $result
$hasRecordedTests = $hasLiveTests -and (Get-ChildItem $rootedTestResourcesPath -Filter 'assets.json' -Recurse).Count -gt 0
$sourcePath = Join-Path $using:RepoRoot $path "src"
$sourceProject = Get-ChildItem $sourcePath -Filter '*.csproj' | Select-Object -First 1
if (-not $sourceProject) {
Write-Error "No source project found for path $path at expected location $sourcePath. Ensure there is a .csproj file in the src directory for this path."
return @{ _error = $true }
}
$sourceProjectDetails = & "$($using:PSScriptRoot)/Get-ProjectProperties.ps1" -Path $sourceProject.FullName
$resolvedClouds = $sourceProjectDetails.AzureSupportedClouds `
? @($sourceProjectDetails.AzureSupportedClouds -split '[;,] *' | ForEach-Object { $_.Trim() } | Where-Object { $_ -ne '' })
: $azureSupportedClouds
if ($sourceProjectDetails.AzureSupportedClouds -and ($resolvedClouds | Where-Object { $azureSupportedClouds -notcontains $_ })) {
Write-Error "Project $($sourceProject.FullName) specifies supported Azure clouds that are not in the global supported list: $($sourceProjectDetails.AzureSupportedClouds). Supported clouds must be a subset of $($azureSupportedClouds -join ', ')."
return @{ _error = $true }
}
return @{
_error = $false
path = $path
hasTestResources = $hasTestResources
testResourcesPath = $hasTestResources ? $testResourcesPath : $null
hasLiveTests = $hasLiveTests
hasUnitTests = $hasUnitTests
hasRecordedTests = $hasRecordedTests
azureSupportedClouds = $resolvedClouds
}
}
if ($pathsToTest | Where-Object { $_._error }) {
$script:exitCode = 1
}
$pathsToTest = $pathsToTest | Where-Object { -not $_._error } | ForEach-Object { $_.Remove('_error'); $_ } | Sort-Object { $_.path }
return $pathsToTest
}
function Get-TestMatrix {
param(
[hashtable[]] $pathsToTest,
[ValidateSet('Unit', 'Live')]
[string] $TestType
)
Write-Host "Forming $($TestType.ToLower()) test matrix"
$testMatrix = [ordered]@{}
foreach ($path in $pathsToTest) {
$entry = [ordered]@{
# We can't use the name 'Path' here because it would override the Path environment variable in matrix based jobs
pathToTest = $path.Path
}
if ($TestType -eq 'Live') {
if (!$path.HasLiveTests -or !$path.HasTestResources) {
continue
}
$entry.testResourcesPath = $path.TestResourcesPath
$entry.hasTestResources = $path.HasTestResources
if ($ServerName) {
$entry.serverName = $ServerName
}
}
if ($TestType -eq 'Unit' -and !$path.HasUnitTests) {
continue
}
$testMatrix[$path.Path] = $entry
}
return $testMatrix
}
function Get-ServerDetails {
Write-Host "Getting server details"
$searchDirectories = $serverDirectories
if ($ServerName) {
$searchDirectories = $serverDirectories | Where-Object { $_.Name -ieq $ServerName }
if ($searchDirectories.Count -eq 0) {
LogError "No server directory found with name $ServerName in $RepoRoot/servers."
$script:exitCode = 1
return @()
}
}
$serverProjects = $searchDirectories | Get-ChildItem -Filter "src/*.csproj"
$serverProperties = @()
foreach ($serverProject in $serverProjects) {
$props = & "$PSScriptRoot/Get-ProjectProperties.ps1" -Path $serverProject
$serverName = $serverProject.BaseName
$version = [AzureEngSemanticVersion]::new($props.Version)
if ($dynamicPrereleaseVersion) {
$version.PrereleaseLabel = 'alpha'
$version.PrereleaseNumber = $BuildId
}
# Calculate VSIX version based on server version
$vsixVersion = $null
$vsixIsPrerelease = $false
# If SETDEVVERSION is true, use BuildId as patch number (dev builds)
if ($env:SETDEVVERSION -eq "true") {
# VS Code Marketplace doesn't support pre-release versions with semantic versioning suffixes
# For dev builds, we strip the prerelease label and use BuildId as patch number
$vsixVersion = "$($version.Major).$($version.Minor).$BuildId"
$vsixIsPrerelease = $false
Write-Host "SETDEVVERSION is true, using BuildId as patch number for VSIX: $($version.ToString()) -> $vsixVersion" -ForegroundColor Yellow
}
elseif ($PublishTarget -eq 'public') {
$packageJsonPath = "$RepoRoot/servers/$serverName/vscode/package.json"
try {
$resolvedVsixVersion = Resolve-PublicVsixVersion `
-ServerName $serverName `
-ServerVersion $version.ToString() `
-PackageJsonPath $packageJsonPath
$vsixVersion = $resolvedVsixVersion.Version
$vsixIsPrerelease = $resolvedVsixVersion.IsPrerelease
if ($resolvedVsixVersion.Source -eq 'Marketplace') {
Write-Host "Marketplace latest: $($resolvedVsixVersion.MarketplaceLatestVersion) -> Next VSIX version: $vsixVersion" -ForegroundColor Green
}
else {
Write-Host "Resolved public VSIX version: $($version.ToString()) -> $vsixVersion ($($resolvedVsixVersion.Source))" -ForegroundColor Green
}
}
catch {
LogError $_.Exception.Message
$script:exitCode = 1
continue
}
}
else {
# For non-public builds without SETDEVVERSION, use a placeholder version
$vsixVersion = "$($version.Major).0.999"
$vsixIsPrerelease = $false
Write-Host "Non-public target without SETDEVVERSION: Using placeholder VSIX version $vsixVersion" -ForegroundColor Yellow
}
$platforms = @()
foreach ($os in $operatingSystems) {
foreach ($arch in $architectures) {
$name = "$($os.name)-$arch"
if ($excludedPlatforms -notcontains $name) {
$platforms += [ordered]@{
name = $name
artifactPath = "$serverName/$name"
operatingSystem = $os.name
nodeOs = $os.nodeName
dotnetOs = $os.dotnetName
architecture = $arch
extension = $os.extension
native = $false
trimmed = $true
}
}
}
}
foreach ($additionalPlatform in $additionalPlatforms) {
$name = $additionalPlatform.name
$os = $operatingSystems | Where-Object { $_.name -eq $additionalPlatform.operatingSystem }
if (-not $os) {
LogError "Additional platform $name has unknown operating system $($additionalPlatform.operatingSystem)"
$script:exitCode = 1
continue
}
$platforms += [ordered]@{
name = $name
artifactPath = "$serverName/$name"
operatingSystem = $os.name
nodeOs = $os.nodeName
dotnetOs = $os.dotnetName
architecture = $additionalPlatform.architecture
extension = $os.extension
native = $additionalPlatform.native
trimmed = $additionalPlatform.trimmed
specialPurpose = $additionalPlatform.specialPurpose
}
}
$serverProperties += [ordered]@{
name = $serverProject.BaseName
path = $serverProject | Get-RepoRelativePath -NormalizeSeparators
artifactPath = $serverName
version = $version.ToString()
vsixVersion = $vsixVersion
vsixIsPrerelease = $vsixIsPrerelease
releaseTag = "$serverName-$version"
cliName = $props.CliName
assemblyTitle = $props.AssemblyTitle
description = $props.Description
readmeUrl = $props.ReadmeUrl
readmePath = $props.ReadmePath | Get-RepoRelativePath -NormalizeSeparators
packageIcon = $props.PackageIcon | Get-RepoRelativePath -NormalizeSeparators
npmPackageName = $props.NpmPackageName
npmDescription = $props.NpmDescription
npmPackageKeywords = Split-PropertyGroup $props.NpmPackageKeywords
dockerImageName = $props.DockerImageName
dockerDescription = $props.DockerDescription
dnxPackageId = $props.DnxPackageId
dnxDescription = $props.DnxDescription
dnxToolCommandName = $props.DnxToolCommandName
dnxPackageTags = Split-PropertyGroup $props.DnxPackageTags
pypiPackageName = $props.PypiPackageName
pypiDescription = $props.PypiDescription
pypiPackageKeywords = Split-PropertyGroup $props.PypiPackageKeywords
platforms = $platforms
mcpRepositoryName = $props.McpRepositoryName
mcpbPlatforms = Split-PropertyGroup $props.McpbPlatforms
serverJsonPath = $props.ServerJsonPath | Get-RepoRelativePath -NormalizeSeparators
}
}
return $serverProperties
}
function Get-BuildMatrices {
param($servers, $pathsToTest)
Write-Host "Forming build matrices"
$matrices = [ordered]@{}
foreach ($os in $operatingSystems.name) {
$buildMatricesByArch = [ordered]@{}
$smokeTestMatrix = [ordered]@{}
$supportedPlatforms = $servers.platforms
| Where-Object { $_.operatingSystem -eq $os }
# Reduce the platform objects to unique combinations of architecture, native, and trimmed
# Select-Object -Unique doesn't work here because we're working with hashtable
| Sort-Object { "$($_.architecture)-$(!$_.native)-$($_.specialPurpose)" } -Descending -Unique # x64 before arm64, non-native before native, non-special before special purpose
foreach ($platform in $supportedPlatforms) {
$arch = $platform.architecture
$legName = $platform.name -replace '\W', '_' # e.g. linux-arm64 or windows-x64-native
if ($excludedPlatforms -contains $platform.name) {
Write-Host "Excluding build leg $legName"
continue
}
# Only linux-arm64 (non-special-purpose) needs actual ARM64 hardware.
# All other arm64 targets (windows-arm64, macos-arm64, linux-musl-arm64-docker) cross-compile on x64.
$needsArm64Hardware = $os -eq 'linux' -and $arch -like '*arm64*' -and !$platform.specialPurpose
$pool = switch ($os) {
'windows' { $windowsPool }
'linux' { if ($needsArm64Hardware) { $linuxArm64Pool } else { $linuxPool } }
'macos' { $macPool }
}
$vmImage = switch ($os) {
'windows' { $windowsVmImage }
'linux' { if ($needsArm64Hardware) { $linuxArm64VmImage } else { $linuxVmImage } }
'macos' { $macVmImage }
}
# we do not currently have a method to get an arm64 mac or windows agent at this time, so we will have to skip $runUnitTests for those platforms
# if a set of unit tests exists, we should run them
$runUnitTests = !!($pathsToTest | Where-Object { $_.hasUnitTests -or $_.hasRecordedTests })
# except for certain platforms
if ($platform.native -or $platform.specialPurpose -or ($arch -like '*arm64*' -and $os -ne 'linux')) {
$runUnitTests = $false
}
$publishCoverage = $runUnitTests -and -not ($arch -like '*arm64*')
$hostArchitecture = if ($needsArm64Hardware) { 'Arm64' } else { '' }
$architectureKey = if ($needsArm64Hardware) { 'arm64' } else { 'x64' }
if (-not $buildMatricesByArch.Contains($architectureKey)) {
$buildMatricesByArch[$architectureKey] = [ordered]@{}
}
$buildMatricesByArch[$architectureKey][$legName] = [ordered]@{
BuildPlatformName = $platform.name
Pool = $pool
OSVmImage = $vmImage
HostArchitecture = $hostArchitecture
RunUnitTests = $runUnitTests
PublishCoverage = $publishCoverage
}
if ($runUnitTests) {
$smokeTestMatrix[$legName] = [ordered]@{
Pool = $pool
OSVmImage = $vmImage
HostArchitecture = $hostArchitecture
Architecture = $arch
}
}
}
foreach ($requiredArch in @('x64', 'arm64')) {
if (-not $buildMatricesByArch.Contains($requiredArch)) {
$buildMatricesByArch[$requiredArch] = [ordered]@{}
}
}
$matrices["${os}BuildMatrices"] = $buildMatricesByArch
$matrices["${os}SmokeTestMatrix"] = $smokeTestMatrix
}
return $matrices
}
function Get-ServerMatrix {
param($servers)
Write-Host "Forming server matrix"
$serverMatrix = [ordered]@{}
# Docker architecture configurations
# {linux/amd64, linux/arm64} is the most common multi-arch combo in Docker, covers almost all
# production use cases, so most official images publish these two.
$dockerArchConfigs = @(
@{
Architecture = 'amd64'
PlatformName = 'linux-musl-x64-docker'
Pool = $linuxPool
VMImage = $linuxVmImage
}
@{
Architecture = 'arm64'
PlatformName = 'linux-musl-arm64-docker'
Pool = $linuxArm64Pool
VMImage = $linuxArm64VmImage
}
)
foreach ($server in $servers) {
$imageName = $server.dockerImageName
if (-not $server.dockerImageName) { $imageName = "microsoft/" + $server.cliName + "-mcp" }
foreach ($archConfig in $dockerArchConfigs) {
$platform = $server.platforms | Where-Object { $_.name -eq $archConfig.PlatformName -and -not $_.native }
$executableExtension = $platform.extension ?? ''
$matrixKey = "$($server.name)_$($archConfig.Architecture)"
$serverMatrix[$matrixKey] = [ordered]@{
ServerName = $server.name
CliName = $server.cliName
ArtifactPath = $server.artifactPath
Version = $server.version
ImageName = $imageName
ExecutableName = $server.cliName + $executableExtension
DockerLocalTag = $imageName + ":" + $BuildId
# Docker build configuration
Platform = $archConfig.PlatformName
Architecture = $archConfig.Architecture
Pool = $archConfig.Pool
VMImage = $archConfig.VMImage
}
}
}
return $serverMatrix
}
Push-Location $RepoRoot
try {
$serverDetails = @(Get-ServerDetails)
$pathsToTest = @(Get-PathsToTest)
$matrices = Get-BuildMatrices $serverDetails $pathsToTest
$matrices['liveTestMatrix'] = Get-TestMatrix $pathsToTest -TestType 'Live'
$matrices['serverMatrix'] = Get-ServerMatrix $serverDetails
# spellchecker: ignore SOURCEVERSION
$branch = $isPipelineRun ? (CheckVariable 'BUILD_SOURCEBRANCH') : (git rev-parse --abbrev-ref HEAD)
$commitSha = $isPipelineRun ? (CheckVariable 'BUILD_SOURCEVERSION') : (git rev-parse HEAD)
if ($isPipelineRun) {
foreach ($key in $matrices.Keys) {
if ($isPullRequestBuild -and $pathsToTest.Count -eq 0) {
if ($key -match 'BuildMatrices$') {
$emptyByArch = [ordered]@{}
foreach ($archKey in @('x64', 'arm64')) {
$emptyByArch[$archKey] = @{}
}
$matrices[$key] = $emptyByArch
}
else {
$matrices[$key] = @{}
}
}
$value = $matrices[$key]
if ($key -match 'BuildMatrices$' -and $value -is [System.Collections.IDictionary]) {
foreach ($subKey in $value.Keys) {
$subJson = $value[$subKey] | ConvertTo-Json -Compress
Write-Host "##vso[task.setvariable variable=${key}.${subKey};isOutput=true]$subJson"
}
}
$matrixJson = $value | ConvertTo-Json -Compress
Write-Host "##vso[task.setvariable variable=${key};isOutput=true]$matrixJson"
}
}
$buildInfo = [ordered]@{
buildId = $BuildId
publishTarget = $PublishTarget
dynamicPrereleaseVersion = $dynamicPrereleaseVersion
repositoryUrl = 'https://github.com/microsoft/mcp'
branch = $branch
commitSha = $commitSha
servers = $serverDetails
pathsToTest = $pathsToTest
matrices = $matrices
}
Write-Host "Writing build info to $OutputPath"
$parentDirectory = Split-Path $OutputPath -Parent
New-Item -Path $parentDirectory -ItemType Directory -Force | Out-Null
$buildInfo | ConvertTo-Json -Depth 5 | Out-File -FilePath $OutputPath -Encoding utf8 -Force
}
finally {
Pop-Location
}
exit $exitCode