From d1642c83b01df8c9b0449e8eb0f606ebe4212262 Mon Sep 17 00:00:00 2001
From: Joshua Larkin <70237359+j0shuams@users.noreply.github.com>
Date: Thu, 16 Dec 2021 13:42:58 -0800
Subject: [PATCH] Static YML build pipeline (#1065)
* Templates, maestro
---
.../CsWinRT-BuildAndTest-Stage.yml | 427 ++++++++++++++++++
.../CsWinRT-Pipeline.yml | 11 +
.../CsWinRT-PublishToMaestro-Stage.yml | 49 ++
.../CsWinRT-PublishToNuget-Stage.yml | 261 +++++++++++
docs/structure.md | 9 +
.../Maestro-PublishBuildToMaestro-Steps.yml | 201 +++++++++
.../ConvertVersionDetailsToPackageConfig.ps1 | 33 ++
eng/common/scripts/MaestroGetRequest.ps1 | 34 ++
eng/common/scripts/MaestroHelpers.ps1 | 81 ++++
eng/common/scripts/MaestroPostRequest.ps1 | 40 ++
.../scripts/UpdateVersionDetailsConfig.ps1 | 34 ++
11 files changed, 1180 insertions(+)
create mode 100644 build/AzurePipelineTemplates/CsWinRT-BuildAndTest-Stage.yml
create mode 100644 build/AzurePipelineTemplates/CsWinRT-Pipeline.yml
create mode 100644 build/AzurePipelineTemplates/CsWinRT-PublishToMaestro-Stage.yml
create mode 100644 build/AzurePipelineTemplates/CsWinRT-PublishToNuget-Stage.yml
create mode 100644 eng/common/AzurePipelineTemplates/Maestro-PublishBuildToMaestro-Steps.yml
create mode 100644 eng/common/scripts/ConvertVersionDetailsToPackageConfig.ps1
create mode 100644 eng/common/scripts/MaestroGetRequest.ps1
create mode 100644 eng/common/scripts/MaestroHelpers.ps1
create mode 100644 eng/common/scripts/MaestroPostRequest.ps1
create mode 100644 eng/common/scripts/UpdateVersionDetailsConfig.ps1
diff --git a/build/AzurePipelineTemplates/CsWinRT-BuildAndTest-Stage.yml b/build/AzurePipelineTemplates/CsWinRT-BuildAndTest-Stage.yml
new file mode 100644
index 000000000..0a520c811
--- /dev/null
+++ b/build/AzurePipelineTemplates/CsWinRT-BuildAndTest-Stage.yml
@@ -0,0 +1,427 @@
+stages:
+- stage: BuildAndTest
+ displayName: Build/Test Stage
+ jobs:
+ - job: BuildAndTest
+ pool:
+ vmImage: windows-2019
+ timeoutInMinutes: 90
+ # https://docs.microsoft.com/en-us/azure/devops/pipelines/process/phases?view=azure-devops&tabs=yaml#multi-job-configuration
+ strategy:
+ maxParallel: 10
+ matrix:
+ x64_Debug:
+ BuildPlatform: x64
+ BuildConfiguration: debug
+ x86_Debug:
+ BuildPlatform: x86
+ BuildConfiguration: debug
+ arm64_Debug:
+ BuildPlatform: arm64
+ BuildConfiguration: debug
+ x64_Release:
+ BuildPlatform: x64
+ BuildConfiguration: release
+ x86_Release:
+ BuildPlatform: x86
+ BuildConfiguration: release
+ arm64_Release:
+ BuildPlatform: arm64
+ BuildConfiguration: release
+
+ steps:
+ - checkout: self
+ clean: true
+ persistCredentials: true
+
+# Clone TestWinRT
+ - task: CmdLine@2
+ displayName: Clone TestWinRT
+ enabled: false
+ inputs:
+ script: get_testwinrt.cmd
+ workingDirectory: $(Build.SourcesDirectory)
+
+# Download procdump
+ - task: PowerShell@2
+ displayName: Download procdump
+ enabled: false
+ inputs:
+ targetType: inline
+ script: |
+ mkdir $env:Agent_TempDirectory\procdump
+
+ Invoke-WebRequest -Uri https://download.sysinternals.com/files/Procdump.zip -OutFile $env:Agent_TempDirectory\procdump\Procdump.zip
+
+ Expand-Archive -Path $env:Agent_TempDirectory\procdump\Procdump.zip $env:Agent_TempDirectory\procdump\
+
+ set PROCDUMP_PATH=$env:Agent_TempDirectory\procdump\
+
+ Write-Host ##vso[task.setvariable variable=PATH;]${env:Agent_TempDirectory}\procdump;${env:PATH};
+
+# Use .NET Core SDK 2.1
+ - task: UseDotNet@2
+ displayName: Use .NET Core SDK 2.1
+ inputs:
+ version: 2.1.x
+ installationPath: C:\Users\VssAdministrator\AppData\Local\Microsoft\dotnet\
+ performMultiLevelLookup: true
+
+# Install .NET 5 SDK
+ - task: PowerShell@2
+ displayName: Install .NET 5 SDK
+ inputs:
+ targetType: inline
+ failOnStderr: true
+ script: |
+ Write-Host ##vso[task.setvariable variable=PATH;]${env:LocalAppData}\Microsoft\dotnet;${env:PATH};
+
+ [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12;
+
+ &([scriptblock]::Create((Invoke-WebRequest -UseBasicParsing 'https://dot.net/v1/dotnet-install.ps1'))) -Version "$($env:NET5_SDK_VERSION)" -Architecture "x64" -AzureFeed "$($env:NET5_SDK_FEED)"
+
+# Install .NET 6 SDK
+ - task: PowerShell@2
+ displayName: Install .NET 6 SDK
+ inputs:
+ targetType: inline
+ failOnStderr: true
+ script: |
+ Write-Host ##vso[task.setvariable variable=PATH;]${env:LocalAppData}\Microsoft\dotnet;${env:PATH};
+
+ [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12;
+
+ dotnet new globaljson --sdk-version "$($env:NET6_SDK_VERSION)"
+
+ &([scriptblock]::Create((Invoke-WebRequest -UseBasicParsing 'https://dot.net/v1/dotnet-install.ps1'))) -Version "$($env:NET6_SDK_VERSION)" -Architecture "x64" -AzureFeed "$($env:NET5_SDK_FEED)"
+
+# Verify .NET SDK
+ - task: CmdLine@2
+ displayName: Verify .NET SDK
+ inputs:
+ workingDirectory: $(Build.SourcesDirectory)
+ script: |
+ where dotnet
+ dotnet --info
+
+# Parse Version
+ - task: CmdLine@2
+ displayName: Parse Version
+ inputs:
+ workingDirectory: $(Build.SourcesDirectory)
+ script: |
+ rem Parse the build-generated Build.BuildNumber into components that
+ rem can be recombined for version resources, nuget packages, etc.
+ @echo off
+
+ rem Encode the build date/rev into a 16 bit value for resource versions
+ if "$(PrereleaseVersion)"=="" (
+ set RevisionBase=30000
+ ) else (
+ set RevisionBase=0
+ )
+
+ for /f "tokens=4,5 delims=." %%i in ("$(Build.BuildNumber)") do set BuildMetadata=%%i.%%j & set /a BuildRevision=%RevisionBase%+(((%%i/10000)-20)*366+((%%i)%%10000)/100*31+((%%i)%%100))*10+%%j
+
+ set VersionNumber=$(MajorVersion).$(MinorVersion).$(PatchVersion).%BuildRevision%
+
+ if "$(PrereleaseVersion)"=="" (
+ set NugetVersion=$(MajorVersion).$(MinorVersion).$(PatchVersion)
+ ) else (
+ set NugetVersion=$(Build.BuildNumber)
+ )
+
+ rem Export generated version numbers back for subsequent tasks
+ echo ##vso[task.setvariable variable=BuildMetadata;]%BuildMetadata%
+ echo ##vso[task.setvariable variable=BuildRevision;]%BuildRevision%
+ echo ##vso[task.setvariable variable=VersionNumber;]%VersionNumber%
+ echo ##vso[task.setvariable variable=NugetVersion;]%NugetVersion%
+
+
+# Build Prerelease Targets
+ - task: CmdLine@2
+ displayName: Build Prerelease Targets
+ inputs:
+ workingDirectory: $(Build.SourcesDirectory)
+ script: |
+ if "$(PrereleaseVersion)"=="" goto :eof
+
+ set prerelease_targets=nuget\Microsoft.Windows.CsWinRT.Prerelease.targets
+ echo ^ > %prerelease_targets%
+ echo ^> %prerelease_targets%
+ echo Condition=" '$(NetCoreSdkVersion)' ^!= '$($env:NET5_SDK_VERSION)' and '$(Net5SdkVersion)' ^!= '$($env:NET5_SDK_VERSION)' "^> >> %prerelease_targets%
+ echo ^ >> %prerelease_targets%
+ echo ^ >> %prerelease_targets%
+ echo ^ >> %prerelease_targets%
+
+
+# Build Tool
+ - task: CmdLine@2
+ displayName: Build Tool
+ inputs:
+ workingDirectory: $(Build.SourcesDirectory)\src
+ script: |
+ if "%VSCMD_VER%"=="" (
+ pushd c:
+ call "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\Common7\Tools\VsDevCmd.bat" >nul 2>&1
+ popd
+ )
+
+ set cswinrt_echo=on
+ set cswinrt_build_only=true
+ set cswinrt_build_params=/bl:$(Build.SourcesDirectory)\cswinrt.binlog /p:CleanIntermediateDirs=true
+ build.cmd $(BuildPlatform) $(BuildConfiguration) $(VersionNumber) $(Build.BuildNumber) $(WinRT.Runtime.AssemblyVersion)
+
+# Mask BuildConfiguration
+ - task: CmdLine@2
+ displayName: Mask BuildConfiguration
+ enabled: False
+ inputs:
+ script: |
+ @echo off
+
+ rem Although BuildConfiguration is PipelineRelease or PipelineDebug, the build outputs still go to Release or Debug
+ rem change BuildConfiguration variable so staging succeeds
+ rem the alternative would be to add configuration property for Pipeline* to every project in cswinrt.sln
+
+ if "$(BuildConfiguration)"=="PipelineRelease" (
+ set NewBuildConfiguration=Release
+ ) else if "$(BuildConfiguration)"=="PipelineDebug" (
+ set NewBuildConfiguration=Debug
+ )
+
+ if "%NewBuildConfiguration%"!="" (
+ echo ##vso[task.setvariable variable=BuildConfiguration;]%NewBuildConfiguration%
+ )
+
+# Component Detection
+ - task: ComponentGovernanceComponentDetection@0
+ displayName: Component Detection
+
+# Stage BinLog
+ - task: CopyFiles@2
+ displayName: Stage BinLog
+ condition: always()
+ inputs:
+ SourceFolder: $(Build.SourcesDirectory)
+ Contents: cswinrt.binlog
+ TargetFolder: $(Build.ArtifactStagingDirectory)\binlog
+
+# Publish BinLog
+ - task: PublishBuildArtifacts@1
+ displayName: Publish BinLog
+ condition: always()
+ inputs:
+ PathtoPublish: $(Build.ArtifactStagingDirectory)\binlog
+ ArtifactName: $(BuildConfiguration)_$(BuildPlatform)_binlog
+
+# Stage Windows projection
+ - task: ArchiveFiles@2
+ displayName: Stage Windows projection
+ enabled: False
+ inputs:
+ rootFolderOrFile: $(Build.SourcesDirectory)\Projections\Windows\Generated Files
+ includeRootFolder: false
+ sevenZipCompression: 5
+ archiveFile: $(Build.ArtifactStagingDirectory)\Windows\sources.zip
+
+# Publish Windows projection
+ - task: PublishBuildArtifacts@1
+ displayName: Publish Windows projection
+ enabled: False
+ inputs:
+ PathtoPublish: $(Build.ArtifactStagingDirectory)\Windows
+ ArtifactName: $(BuildConfiguration)_$(BuildPlatform)_Windows
+
+# Stage CsWinRT
+ - task: CopyFiles@2
+ displayName: Stage CsWinRT
+ condition: and(succeeded(), or(eq(variables['BuildPlatform'], 'x86'), eq(variables['BuildPlatform'], 'x64')))
+ inputs:
+ SourceFolder: $(Build.SourcesDirectory)\src\_build\$(BuildPlatform)\$(BuildConfiguration)\cswinrt\bin
+ Contents: |
+ cswinrt.exe
+ cswinrt.pdb
+ TargetFolder: $(Build.ArtifactStagingDirectory)\native
+
+# Stage WinRT.Interop.winmd
+ - task: CopyFiles@2
+ displayName: Stage WinRT.Interop.winmd
+ condition: and(succeeded(), or(eq(variables['BuildPlatform'], 'x86'), eq(variables['BuildPlatform'], 'x64')))
+ continueOnError: True
+ inputs:
+ SourceFolder: $(Build.SourcesDirectory)\src\_build\$(BuildPlatform)\$(BuildConfiguration)\cswinrt\bin
+ Contents: WinRT.Interop.winmd
+ TargetFolder: $(Build.ArtifactStagingDirectory)\native
+
+# Stage WinRT.Host
+ - task: CopyFiles@2
+ displayName: Stage WinRT.Host
+ continueOnError: True
+ inputs:
+ SourceFolder: $(Build.SourcesDirectory)\src\_build\$(BuildPlatform)\$(BuildConfiguration)\WinRT.Host\bin
+ Contents: |
+ WinRT.Host.dll
+ WinRT.Host.pdb
+ TargetFolder: $(Build.ArtifactStagingDirectory)\native
+
+# Stage Unit Test
+ - task: CopyFiles@2
+ displayName: Stage Unit Test
+ condition: and(succeeded(), or(eq(variables['BuildPlatform'], 'x86'), eq(variables['BuildPlatform'], 'x64')))
+ inputs:
+ SourceFolder: $(Build.SourcesDirectory)\src\Tests\UnitTest\bin\$(BuildPlatform)\$(BuildConfiguration)\net5.0
+ Contents: |
+ unittest.dll
+ unittest.pdb
+ TargetFolder: $(Build.ArtifactStagingDirectory)\native
+
+# Publish Native
+ - task: PublishBuildArtifacts@1
+ displayName: Publish Native
+ inputs:
+ PathtoPublish: $(Build.ArtifactStagingDirectory)\native
+ ArtifactName: $(BuildConfiguration)_$(BuildPlatform)
+
+# Stage NetStandard 2.0
+ - task: CopyFiles@2
+ displayName: Stage NetStandard 2.0
+ condition: and(succeeded(), eq(variables['BuildPlatform'], 'x86'), eq(variables['BuildConfiguration'], 'release'))
+ inputs:
+ SourceFolder: $(Build.SourcesDirectory)\src\WinRT.Runtime\bin\$(BuildConfiguration)\netstandard2.0
+ Contents: |
+ WinRT.Runtime.dll
+ WinRT.Runtime.pdb
+ TargetFolder: $(Build.ArtifactStagingDirectory)\release_netstandard2.0\
+
+# Stage Source Generator
+ - task: CopyFiles@2
+ displayName: Stage Source Generator
+ condition: and(succeeded(), eq(variables['BuildPlatform'], 'x86'), eq(variables['BuildConfiguration'], 'release'))
+ inputs:
+ SourceFolder: $(Build.SourcesDirectory)\src\Authoring\WinRT.SourceGenerator\bin\$(BuildConfiguration)\netstandard2.0
+ Contents: |
+ WinRT.SourceGenerator.dll
+ WinRT.SourceGenerator.pdb
+ TargetFolder: $(Build.ArtifactStagingDirectory)\release_netstandard2.0\
+
+# Publish NetStandard 2.0
+ - task: PublishBuildArtifacts@1
+ displayName: Publish NetStandard 2.0
+ condition: and(succeeded(), eq(variables['BuildPlatform'], 'x86'), eq(variables['BuildConfiguration'], 'release'))
+ inputs:
+ PathtoPublish: $(Build.ArtifactStagingDirectory)\release_netstandard2.0
+ ArtifactName: netstandard2.0
+
+# Stage Net5.0
+ - task: CopyFiles@2
+ displayName: Stage Net5.0
+ condition: and(succeeded(), eq(variables['BuildPlatform'], 'x86'), eq(variables['BuildConfiguration'], 'release'))
+ inputs:
+ SourceFolder: $(Build.SourcesDirectory)\src\WinRT.Runtime\bin\$(BuildConfiguration)\net5.0
+ Contents: |
+ WinRT.Runtime.dll
+ WinRT.Runtime.pdb
+ TargetFolder: $(Build.ArtifactStagingDirectory)\release_net5.0
+
+# Stage WinRT.Host.Shim
+ - task: CopyFiles@2
+ displayName: Stage WinRT.Host.Shim
+ condition: and(succeeded(), eq(variables['BuildPlatform'], 'x86'), eq(variables['BuildConfiguration'], 'release'))
+ continueOnError: True
+ inputs:
+ SourceFolder: $(Build.SourcesDirectory)\src\Authoring\WinRT.Host.Shim\bin\$(BuildConfiguration)\net5.0
+ Contents: |
+ WinRT.Host.Shim.dll
+ WinRT.Host.Shim.pdb
+ TargetFolder: $(Build.ArtifactStagingDirectory)\release_net5.0
+
+# Stage IID Optimizer
+ - task: CopyFiles@2
+ displayName: Stage IID Optimizer
+ condition: and(succeeded(), eq(variables['BuildPlatform'], 'x86'), eq(variables['BuildConfiguration'], 'release'))
+ continueOnError: True
+ inputs:
+ SourceFolder: $(Build.SourcesDirectory)\src\Perf\IIDOptimizer\bin\$(BuildConfiguration)\net5.0
+ Contents: |
+ IIDOptimizer.exe
+ IIDOptimizer.dll
+ IIDOptimizer.deps.json
+ IIDOptimizer.runtimeconfig.json
+ Mono.Cecil.dll
+ Mono.Cecil.Mdb.dll
+ Mono.Cecil.Pdb.dll
+ Mono.Cecil.Rocks.dll
+ System.CommandLine.dll
+ cs/System.CommandLine.resources.dll
+ de/System.CommandLine.resources.dll
+ es/System.CommandLine.resources.dll
+ fr/System.CommandLine.resources.dll
+ it/System.CommandLine.resources.dll
+ ja/System.CommandLine.resources.dll
+ ko/System.CommandLine.resources.dll
+ pl/System.CommandLine.resources.dll
+ pt-BR/System.CommandLine.resources.dll
+ ru/System.CommandLine.resources.dll
+ tr/System.CommandLine.resources.dll
+ zh-Hans/System.CommandLine.resources.dll
+ zh-Hant/System.CommandLine.resources.dll
+ TargetFolder: $(Build.ArtifactStagingDirectory)\release_net5.0\IIDOptimizer
+
+# Publish Net5.0
+ - task: PublishBuildArtifacts@1
+ displayName: Publish Net5.0
+ condition: and(succeeded(), eq(variables['BuildPlatform'], 'x86'), eq(variables['BuildConfiguration'], 'release'))
+ inputs:
+ PathtoPublish: $(Build.ArtifactStagingDirectory)\release_net5.0
+ ArtifactName: net5.0
+
+# Run Unit Tests
+ - task: DotNetCoreCLI@2
+ displayName: Run Unit Tests
+ condition: and(succeeded(), or(eq(variables['BuildPlatform'], 'x86'), eq(variables['BuildPlatform'], 'x64')))
+ inputs:
+ command: test
+ projects: 'src/Tests/UnitTest/UnitTest.csproj '
+ arguments: --diag $(Build.ArtifactStagingDirectory)\unittest\test.log --no-build --logger xunit;LogFilePath=UNITTEST-$(Build.BuildNumber).xml /nologo /m /p:platform=$(BuildPlatform);configuration=$(BuildConfiguration)
+ testRunTitle: Unit Tests
+
+# Run Object Lifetime Tests
+ - task: VSTest@2
+ displayName: Run Object Lifetime Tests
+ condition: and(succeeded(), or(eq(variables['BuildPlatform'], 'x86'), eq(variables['BuildPlatform'], 'x64')))
+ inputs:
+ testAssemblyVer2: Tests\ObjectLifetimeTests\bin\$(BuildPlatform)\$(BuildConfiguration)\net5.0-windows10.0.19041.0\win10-$(BuildPlatform)\ObjectLifetimeTests.Lifted.build.appxrecipe
+ searchFolder: $(Build.SourcesDirectory)\src
+
+# Publish Test Log
+ - task: PublishBuildArtifacts@1
+ displayName: Publish Test Log
+ condition: and(succeeded(), or(eq(variables['BuildPlatform'], 'x86'), eq(variables['BuildPlatform'], 'x64')))
+ inputs:
+ PathtoPublish: $(Build.ArtifactStagingDirectory)\unittest
+ ArtifactName: $(BuildConfiguration)_$(BuildPlatform)_UnitTest
+
+# Run Host Tests
+ - task: CmdLine@2
+ displayName: Run Host Tests
+ condition: and(succeeded(), or(eq(variables['BuildPlatform'], 'x86'), eq(variables['BuildPlatform'], 'x64')))
+ continueOnError: True
+ inputs:
+ workingDirectory: $(Build.SourcesDirectory)\src
+ script: |
+ dir _build\$(BuildPlatform)\$(BuildConfiguration)\HostTest\bin
+ _build\$(BuildPlatform)\$(BuildConfiguration)\HostTest\bin\HostTest.exe --gtest_output=xml:HOSTTEST-$(Build.BuildNumber).xml
+ exit /b 0
+
+# Run Source Generator Tests
+ - task: CmdLine@2
+ displayName: Run Source Generator Tests
+ condition: and(succeeded(), or(eq(variables['BuildPlatform'], 'x86'), eq(variables['BuildPlatform'], 'x64')))
+ continueOnError: True
+ inputs:
+ workingDirectory: $(Build.SourcesDirectory)\src
+ script: |
+ dir _build\$(BuildPlatform)\$(BuildConfiguration)\AuthoringConsumptionTest\bin
+ _build\$(BuildPlatform)\$(BuildConfiguration)\AuthoringConsumptionTest\bin\AuthoringConsumptionTest.exe --gtest_output=xml:AUTHORINGTEST-$(Build.BuildNumber).xml
+ exit /b 0
diff --git a/build/AzurePipelineTemplates/CsWinRT-Pipeline.yml b/build/AzurePipelineTemplates/CsWinRT-Pipeline.yml
new file mode 100644
index 000000000..d562207a7
--- /dev/null
+++ b/build/AzurePipelineTemplates/CsWinRT-Pipeline.yml
@@ -0,0 +1,11 @@
+# This file is used as basis for the ADO pipeline
+name: $(MajorVersion).$(MinorVersion).$(PatchVersion)$(PrereleaseVersion).$(date:yyMMdd)$(rev:.r)
+
+stages:
+- template: CsWinRT-BuildAndTest-Stage.yml
+
+- template: CsWinRT-PublishToNuget-Stage.yml
+
+# if we are doing a release build, publish the build to Maestro
+- ${{ if eq( '$(PrereleaseVersion)', '') }}:
+ template: CsWinRT-PublishToMaestro-Stage.yml
\ No newline at end of file
diff --git a/build/AzurePipelineTemplates/CsWinRT-PublishToMaestro-Stage.yml b/build/AzurePipelineTemplates/CsWinRT-PublishToMaestro-Stage.yml
new file mode 100644
index 000000000..bc1811fa6
--- /dev/null
+++ b/build/AzurePipelineTemplates/CsWinRT-PublishToMaestro-Stage.yml
@@ -0,0 +1,49 @@
+stages:
+- stage: PublishToMaestro
+ displayName: Trigger Maestro Publish
+ jobs:
+ - job: TriggerMaestroPublish
+ variables:
+ _DotNetCoreRuntimeVersion: 5.0.11 # matches with SDK v. 5.0.402
+ _WindowsSdkPackageVersion: 10.0.18362.22 # matches with one consumed in WindowsAppSdk
+ pool:
+ vmImage: windows-2019
+ steps:
+
+ # Parse Versions needed for offical CsWinRT version of build
+ - task: CmdLine@2
+ displayName: Parse Versions
+ inputs:
+ workingDirectory: $(Build.SourcesDirectory)
+ script: |
+ rem Parse the build-generated Build.BuildNumber into components that
+ rem can be recombined for version resources, nuget packages, etc.
+
+ @echo off
+
+ rem Encode the build date/rev into a 16 bit value for resource versions
+ if "$(PrereleaseVersion)"=="" (
+ set RevisionBase=30000
+ ) else (
+ set RevisionBase=0
+ )
+ for /f "tokens=4,5 delims=." %%i in ("$(Build.BuildNumber)") do set BuildMetadata=%%i.%%j & set /a BuildRevision=%RevisionBase%+(((%%i/10000)-20)*366+((%%i)%%10000)/100*31+((%%i)%%100))*10+%%j
+
+ set VersionNumber=$(MajorVersion).$(MinorVersion).$(PatchVersion).%BuildRevision%
+
+ if "$(PrereleaseVersion)"=="" (
+ set NugetVersion=$(MajorVersion).$(MinorVersion).$(PatchVersion)
+ ) else (
+ set NugetVersion=$(Build.BuildNumber)
+ )
+
+ rem Export generated version numbers back for subsequent tasks
+ echo ##vso[task.setvariable variable=BuildMetadata;]%BuildMetadata%
+ echo ##vso[task.setvariable variable=BuildRevision;]%BuildRevision%
+ echo ##vso[task.setvariable variable=VersionNumber;]%VersionNumber%
+ echo ##vso[task.setvariable variable=NugetVersion;]%NugetVersion%
+
+ - template: ..\..\eng\common\AzurePipelineTemplates\Maestro-PublishBuildToMaestro-Steps.yml
+ parameters:
+ AssetNames: Microsoft.Windows.CsWinRT;CsWinRT.Dependency.DotNetCoreSdk;CsWinRT.Dependency.DotNetCoreRuntime;CsWinRT.Dependency.WindowsSdkPackage
+ AssetVersions: $(NugetVersion);$(Net5.Sdk.Version);$(_DotNetCoreRuntimeVersion);$(_WindowsSdkPackageVersion)
\ No newline at end of file
diff --git a/build/AzurePipelineTemplates/CsWinRT-PublishToNuget-Stage.yml b/build/AzurePipelineTemplates/CsWinRT-PublishToNuget-Stage.yml
new file mode 100644
index 000000000..936ffeffc
--- /dev/null
+++ b/build/AzurePipelineTemplates/CsWinRT-PublishToNuget-Stage.yml
@@ -0,0 +1,261 @@
+stages:
+- stage: Publish
+ displayName: Publish To Internal Nuget Feed Stage
+ jobs:
+ - job: PublishTo_CsWinRT_InternalFeed
+ pool:
+ vmImage: windows-2019
+ steps:
+ - checkout: self
+ clean: True
+ persistCredentials: True
+
+# Use NuGet 5.3
+ - task: NuGetToolInstaller@1
+ displayName: Use NuGet 5.3
+ continueOnError: True
+ inputs:
+ versionSpec: 5.3
+
+# Component Detection
+ - task: ComponentGovernanceComponentDetection@0
+ displayName: Component Detection
+
+# Download x86
+ - task: DownloadBuildArtifacts@0
+ displayName: 'Download x86 '
+ inputs:
+ artifactName: release_x86
+ itemPattern: ''
+ downloadPath: $(Build.SourcesDirectory)
+ extractTars: false
+
+# Download x64
+ - task: DownloadBuildArtifacts@0
+ displayName: Download x64
+ inputs:
+ artifactName: release_x64
+ itemPattern: ''
+ downloadPath: $(Build.SourcesDirectory)
+ extractTars: false
+
+# Download arm64
+ - task: DownloadBuildArtifacts@0
+ displayName: Download arm64
+ inputs:
+ artifactName: release_arm64
+ itemPattern: ''
+ downloadPath: $(Build.SourcesDirectory)
+ extractTars: false
+
+# Download NetStandard2.0
+ - task: DownloadBuildArtifacts@0
+ displayName: Download NetStandard 2.0
+ inputs:
+ artifactName: netstandard2.0
+ itemPattern: ''
+ downloadPath: $(Build.SourcesDirectory)
+ extractTars: false
+
+# Download Net5.0
+ - task: DownloadBuildArtifacts@0
+ displayName: 'Download Net5.0 '
+ inputs:
+ artifactName: net5.0
+ itemPattern: ''
+ downloadPath: $(Build.SourcesDirectory)
+ extractTars: false
+
+# Stage Binaries
+ - task: CmdLine@2
+ displayName: Stage Binaries
+ inputs:
+ workingDirectory: $(Build.SourcesDirectory)
+ script: |
+ copy release_x86\cswinrt.exe cswinrt.exe
+ copy release_x86\WinRT.Interop.winmd WinRT.Interop.winmd
+
+# ESRP Codesigning
+ - task: EsrpCodeSigning@1
+ displayName: ESRP CodeSigning
+ inputs:
+ ConnectedServiceName: 81cc6790-027c-4ef3-928d-65e8b96a691a
+ FolderPath: $(Build.SourcesDirectory)
+ Pattern: |
+ cswinrt.exe
+ WinRT.Interop.winmd
+ netstandard2.0\WinRT.Runtime.dll
+ netstandard2.0\WinRT.Host.Shim.dll
+ netstandard2.0\WinRT.SourceGenerator.dll
+ net5.0\WinRT.Runtime.dll
+ net5.0\WinRT.Host.Shim.dll
+ release_x64\WinRT.Host.dll
+ release_x86\WinRT.Host.dll
+ release_arm64\WinRT.Host.dll
+ net5.0\IIDOptimizer\IIDOptimizer.exe
+ net5.0\IIDOptimizer\IIDOptimizer.dll
+ UseMinimatch: true
+ signConfigType: inlineSignParams
+ inlineOperation: |
+ [
+ {
+ "keyCode": "CP-230012",
+ "operationSetCode": "SigntoolSign",
+ "parameters": [
+ {
+ "parameterName": "OpusName",
+ "parameterValue": "Microsoft"
+ },
+ {
+ "parameterName": "OpusInfo",
+ "parameterValue": "http://www.microsoft.com"
+ },
+ {
+ "parameterName": "PageHash",
+ "parameterValue": "/NPH"
+ },
+ {
+ "parameterName": "FileDigest",
+ "parameterValue": "/fd sha256"
+ },
+ {
+ "parameterName": "TimeStamp",
+ "parameterValue": "/tr \"http://rfc3161.gtm.corp.microsoft.com/TSS/HttpTspServer\" /td sha256"
+ }
+ ],
+ "toolName": "signtool.exe",
+ "toolVersion": "6.2.9304.0"
+ }
+ ]
+
+# ESRP CodeSigning 3rd Party
+ - task: EsrpCodeSigning@1
+ displayName: ESRP CodeSigning 3rd party
+ continueOnError: True
+ inputs:
+ ConnectedServiceName: 81cc6790-027c-4ef3-928d-65e8b96a691a
+ FolderPath: $(Build.SourcesDirectory)
+ Pattern: |
+ net5.0\IIDOptimizer\Mono.Cecil.dll
+ net5.0\IIDOptimizer\Mono.Cecil.Mdb.dll
+ net5.0\IIDOptimizer\Mono.Cecil.Pdb.dll
+ net5.0\IIDOptimizer\Mono.Cecil.Rocks.dll
+ UseMinimatch: true
+ signConfigType: inlineSignParams
+ inlineOperation: |
+ [
+ {
+ "KeyCode" : "CP-231522",
+ "OperationCode" : "SigntoolSign",
+ "Parameters" : {
+ "OpusName" : "Microsoft",
+ "OpusInfo" : "http://www.microsoft.com",
+ "Append" : "/as",
+ "FileDigest" : "/fd \"SHA256\"",
+ "PageHash" : "/NPH",
+ "TimeStamp" : "/tr \"http://rfc3161.gtm.corp.microsoft.com/TSS/HttpTspServer\" /td sha256"
+ },
+ "ToolName" : "sign",
+ "ToolVersion" : "1.0"
+ },
+ {
+ "KeyCode" : "CP-231522",
+ "OperationCode" : "SigntoolVerify",
+ "Parameters" : {},
+ "ToolName" : "sign",
+ "ToolVersion" : "1.0"
+ }
+ ]
+
+# Parse Versions
+ - task: CmdLine@2
+ displayName: Parse Versions
+ inputs:
+ workingDirectory: $(Build.SourcesDirectory)
+ script: |
+ rem Parse the build-generated Build.BuildNumber into components that
+ rem can be recombined for version resources, nuget packages, etc.
+
+ @echo off
+
+ rem Encode the build date/rev into a 16 bit value for resource versions
+ if "$(PrereleaseVersion)"=="" (
+ set RevisionBase=30000
+ ) else (
+ set RevisionBase=0
+ )
+ for /f "tokens=4,5 delims=." %%i in ("$(Build.BuildNumber)") do set BuildMetadata=%%i.%%j & set /a BuildRevision=%RevisionBase%+(((%%i/10000)-20)*366+((%%i)%%10000)/100*31+((%%i)%%100))*10+%%j
+
+ set VersionNumber=$(MajorVersion).$(MinorVersion).$(PatchVersion).%BuildRevision%
+
+ if "$(PrereleaseVersion)"=="" (
+ set NugetVersion=$(MajorVersion).$(MinorVersion).$(PatchVersion)
+ ) else (
+ set NugetVersion=$(Build.BuildNumber)
+ )
+
+ rem Export generated version numbers back for subsequent tasks
+ echo ##vso[task.setvariable variable=BuildMetadata;]%BuildMetadata%
+ echo ##vso[task.setvariable variable=BuildRevision;]%BuildRevision%
+ echo ##vso[task.setvariable variable=VersionNumber;]%VersionNumber%
+ echo ##vso[task.setvariable variable=NugetVersion;]%NugetVersion%
+
+# NuGet Pack
+ - task: NuGetCommand@2
+ displayName: NuGet pack
+ inputs:
+ command: pack
+ searchPatternPack: nuget/Microsoft.Windows.CsWinRT.nuspec
+ configurationToPack: Release
+ buildProperties: cswinrt_nuget_version=$(NugetVersion);cswinrt_exe=$(Build.SourcesDirectory)\cswinrt.exe;interop_winmd=$(Build.SourcesDirectory)\WinRT.Interop.winmd;netstandard2_runtime=$(Build.SourcesDirectory)\netstandard2.0\WinRT.Runtime.dll;net5_runtime=$(Build.SourcesDirectory)\net5.0\WinRT.Runtime.dll;source_generator=$(Build.SourcesDirectory)\netstandard2.0\WinRT.SourceGenerator.dll;winrt_shim=$(Build.SourcesDirectory)\net5.0\WinRT.Host.Shim.dll;winrt_host_x86=$(Build.SourcesDirectory)\release_x86\WinRT.Host.dll;winrt_host_x64=$(Build.SourcesDirectory)\release_x64\WinRT.Host.dll;winrt_host_arm64=$(Build.SourcesDirectory)\release_arm64\WinRT.Host.dll;guid_patch=$(Build.SourcesDirectory)\net5.0\IIDOptimizer\*.*
+
+# ESRP CodeSigning
+ - task: EsrpCodeSigning@1
+ displayName: ESRP CodeSigning
+ inputs:
+ ConnectedServiceName: 81cc6790-027c-4ef3-928d-65e8b96a691a
+ FolderPath: $(Build.ArtifactStagingDirectory)
+ Pattern: '*.nupkg'
+ signConfigType: inlineSignParams
+ inlineOperation: |
+ [
+ {
+ "KeyCode" : "CP-401405",
+ "OperationCode" : "NuGetSign",
+ "Parameters" : {},
+ "ToolName" : "sign",
+ "ToolVersion" : "1.0"
+ },
+ {
+ "KeyCode" : "CP-401405",
+ "OperationCode" : "NuGetVerify",
+ "Parameters" : {},
+ "ToolName" : "sign",
+ "ToolVersion" : "1.0"
+ }
+ ]
+
+# NuGet push
+ - task: NuGetCommand@2
+ displayName: NuGet push
+ inputs:
+ command: push
+ searchPatternPush: $(Build.ArtifactStagingDirectory)/**/*.nupkg
+ feedPublish: cfbb8a6b-97b7-4070-a6e8-a4081b046ae0
+ externalEndpoint: 80b1372e-52e9-486d-934f-92d5590c2241
+
+# NuGet publish
+ - task: PublishPipelineArtifact@1
+ displayName: NuGet publish
+ inputs:
+ path: $(Build.ArtifactStagingDirectory)
+ artifactName: Publish
+
+# Publish Symbols
+ - task: PublishSymbols@2
+ displayName: Publish Symbols
+ inputs:
+ SearchPattern: '**/*.pdb'
+ IndexSources: false
+ SymbolServerType: TeamServices
+ SymbolsProduct: C#/WinRT
\ No newline at end of file
diff --git a/docs/structure.md b/docs/structure.md
index 4b0f9a3fc..d3325d7ad 100644
--- a/docs/structure.md
+++ b/docs/structure.md
@@ -2,6 +2,15 @@
This document describes the CsWinRT repository organization. Documentation and specs are located in the [`/docs`](.) folder. All source code for CsWinRT is located in the [`/src`](../src) folder, and files for generating the NuGet package are located in [`/nuget`](../nuget).
+## [`build`](../build)
+
+Contains source files for Azure DevOps pipeline that handles official builds and testing for C#/WinRT. Uses Maestro to publish builds conveniently for dependent projects; Maestro is a dependency manager
+developed by dotnet as part of the [Arcade Build System](https://github.com/dotnet/arcade).
+
+## [`eng`](../eng)
+
+Contains files that assist with publishing to Maestro.
+
## [`nuget`](../nuget)
Contains source files for producing a C#/WinRT NuGet package, which is regularly built, signed, and published to nuget.org by Microsoft. The C#/WinRT NuGet package contains the **cswinrt.exe** compiler and the runtime assembly, `WinRT.Runtime.dll`.
diff --git a/eng/common/AzurePipelineTemplates/Maestro-PublishBuildToMaestro-Steps.yml b/eng/common/AzurePipelineTemplates/Maestro-PublishBuildToMaestro-Steps.yml
new file mode 100644
index 000000000..ac46567c0
--- /dev/null
+++ b/eng/common/AzurePipelineTemplates/Maestro-PublishBuildToMaestro-Steps.yml
@@ -0,0 +1,201 @@
+# Parameters:
+# AssetNames and AssetVersions can be separated by ';' for publishing build with multiple assets
+# The lengths when split by ';' must match between both of these parameters
+# Example:
+# AssetNames: "PackageName1;PackageName2"
+# AssetVersions: "1.1;1.2"
+#
+# TriggerSubscription will publish the build to the default channel.
+parameters:
+ AssetNames: ''
+ AssetVersions: ''
+ TriggerSubscription: true
+ BranchTag: ''
+
+steps:
+ - task: PowerShell@2
+ displayName: Check prerequisites
+ inputs:
+ targetType: 'inline'
+ script: |
+ Write-Host "Pipeline Variable 'MaestroToken' and 'MaestroUri' must have a value"
+ Write-Host "##vso[task.complete result=Failed;]DONE"
+ condition: or(eq(variables['MaestroUri'], ''), eq(variables['MaestroToken'], ''))
+
+ - task: PowerShell@2
+ displayName: Build JsonBody
+ inputs:
+ targetType: 'inline'
+ script: |
+ . .\eng\common\Scripts\MaestroHelpers.ps1
+
+ $jsonBase = @{}
+
+ $branchTag = ''
+ if ('${{ parameters.BranchTag }}' -ne '')
+ {
+ $branchTag = "-${{ parameters.BranchTag }}"
+ }
+
+ $assetNames = '${{ parameters.AssetNames }}'.Split(";")
+ $assetVersions = '${{ parameters.AssetVersions }}'.Split(";")
+
+ if ($assetNames.length -ne $assetVersions.length)
+ {
+ Write-Host "AssetNames and AssetVersions must have equal lengths"
+ Write-Host "##vso[task.complete result=Failed;]DONE"
+ exit 1
+ }
+
+ $assetList = New-Object System.Collections.ArrayList
+ for ($i = 0; $i -lt ($assetNames.length); $i += 1)
+ {
+ $assetList.Add(
+ @{
+ "name"=$assetNames[$i];
+ "version"=$assetVersions[$i];
+ "nonShipping"=$false;
+ "locations"=$null
+ }
+ )
+ }
+
+ # These fields below are unused but can be enabled in the future
+ # $locationList = New-Object System.Collections.ArrayList
+ # $locationList.Add(@{"location"="maestroTestValue";"type"="none"})
+
+ # $dependenciesList = New-Object System.Collections.ArrayList
+ # $dependenciesList.Add(
+ # @{
+ # "buildId"=0;
+ # "isProduct"=$true;
+ # "timeToInclusionInMinutes"=0;
+ # }
+ # )
+
+ # $incoherenciesList = New-Object System.Collections.ArrayList
+ # $incoherenciesList.Add(
+ # @{
+ # "name"="maestroTestValue";
+ # "version"="maestroTestValue";
+ # "repository"="maestroTestValue";
+ # "commit"="maestroTestValue";
+ # }
+ # )
+
+ Write-Host "collectionuri: $(System.CollectionUri)"
+ $devOpsAccount = ExtractOrgFromAzureDevOpsCollectionUri '$(System.CollectionUri)'
+ Write-Host "reposiitory: $(Build.Repository.Uri)"
+ Write-Host "account: " $devOpsAccount
+
+ $gitHubRepo = "$(Build.Repository.Uri)"
+ $azureDevOpsRepo = "$(Build.Repository.Uri)"
+
+ $jsonBase =
+ @{
+ "commit"="$(Build.SourceVersion)";
+ "assets"=$assetList;
+ "dependencies"=$null;
+ "azureDevOpsBuildId"=$(Build.BuildId);
+ "azureDevOpsBuildDefinitionId"=$(System.DefinitionId);
+ "azureDevOpsAccount"=$devOpsAccount;
+ "azureDevOpsProject"="$(System.TeamProject)";
+ "azureDevOpsBuildNumber"="$(Build.BuildNumber)";
+ "azureDevOpsRepository"=$azureDevOpsRepo;
+ "azureDevOpsBranch"="$(Build.SourceBranch)$branchTag";
+ "gitHubRepository"=$gitHubRepo;
+ "gitHubBranch"="$(Build.SourceBranch)$branchTag";
+ "released"=$true;
+ "stable"=$true
+ "incoherencies"=$null;
+ }
+
+ $jsonBase | ConvertTo-Json -Depth 10 | Out-File '$(Build.SourcesDirectory)\eng\common\maestro-build.json'
+ $body = Get-Content -Raw -Path '$(Build.SourcesDirectory)\eng\common\maestro-build.json'
+ Write-host $body
+
+ - task: powershell@2
+ displayName: 'Post build to Maestro'
+ inputs:
+ targetType: filePath
+ filePath: eng\common\Scripts\MaestroPostRequest.ps1
+ arguments: -url '$(MaestroUri)' -Token '$(MaestroToken)' -api '/api/builds' -jsonBodyPath '$(Build.SourcesDirectory)\eng\common\maestro-build.json'
+
+ - ${{ if eq(parameters.TriggerSubscription, 'true') }}:
+ - task: powershell@2
+ displayName: 'Publish Build to Default Channel'
+ inputs:
+ targetType: 'inline'
+ script: |
+ . .\eng\common\Scripts\MaestroHelpers.ps1
+
+ $branchTag = ''
+ if ('${{ parameters.BranchTag }}' -ne '')
+ {
+ $branchTag = "-${{ parameters.BranchTag }}"
+ }
+
+ $repository = '$(Build.Repository.Uri)'
+ if (!(IsGitHubRepo($repository)))
+ {
+ # Maestro expects https://dev.azure.com/microsoft/xx/_git/xx
+ # But Build.Repository.Uri returns https://microsoft.visualstudio.com/xx/_git/xx
+ # So we convert it into the right form here
+ $repository = ConvertToMaestroFriendlyAzureDevOpUri $repository
+ }
+
+ # Get the id of the default channel of this branch
+ $api = "/api/default-channels"
+ $queryParam = "&repository=" + $repository + "&branch=$(Build.SourceBranch)$branchTag"
+ $response = &".\eng\common\Scripts\MaestroGetRequest.ps1" -url '$(MaestroUri)' -Token '$(MaestroToken)' -api $api -queryParameters $queryParam
+ $jsonObj = ConvertFrom-Json $response.Content
+ $channelId = $jsonObj.channel.id
+ if ([string]::IsNullOrEmpty($channelId))
+ {
+ Write-Host "Error: Default channel not found. Please use darc add-default-channel to add a default channel for this repo and branch"
+ Write-Host "##vso[task.complete result=SucceededWithIssues;]DONE"
+ }
+ else
+ {
+ # Get the id of the build posted earlier
+ $api = "/api/builds"
+ $queryParam = "&repository=" + $repository + "&commit=$(Build.SourceVersion)"
+ $response = &".\eng\common\Scripts\MaestroGetRequest.ps1" -url '$(MaestroUri)' -Token '$(MaestroToken)' -api $api -queryParameters $queryParam
+ $jsonObj = ConvertFrom-Json $response.Content
+ $buildId = $jsonObj.id
+ if ([string]::IsNullOrEmpty($buildId))
+ {
+ Write-Host "Error: build not found in Maestro"
+ Write-Host "##vso[task.complete result=Failed;]DONE"
+ }
+
+ # AddBuildToChannel with the corresponding build id and channel id
+ $api = "/api/channels/" + $channelId + "/builds/" + $buildId
+ # buildId may return a space separated numbers if there are more than one build for the same commit
+ $api = $api.Split(" ")[0]
+ $response = &".\eng\common\Scripts\MaestroPostRequest.ps1" -url '$(MaestroUri)' -Token '$(MaestroToken)' -api $api -jsonBodyPath ''
+
+ # Get the list of subscriptions on the channel
+ $api = "/api/subscriptions"
+ $queryParams = "&channelId=" + $channelId
+ $response = &".\eng\common\Scripts\MaestroGetRequest.ps1" -url '$(MaestroUri)' -Token '$(MaestroToken)' -api $api -queryParameters $queryParams
+ $jsonObj = ConvertFrom-Json $response.Content
+ foreach ($sub in $jsonObj)
+ {
+ if ($sub.sourceRepository -eq $repository)
+ {
+ # Trigger the subscription
+ $id = $sub.id
+ $updateFrequency = $sub.policy.updateFrequency
+ Write-Host "updateFrequency " $updateFrequency
+ if ($updateFrequency -eq 'everyBuild')
+ {
+ Write-Host "Triggering subscription on " $id
+ # bar-build-id is always 0
+ $api = "/api/subscriptions/" + $id + "/trigger"
+ $response = &".\eng\common\Scripts\MaestroPostRequest.ps1" -url '$(MaestroUri)' -Token '$(MaestroToken)' -api $api -jsonBodyPath '' -queryParameters '&bar-build-id=0'
+ }
+ }
+ }
+ }
+
diff --git a/eng/common/scripts/ConvertVersionDetailsToPackageConfig.ps1 b/eng/common/scripts/ConvertVersionDetailsToPackageConfig.ps1
new file mode 100644
index 000000000..ec69b80bd
--- /dev/null
+++ b/eng/common/scripts/ConvertVersionDetailsToPackageConfig.ps1
@@ -0,0 +1,33 @@
+Param(
+ [Parameter(Position=0)]
+ [string]$versionDetailsPath = "",
+ [Parameter(Position=1)]
+ [string]$packageConfigPath = ""
+)
+
+[xml]$buildConfig = Get-Content -Path $versionDetailsPath
+
+$packagesText =
+@"
+
+
+
+
+"@
+foreach ($dependency in $buildConfig.Dependencies.ProductDependencies.Dependency)
+{
+ $name = $dependency.name
+ $ver = $dependency.version
+ Write-Host "id: " $name
+ Write-Host "ver: " $ver
+ $packagesText += '
+'
+ }
+$packagesText +=
+@"
+
+"@
+
+Write-Host $packagesText
+
+Set-Content -Value $packagesText $packageConfigPath
diff --git a/eng/common/scripts/MaestroGetRequest.ps1 b/eng/common/scripts/MaestroGetRequest.ps1
new file mode 100644
index 000000000..1555f90ad
--- /dev/null
+++ b/eng/common/scripts/MaestroGetRequest.ps1
@@ -0,0 +1,34 @@
+[CmdLetBinding()]
+Param(
+ [string]$url,
+ [string]$token,
+ [string]$api,
+ [string]$queryParameters
+)
+
+$headers = @{
+ Authorization="Bearer $token"
+}
+
+$contentType = 'application/json'
+$api = $api + '?api-version=2020-02-20' + $queryParameters
+$fullUri = $url + $api
+
+Write-Host $fullUri
+
+$response = Invoke-WebRequest -Method 'GET' -Uri $fullUri -Headers $headers -ContentType $contentType
+Write-Host $response
+
+if ($Response.statuscode -lt '200')
+{
+ Write-Host $Response.statuscode
+ Write-Host "##vso[task.complete result=Failed;]DONE"
+}
+if ($Response.statuscode -ge '300')
+{
+ Write-Host $Response.statuscode
+ Write-Host "##vso[task.complete result=Failed;]DONE"
+}
+
+
+return $response
\ No newline at end of file
diff --git a/eng/common/scripts/MaestroHelpers.ps1 b/eng/common/scripts/MaestroHelpers.ps1
new file mode 100644
index 000000000..962e99f9a
--- /dev/null
+++ b/eng/common/scripts/MaestroHelpers.ps1
@@ -0,0 +1,81 @@
+# Param: CollectionUri must be in the form below
+# https://microsoft.visualstudio.com[/]*
+function ExtractOrgFromAzureDevOpsCollectionUri([string]$CollectionUri)
+{
+ $CollectionUri = $CollectionUri.TrimEnd('/')
+ $Split1 = $CollectionUri.Split(".")
+ if ($Split1.Length -ne 3)
+ {
+ Write-Host "##vso[task.complete result=Failed;]Format Error"
+ Exit 1
+ }
+ if ($split1[1] -ne "visualstudio")
+ {
+ Write-Host "##vso[task.complete result=Failed;]Format Error"
+ Exit 1
+ }
+ if ($split1[2].Substring(0,3) -ne "com")
+ {
+ Write-Host "##vso[task.complete result=Failed;]Format Error"
+ Exit 1
+ }
+ $temp1 = $Split1[0]
+ $result = $temp1.Substring(8)
+ return $result
+}
+
+# Param: buildRepositoryUri must be in the form below
+# https://microsoft.visualstudio.com/xxx/_git/xxx[/]
+function ConvertToMaestroFriendlyAzureDevOpUri([string]$buildRepositoryUri)
+{
+ $buildRepositoryUri = $buildRepositoryUri.TrimEnd('/')
+ if (IsGitHubRepo($buildRepositoryUri))
+ {
+ Write-Host "URI is github"
+ return $buildRepositoryUri
+ }
+ if (IsMaestroFriendlyAzureDevOpUri($buildRepositoryUri))
+ {
+ Write-Host "URI is already Maestro friendly"
+ return $buildRepositoryUri
+ }
+ $devOpsAccount = ExtractOrgFromAzureDevOpsCollectionUri $buildRepositoryUri
+ $buildRepositoryUriSplit = $buildRepositoryUri.Split("/")
+ if ($buildRepositoryUriSplit.Length -ne 6)
+ {
+ Write-Host "##vso[task.complete result=Failed;]Format Error"
+ Exit 1
+ }
+
+ $repository = "https://dev.azure.com/" + $devOpsAccount + "/" + $buildRepositoryUriSplit[3] + "/" + $buildRepositoryUriSplit[4] + "/" + $buildRepositoryUriSplit[5]
+ return $repository
+}
+
+function IsGitHubRepo([string]$buildRepositoryUri)
+{
+ $githubUrls = @("https://github.com", "https://wwww.github.com")
+ for ($i = 0; $i -le ($githubUrls.length); $i += 1)
+ {
+ if ($buildRepositoryUri.length -ge $githubUrls[$i].length)
+ {
+ if($buildRepositoryUri.Substring(0, $githubUrls[$i].length) -eq $githubUrls[$i])
+ {
+ return $true
+ }
+ }
+ }
+ return $false
+}
+
+function IsMaestroFriendlyAzureDevOpUri([string]$buildRepositoryUri)
+{
+ $azdoUri = "https://dev.azure.com"
+ if ($buildRepositoryUri.length -ge $azdoUri.length)
+ {
+ if($buildRepositoryUri.Substring(0, $azdoUri.length) -eq $azdoUri)
+ {
+ return $true
+ }
+ }
+ return $false
+}
\ No newline at end of file
diff --git a/eng/common/scripts/MaestroPostRequest.ps1 b/eng/common/scripts/MaestroPostRequest.ps1
new file mode 100644
index 000000000..5b6f50278
--- /dev/null
+++ b/eng/common/scripts/MaestroPostRequest.ps1
@@ -0,0 +1,40 @@
+[CmdLetBinding()]
+Param(
+ [string]$url,
+ [string]$token,
+ [string]$api,
+ [string]$jsonBodyPath,
+ [string]$queryParameters = ''
+)
+
+$headers = @{
+ Authorization="Bearer $token"
+}
+
+$body = ''
+if (-not [string]::IsNullOrEmpty($jsonBodyPath))
+{
+ $body = Get-Content -Raw -Path $jsonBodyPath
+}
+
+$contentType = 'application/json'
+$api = $api + '?api-version=2020-02-20' + $queryParameters
+$fullUri = $url + $api
+
+Write-Host $fullUri
+
+$Response = Invoke-WebRequest -Method 'POST' -Uri $fullUri -Headers $headers -Body $body -ContentType $contentType
+Write-Host $Response
+
+if ($Response.statuscode -lt '200')
+{
+ Write-Host $Response.statuscode
+ Write-Host "##vso[task.complete result=Failed;]DONE"
+}
+if ($Response.statuscode -ge '300')
+{
+ Write-Host $Response.statuscode
+ Write-Host "##vso[task.complete result=Failed;]DONE"
+}
+
+return $Response
\ No newline at end of file
diff --git a/eng/common/scripts/UpdateVersionDetailsConfig.ps1 b/eng/common/scripts/UpdateVersionDetailsConfig.ps1
new file mode 100644
index 000000000..13c5ce3f5
--- /dev/null
+++ b/eng/common/scripts/UpdateVersionDetailsConfig.ps1
@@ -0,0 +1,34 @@
+[CmdLetBinding()]
+Param(
+ [string]$dependencyName,
+ [string]$dependencyVersion
+)
+
+Write-Host $dependencyName
+Write-Host $dependencyVersion
+
+# Get the root of the repo.
+$scriptFullPath = (split-path -parent $MyInvocation.MyCommand.Definition)
+$engPath = (split-path -parent (split-path -parent $scriptFullPath))
+
+Function CheckFile($filename)
+{
+ if(-not (Test-Path $filename))
+ {
+ write-host "File not found: $filename"
+ exit 1
+ }
+}
+
+$configFilename = "$engPath\Version.Details.xml"
+CheckFile $configFilename
+
+# Load the build.config, update the requested version entry, then write it back out
+$xmldoc = [System.Xml.XmlDocument](Get-Content $configFilename)
+Write-Host $xmldoc
+
+$node = $xmldoc.Dependencies.ProductDependencies.Dependency | ?{$_.Name -eq $dependencyName}
+$node.Version = $dependencyVersion
+$xmldoc.Save($configFilename)
+
+Write-Host "Updated $configFilename"