diff --git a/tools/releaseBuild/azureDevOps/releasePipeline.yml b/tools/releaseBuild/azureDevOps/releasePipeline.yml
new file mode 100644
index 00000000000..99d9091d848
--- /dev/null
+++ b/tools/releaseBuild/azureDevOps/releasePipeline.yml
@@ -0,0 +1,274 @@
+trigger: none
+
+# needed to disable CI trigger and allow manual trigger
+# when the branch is same as pipline source, the latest build from the source is used.
+# all environment used are for manual tasks and approvals.
+
+resources:
+ pipelines:
+ - pipeline: releasePipeline
+ source: 'Coordinated Packages'
+ trigger:
+ branches:
+ - release/*
+
+variables:
+ runCodesignValidationInjection: false
+
+stages:
+- stage: ValidateSDK
+ displayName: Validate SDK
+ dependsOn: []
+ jobs:
+ - template: templates/release-SDKTests.yml
+ parameters:
+ jobName: WinSDK
+ displayName: Windows SDK Test
+ imageName: windows-latest
+
+ - template: templates/release-SDKTests.yml
+ parameters:
+ jobName: LinuxSDK
+ displayName: Linux SDK Test
+ imageName: ubuntu-latest
+
+ - template: templates/release-SDKTests.yml
+ parameters:
+ jobName: macOSSDK
+ displayName: macOS SDK Test
+ imageName: macOS-latest
+
+- stage: PRCreation
+ displayName: Create PR in GH Master
+ dependsOn: []
+ jobs:
+ - deployment: CreatePRInMaster
+ displayName: Update README.md and metadata.json
+ pool: server
+ environment: PSReleaseCreatePR
+
+- stage: ValidateGlobalTool
+ displayName: Validate Global Tool
+ dependsOn: []
+ jobs:
+ - template: templates/release-GlobalToolTest.yml
+ parameters:
+ jobName: WinGblTool
+ displayName: Global Tool Test Windows
+ imageName: windows-latest
+ globalToolExeName: 'pwsh.exe'
+ globalToolPackageName: 'PowerShell.Windows.x64'
+
+ - template: templates/release-GlobalToolTest.yml
+ parameters:
+ jobName: LinuxWinGblTool
+ displayName: Global Tool Test Linux
+ imageName: ubuntu-latest
+ globalToolExeName: 'pwsh'
+ globalToolPackageName: 'PowerShell.Linux.x64'
+
+- stage: ValidatePkgNames
+ dependsOn: []
+ displayName: Package Names validation
+ jobs:
+ - job: ValidatePkgNames
+ pool:
+ vmImage: windows-latest
+ variables:
+ - group: 'Azure Blob variable group'
+ steps:
+ - template: templates/release-ValidatePackageNames.yml
+
+- stage: StartDocker
+ dependsOn: []
+ displayName: Kick Off Docker Staging build
+ jobs:
+ - deployment: PSDockerKickOff
+ displayName: Start Docker build
+ pool: server
+ environment: PSReleaseDockerKickOff
+
+- stage: ManualValidation
+ dependsOn: []
+ displayName: Manual Validation
+ jobs:
+ - deployment: ValidateWinPkg
+ displayName: Validate Windows Packages
+ pool: server
+ environment: PSReleaseWinPkgValidate
+
+ - deployment: ValidateLinuxPkg
+ displayName: Validate Linux Packages
+ pool: server
+ environment: PSReleaseLinuxPkgValidate
+
+- stage: ReleaseAutomation
+ displayName: Release Automation
+ dependsOn: []
+ jobs:
+ - deployment: ReleaseAutomationKickOff
+ displayName: Kick off release ReleaseAutomation
+ pool: server
+ environment: PSReleaseAutomationKickOff
+
+- stage: GitHubDraftRelease
+ displayName: Create GitHub draft release
+ # do not include stages that are likely to fail in dependency as there is no way to force deploy.
+ dependsOn:
+ - ValidateSDK
+ - PRCreation
+ - ValidatePkgNames
+ - StartDocker
+ - ManualValidation
+ - ReleaseAutomation
+
+# The environment here is used for approval.
+ jobs:
+ - deployment: AzureBlobPublic
+ displayName: Make Azure Blob Public
+ pool:
+ vmImage: windows-latest
+ variables:
+ - group: 'ACR'
+ environment: PSReleaseAzureBlobPublic
+ strategy:
+ runOnce:
+ deploy:
+ steps:
+ - template: templates/release-MakeContainerPublic.yml
+
+ - job: GitHubDraft
+ displayName: Create GitHub Draft release
+ pool:
+ vmImage: windows-latest
+ variables:
+ - group: 'Azure Blob variable group'
+ - group: 'AzDevOpsArtifacts'
+ dependsOn: AzureBlobPublic
+ steps:
+ - template: templates/release-CreateGitHubDraft.yml
+
+- stage: GitHubManualTasks
+ displayName: GitHub manual tasks
+ dependsOn: GitHubDraftRelease
+ jobs:
+ - deployment: UpdateChangeLog
+ displayName: Update Change Log
+ pool : server
+ environment: PSReleaseUpdateChangeLog
+
+ - deployment: PushTag
+ displayName: Push Git Tag
+ pool : server
+ environment: PSReleasePushTag
+
+ - deployment: MakeDraftPublic
+ displayName: Make GitHub Draft public
+ pool : server
+ environment: PSReleaseDraftPublic
+
+- stage: PublishPackages
+ displayName: Publish packages
+ dependsOn: GitHubManualTasks
+ jobs:
+ - job: PublishNuget
+ pool:
+ vmImage: windows-latest
+ steps:
+ - template: templates/release-ReleaseToNuGet.yml
+
+ - job: PublishPkgsMsftCom
+ pool:
+ vmImage: ubuntu-latest
+ variables:
+ - group: 'AzDevOpsArtifacts'
+ - group: 'packages.microsoft.com'
+ steps:
+ - template: templates/release-PublishPackageMsftCom.yml
+
+- stage: ChangesToMaster
+ displayName: Ensure changes are in GH master
+ dependsOn: PublishPackages
+ jobs:
+ - deployment: MergeToMaster
+ displayName: Make sure changes are in master
+ pool: server
+ environment: PSReleaseChangesToMaster
+
+- stage: ReleaseDocker
+ displayName: Release Docker
+ dependsOn:
+ - StartDocker
+ jobs:
+ - deployment: ReleaseDocker
+ displayName: Release Docker
+ pool: server
+ environment: PSReleaseDockerRelease
+
+- stage: ReleaseSnap
+ displayName: Release Snap
+ dependsOn:
+ - PublishPackages
+ - ChangesToMaster
+ jobs:
+ - deployment: ReleaseSnap
+ displayName: Release Snap
+ pool: server
+ environment: PSReleaseSnapRelease
+
+- stage: UpdateDotnetDocker
+ dependsOn: GitHubManualTasks
+ displayName: Update DotNet SDK Docker images
+ jobs:
+ - deployment: DotnetSDkDocker
+ displayName: Update .NET SDK docker images
+ pool: server
+ environment: PSReleaseUpdateDotnetDocker
+
+- stage: BuildInfoJson
+ dependsOn: GitHubManualTasks
+ displayName: Upload BuildInfoJson
+ jobs:
+ - job: UploadJson
+ pool:
+ vmImage: windows-latest
+ variables:
+ - group: 'Azure Blob variable group'
+ steps:
+ - template: templates/release-BuildJson.yml
+
+- stage: ReleaseVPack
+ dependsOn: GitHubManualTasks
+ displayName: Release VPack
+ jobs:
+ - deployment: ReleaseVPackManual
+ displayName: Release VPack
+ pool: server
+ environment: PSReleaseVpack
+
+- stage: ReleaseDeps
+ dependsOn: GitHubManualTasks
+ displayName: Update pwsh.deps.json links
+ jobs:
+ - template: templates/release-UpdateDepsJson.yml
+
+- stage: ReleaseClose
+ displayName: Finish Release
+ dependsOn:
+ - ReleaseVPack
+ - BuildInfoJson
+ - UpdateDotnetDocker
+ - ReleaseDocker
+ - ReleaseSnap
+ - ChangesToMaster
+ - ReleaseDeps
+ jobs:
+ - deployment: RetainBuild
+ displayName: Retain Build
+ pool: server
+ environment: PSReleaseRetainBuild
+
+ - deployment: DeleteBranch
+ displayName: Delete release branch
+ pool: server
+ environment: PSReleaseDeleteBranch
diff --git a/tools/releaseBuild/azureDevOps/templates/nuget.yml b/tools/releaseBuild/azureDevOps/templates/nuget.yml
index 6a0c1ef5334..5a18354b91e 100644
--- a/tools/releaseBuild/azureDevOps/templates/nuget.yml
+++ b/tools/releaseBuild/azureDevOps/templates/nuget.yml
@@ -137,10 +137,18 @@ jobs:
binVersionOverride: $(SigningVersionOverride)
condition: and(succeeded(), eq(variables['SHOULD_SIGN'], 'true'))
+ - pwsh: |
+ if (-not (Test-Path '$(System.ArtifactsDirectory)\signed\')) { $null = New-Item -ItemType Directory -Path '$(System.ArtifactsDirectory)\signed\' }
+ Copy-Item -Path '$(PackagePath)\*.nupkg' -Destination '$(System.ArtifactsDirectory)\signed\' -Verbose -Force
+ Copy-Item -Path '$(PackagePath)\globaltool\*.nupkg' -Destination '$(System.ArtifactsDirectory)\signed\' -Verbose -Force
+ displayName: Fake copy when not signing
+ condition: eq(variables['SHOULD_SIGN'], 'false')
+
- powershell: |
Import-Module $(Build.SourcesDirectory)\build.psm1 -Force
Get-ChildItem -Recurse "$(System.ArtifactsDirectory)\signed\*.nupkg" -Verbose | ForEach-Object { Start-NativeExecution -sb { nuget.exe verify -All $_.FullName } }
displayName: Verify all packages are signed
+ condition: eq(variables['SHOULD_SIGN'], 'true')
- task: securedevelopmentteam.vss-secure-development-tools.build-task-antimalware.AntiMalware@3
displayName: 'Run MpCmdRun.exe'
@@ -156,7 +164,11 @@ jobs:
artifactPath: '$(System.ArtifactsDirectory)\signed'
- powershell: |
- $null = New-Item -Path "$(System.ArtifactsDirectory)\signed\globaltool" -ItemType Directory
+ if (-not (Test-Path "$(System.ArtifactsDirectory)\signed"))
+ {
+ $null = New-Item -Path "$(System.ArtifactsDirectory)\signed\globaltool" -ItemType Directory
+ }
+
Move-Item -Path "$(System.ArtifactsDirectory)\signed\PowerShell.*" -Destination "$(System.ArtifactsDirectory)\signed\globaltool" -Force
Get-ChildItem "$(System.ArtifactsDirectory)\signed\globaltool" -Recurse
displayName: Move global tool packages to subfolder and capture
diff --git a/tools/releaseBuild/azureDevOps/templates/release-BuildJson.yml b/tools/releaseBuild/azureDevOps/templates/release-BuildJson.yml
new file mode 100644
index 00000000000..8def847dce2
--- /dev/null
+++ b/tools/releaseBuild/azureDevOps/templates/release-BuildJson.yml
@@ -0,0 +1,69 @@
+steps:
+- download: releasePipeline
+ artifact: BuildInfoJson
+
+- pwsh: |
+ $jsonFile = Get-Item "$ENV:PIPELINE_WORKSPACE/releasePipeline/BuildInfoJson/*.json"
+ $fileName = Split-Path $jsonFile -Leaf
+
+ $dateTime = [datetime]::UtcNow
+ $dateTime = [datetime]::new($dateTime.Ticks - ($dateTime.Ticks % [timespan]::TicksPerSecond), $dateTime.Kind)
+
+ $buildInfo = Get-Content $jsonFile | ConvertFrom-Json
+ $buildInfo.ReleaseDate = $dateTime
+
+ $targetFile = "$ENV:PIPELINE_WORKSPACE/$fileName"
+ ConvertTo-Json -InputObject $buildInfo | Out-File $targetFile -Encoding ascii
+
+ $vstsCommandString = "vso[task.setvariable variable=BuildInfoJsonFile]$targetFile"
+ Write-Host "sending " + $vstsCommandString
+ Write-Host "##$vstsCommandString"
+
+
+ ## Create 'lts.json' if it's the latest stable and also a LTS release.
+
+ $vstsCommandCopyLTSBuildInfo = "vso[task.setvariable variable=CopyLTSBuildInfo]NO"
+
+ if ($fileName -eq "stable.json")
+ {
+ $releaseTag = $buildInfo.ReleaseTag
+ $version = $releaseTag -replace '^v'
+ $semVersion = [System.Management.Automation.SemanticVersion] $version
+
+ if ($semVersion.PreReleaseLabel -eq $null -and $semVersion.Minor % 2 -eq 0 -and $semVersion.Major -ge 7)
+ {
+ $ltsFile = "$ENV:PIPELINE_WORKSPACE/lts.json"
+ Copy-Item -Path $targetFile -Destination $ltsFile -Force
+ $vstsCommandLtsJsonFile = "vso[task.setvariable variable=LtsBuildInfoJsonFile]$ltsFile"
+ $vstsCommandCopyLTSBuildInfo = "vso[task.setvariable variable=CopyLTSBuildInfo]YES"
+ }
+ }
+
+ Write-Host "sending " + $vstsCommandCopyLTSBuildInfo
+ Write-Host "##$vstsCommandCopyLTSBuildInfo"
+
+ if ($vstsCommandLtsJsonFile)
+ {
+ Write-Host "sending " + $vstsCommandLtsJsonFile
+ Write-Host "##$vstsCommandLtsJsonFile"
+ }
+ displayName: Download and Capture NuPkgs
+
+- task: AzureFileCopy@2
+ displayName: 'AzureBlob build info JSON file Copy'
+ inputs:
+ SourcePath: '$(BuildInfoJsonFile)'
+ azureSubscription: '$(AzureFileCopySubscription)'
+ Destination: AzureBlob
+ storage: '$(StorageAccount)'
+ ContainerName: BuildInfo
+
+- task: AzureFileCopy@2
+ displayName: 'AzureBlob build info ''lts.json'' Copy when needed'
+ inputs:
+ SourcePath: '$(LtsBuildInfoJsonFile)'
+ azureSubscription: '$(AzureFileCopySubscription)'
+ Destination: AzureBlob
+ storage: '$(StorageAccount)'
+ ContainerName: BuildInfo
+ condition: and(succeeded(), eq(variables['CopyLTSBuildInfo'], 'YES'))
diff --git a/tools/releaseBuild/azureDevOps/templates/release-CreateGitHubDraft.yml b/tools/releaseBuild/azureDevOps/templates/release-CreateGitHubDraft.yml
new file mode 100644
index 00000000000..3b316e84cbf
--- /dev/null
+++ b/tools/releaseBuild/azureDevOps/templates/release-CreateGitHubDraft.yml
@@ -0,0 +1,22 @@
+steps:
+- download: none
+
+- template: release-SetReleaseTagAndContainerName.yml
+
+- pwsh: |
+ $azcopy = "C:\Program Files (x86)\Microsoft SDKs\Azure\AzCopy\AzCopy.exe"
+ & $azcopy /Source:https://$(StorageAccount).blob.core.windows.net/$(AzureVersion) /Dest:$(System.ArtifactsDirectory) /S /SourceKey:$(StorageAccountKey)
+ displayName: Download Azure Artifacts
+
+- pwsh: |
+ Get-ChildItem $(System.ArtifactsDirectory)\* -recurse | Select-Object -ExpandProperty Name
+ displayName: Capture downloaded artifacts
+
+- pwsh: |
+ git clone https://$(AzureDevOpsPat)@mscodehub.visualstudio.com/PowerShellCore/_git/Internal-PowerShellTeam-Tools '$(Pipeline.Workspace)/tools'
+ displayName: Clone Internal-Tools repository
+
+- pwsh: |
+ Import-module '$(Pipeline.Workspace)/tools/Scripts/GitHubRelease.psm1'
+ Publish-ReleaseDraft -Tag '$(ReleaseTag)' -Name '$(ReleaseTag) Release of PowerShell' -Description '<-- Update Me -->' -User PowerShell -Repository PowerShell -PackageFolder $(System.ArtifactsDirectory) -Token $(GitHubReleasePat)
+ displayName: Publish Release Draft
diff --git a/tools/releaseBuild/azureDevOps/templates/release-GlobalToolTest.yml b/tools/releaseBuild/azureDevOps/templates/release-GlobalToolTest.yml
new file mode 100644
index 00000000000..7d9a49189bd
--- /dev/null
+++ b/tools/releaseBuild/azureDevOps/templates/release-GlobalToolTest.yml
@@ -0,0 +1,121 @@
+parameters:
+ jobName: ""
+ displayName: ""
+ imageName: ""
+ globalToolExeName: 'pwsh.exe'
+ globalToolPackageName: 'PowerShell.Windows.x64'
+
+
+jobs:
+- job: ${{ parameters.jobName }}
+ displayName: ${{ parameters.displayName }}
+ pool:
+ vmImage: ${{ parameters.imageName }}
+ steps:
+ - download: releasePipeline
+ artifact: finalResults
+ patterns: '**/*.nupkg'
+
+ - task: DownloadBuildArtifacts@0
+ inputs:
+ buildType: specific
+ project: PowerShellCore
+ pipeline: Co-ordinated Build Test
+ specificBuildWithTriggering: true
+ buildVersionToDownload: latest
+ downloadType: single
+ artifactName: finalResults
+ downloadPath: '$(Pipeline.Workspace)'
+ itemPattern: '*.nupkg'
+
+ - pwsh: |
+ $dotnetMetadataPath = "$(Build.SourcesDirectory)/DotnetRuntimeMetadata.json"
+ $dotnetMetadataJson = Get-Content $dotnetMetadataPath -Raw | ConvertFrom-Json
+
+ # Channel is like: $Channel = "5.0.1xx-preview2"
+ $Channel = $dotnetMetadataJson.sdk.channel
+
+ $sdkVersion = (Get-Content "$(Build.SourcesDirectory)/global.json" -Raw | ConvertFrom-Json).sdk.version
+ Import-Module "$(Build.SourcesDirectory)/build.psm1" -Force
+
+ Find-Dotnet
+
+ if(-not (Get-PackageSource -Name 'dotnet5' -ErrorAction SilentlyContinue))
+ {
+ $nugetFeed = ([xml](Get-Content $(Build.SourcesDirectory)/nuget.config -Raw)).Configuration.packagesources.add | Where-Object { $_.Key -eq 'dotnet5' } | Select-Object -ExpandProperty Value
+ Register-PackageSource -Name 'dotnet5' -Location $nugetFeed -ProviderName NuGet
+ Write-Verbose -Message "Register new package source 'dotnet5'" -verbose
+ }
+
+ ## Install latest version from the channel
+
+ Install-Dotnet -Channel "$Channel" -Version $sdkVersion
+
+ Write-Verbose -Message "Installing .NET SDK completed." -Verbose
+
+ displayName: Install .NET
+
+ - pwsh: |
+ $branch = $ENV:RESOURCES_PIPELINE_RELEASEPIPELINE_SOURCEBRANCH
+ $version = $branch -replace '^.*(release[-/])v'
+ $vstsCommandString = "vso[task.setvariable variable=PowerShellVersion]$version"
+ Write-Verbose -Message "Version is $version" -Verbose
+ Write-Host -Object "##$vstsCommandString"
+ displayName: Set PowerShell Version
+
+ - pwsh: |
+ $env:DOTNET_SKIP_FIRST_TIME_EXPERIENCE=1
+ Import-Module "$(Build.SourcesDirectory)/build.psm1" -Force
+ Start-PSBootstrap -Force
+
+ $toolPath = New-Item -ItemType Directory "$(System.DefaultWorkingDirectory)/toolPath" | Select-Object -ExpandProperty FullName
+
+ dotnet tool install --add-source "$ENV:PIPELINE_WORKSPACE/releasePipeline/finalResults" --tool-path $toolPath --version '$(PowerShellVersion)' '${{ parameters.globalToolPackageName }}'
+
+ Get-ChildItem -Path $toolPath
+
+ displayName: Install global tool
+
+ - pwsh: |
+ $toolPath = "$(System.DefaultWorkingDirectory)/toolPath/${{ parameters.globalToolExeName }}"
+
+ if (-not (Test-Path $toolPath))
+ {
+ throw "Tool is not installed at $toolPath"
+ }
+ else
+ {
+ Write-Verbose -Verbose "Tool found at: $toolPath"
+ }
+ displayName: Validate tool is installed
+
+ - pwsh: |
+ Import-Module "$(Build.SourcesDirectory)/build.psm1" -Force
+ Start-PSBootstrap -Force
+
+ $exeName = if ($IsWindows) { "pwsh.exe" } else { "pwsh" }
+
+ $toolPath = "$(System.DefaultWorkingDirectory)/toolPath/${{ parameters.globalToolExeName }}"
+
+ $versionFound = & $toolPath -c '$PSVersionTable.PSVersion.ToString()'
+
+ if ( '$(PowerShellVersion)' -ne $versionFound)
+ {
+ throw "Expected version of global tool not found. Installed version is $versionFound"
+ }
+ else
+ {
+ write-verbose -verbose "Found expected version: $versionFound"
+ }
+
+ $dateYear = & $toolPath -c '(Get-Date).Year'
+
+ if ( $dateYear -ne [DateTime]::Now.Year)
+ {
+ throw "Get-Date returned incorrect year: $dateYear"
+ }
+ else
+ {
+ write-verbose -verbose "Got expected year: $dateYear"
+ }
+ displayName: Basic validation
diff --git a/tools/releaseBuild/azureDevOps/templates/release-MakeContainerPublic.yml b/tools/releaseBuild/azureDevOps/templates/release-MakeContainerPublic.yml
new file mode 100644
index 00000000000..870f2794dcb
--- /dev/null
+++ b/tools/releaseBuild/azureDevOps/templates/release-MakeContainerPublic.yml
@@ -0,0 +1,16 @@
+steps:
+- download: none
+
+- template: release-SetReleaseTagAndContainerName.yml
+
+- pwsh: |
+ az login --service-principal -u $(az_url) -p $(az_key) --tenant $(az_name)
+ displayName: az login
+
+- pwsh: |
+ az storage container set-permission --account-name $(StorageAccount) --name $(azureVersion) --public-access blob
+ displayName: Make container public
+
+- pwsh: |
+ az logout
+ displayName: az logout
diff --git a/tools/releaseBuild/azureDevOps/templates/release-PublishPackageMsftCom.yml b/tools/releaseBuild/azureDevOps/templates/release-PublishPackageMsftCom.yml
new file mode 100644
index 00000000000..27d76a11fed
--- /dev/null
+++ b/tools/releaseBuild/azureDevOps/templates/release-PublishPackageMsftCom.yml
@@ -0,0 +1,22 @@
+steps:
+- template: release-SetReleaseTagAndContainerName.yml
+
+- pwsh: |
+ $packageVersion = '$(ReleaseTag)'.ToLowerInvariant() -replace '^v',''
+ $vstsCommandString = "vso[task.setvariable variable=packageVersion]$packageVersion"
+ Write-Host "sending " + $vstsCommandString
+ Write-Host "##$vstsCommandString"
+ displayName: Set Package version
+
+- pwsh: |
+ git clone https://$(AzureDevOpsPat)@mscodehub.visualstudio.com/PowerShellCore/_git/Internal-PowerShellTeam-Tools '$(Pipeline.Workspace)/tools'
+ displayName: Clone Internal-PowerShellTeam-Tools from MSCodeHub
+
+- download: releasePipeline
+ artifact: metadata
+
+- pwsh: |
+ $metadata = Get-Content -Path "$ENV:PIPELINE_WORKSPACE/releasePipeline/metadata/release.json" -Raw | ConvertFrom-Json
+ Write-Verbose -Verbose "$(releaseTag) $(PackagesRepoPassword) $(AzureVersion) $($metadata.LTSRelease)"
+ & '$(Pipeline.Workspace)/tools/packages.microsoft.com/upload.ps1' -ReleaseTag "$(releaseTag)" -Secret "$(PackagesRepoPassword)" -BlobFolderName "$(AzureVersion)" -LTS:($metadata.LTSRelease)
+ displayName: Run release script
diff --git a/tools/releaseBuild/azureDevOps/templates/release-ReleaseToNuGet.yml b/tools/releaseBuild/azureDevOps/templates/release-ReleaseToNuGet.yml
new file mode 100644
index 00000000000..dd8404f5dce
--- /dev/null
+++ b/tools/releaseBuild/azureDevOps/templates/release-ReleaseToNuGet.yml
@@ -0,0 +1,27 @@
+steps:
+- download: releasePipeline
+ artifact: finalResults
+ patterns: "**/*.nupkg"
+
+- download: releasePipeline
+ artifact: metadata
+
+- pwsh: |
+ #Exclude all global tool packages. Their names start with 'PowerShell.'
+ Copy-Item "$ENV:PIPELINE_WORKSPACE/releasePipeline/finalResults/*.nupkg" -Destination "$(Pipeline.Workspace)/release" -Exclude "PowerShell.*.nupkg" -Force
+
+ $releaseVersion = Get-Content "$ENV:PIPELINE_WORKSPACE/releasePipeline/metadata/release.json" | ConvertFrom-Json | Select-Object -ExpandProperty 'ReleaseVersion'
+ $globalToolPath = "$ENV:PIPELINE_WORKSPACE/releasePipeline/finalResults/PowerShell.$releaseVersion.nupkg"
+ ### -WhatIf to make sure we do not release global tool. Remove -WhatIf when the PowerShell name reservation is done.
+ Copy-Item $globalToolPath -Destination "$(Pipeline.Workspace)/release" -WhatIf
+
+ Get-ChildItem "$(Pipeline.Workspace)"
+ displayName: Download and capture nupkgs
+
+- task: NuGetCommand@2
+ displayName: 'NuGet push'
+ inputs:
+ command: push
+ packagesToPush: '$(Agent.ReleaseDirectory)/*.nupkg'
+ nuGetFeedType: external
+ publishFeedCredentials: PowerShellNuGetOrgPush
diff --git a/tools/releaseBuild/azureDevOps/templates/release-SDKTests.yml b/tools/releaseBuild/azureDevOps/templates/release-SDKTests.yml
new file mode 100644
index 00000000000..df2ea6085d7
--- /dev/null
+++ b/tools/releaseBuild/azureDevOps/templates/release-SDKTests.yml
@@ -0,0 +1,82 @@
+parameters:
+ jobName: ""
+ displayName: ""
+ imageName: ""
+
+jobs:
+- job: ${{ parameters.jobName }}
+ displayName: ${{ parameters.displayName }}
+ pool:
+ vmImage: ${{ parameters.imageName }}
+ steps:
+ - download: releasePipeline
+ artifact: finalResults
+ patterns: '**/*.nupkg'
+
+ - download: releasePipeline
+ artifact: metadata
+
+ - pwsh: |
+ $dotnetMetadataPath = "$(Build.SourcesDirectory)/DotnetRuntimeMetadata.json"
+ $dotnetMetadataJson = Get-Content $dotnetMetadataPath -Raw | ConvertFrom-Json
+
+ # Channel is like: $Channel = "5.0.1xx-preview2"
+ $Channel = $dotnetMetadataJson.sdk.channel
+
+ $sdkVersion = (Get-Content "$(Build.SourcesDirectory)/global.json" -Raw | ConvertFrom-Json).sdk.version
+ Import-Module "$(Build.SourcesDirectory)/build.psm1" -Force
+
+ Find-Dotnet
+
+ if(-not (Get-PackageSource -Name 'dotnet5' -ErrorAction SilentlyContinue))
+ {
+ $nugetFeed = ([xml](Get-Content $(Build.SourcesDirectory)/nuget.config -Raw)).Configuration.packagesources.add | Where-Object { $_.Key -eq 'dotnet5' } | Select-Object -ExpandProperty Value
+ Register-PackageSource -Name 'dotnet5' -Location $nugetFeed -ProviderName NuGet
+ Write-Verbose -Message "Register new package source 'dotnet5'" -verbose
+ }
+
+ ## Install latest version from the channel
+
+ Install-Dotnet -Channel "$Channel" -Version $sdkVersion
+
+ Write-Verbose -Message "Installing .NET SDK completed." -Verbose
+
+ displayName: Install .NET
+
+ - pwsh: |
+ $env:DOTNET_SKIP_FIRST_TIME_EXPERIENCE=1
+ Import-Module "$(Build.SourcesDirectory)/build.psm1" -Force
+ Start-PSBootstrap -Force
+
+ $localLocation = "$(Pipeline.Workspace)/releasePipeline/finalResults"
+ $xmlElement = @"
+
+
+
+ "@
+
+ $releaseVersion = Get-Content "$(Pipeline.Workspace)/releasePipeline/metadata/release.json" | ConvertFrom-Json | Select-Object -ExpandProperty 'ReleaseVersion'
+
+ Set-Location -Path $(Build.SourcesDirectory)/test/hosting
+
+ Get-ChildItem
+
+ ## register the packages download directory in the nuget file
+ $nugetConfigContent = Get-Content ./NuGet.Config -Raw
+ $updateNugetContent = $nugetConfigContent.Replace("", $xmlElement)
+
+ $updateNugetContent | Out-File ./NuGet.Config -Encoding ascii
+
+ Get-Content ./NuGet.Config
+
+ dotnet --info
+ dotnet restore
+ dotnet test /property:RELEASE_VERSION=$releaseVersion --test-adapter-path:. "--logger:xunit;LogFilePath=$(System.DefaultWorkingDirectory)/test-hosting.xml"
+
+ displayName: Restore and execute tests
+
+ - task: PublishTestResults@2
+ displayName: 'Publish Test Results **\test-hosting.xml'
+ inputs:
+ testResultsFormat: XUnit
+ testResultsFiles: '**\test-hosting.xml'
diff --git a/tools/releaseBuild/azureDevOps/templates/release-SetReleaseTagAndContainerName.yml b/tools/releaseBuild/azureDevOps/templates/release-SetReleaseTagAndContainerName.yml
new file mode 100644
index 00000000000..a1af9a4acdf
--- /dev/null
+++ b/tools/releaseBuild/azureDevOps/templates/release-SetReleaseTagAndContainerName.yml
@@ -0,0 +1,21 @@
+steps:
+- pwsh: |
+ $variable = 'releaseTag'
+ $branch = $ENV:RESOURCES_PIPELINE_RELEASEPIPELINE_SOURCEBRANCH
+ if($branch -notmatch '^.*(release[-/])')
+ {
+ throw "Branch name is not in release format: '$branch'"
+ }
+
+ $releaseTag = $Branch -replace '^.*(release[-/])'
+ $vstsCommandString = "vso[task.setvariable variable=$Variable]$releaseTag"
+ Write-Verbose -Message "setting $Variable to $releaseTag" -Verbose
+ Write-Host -Object "##$vstsCommandString"
+ displayName: Set Release Tag
+
+- pwsh: |
+ $azureVersion = '$(ReleaseTag)'.ToLowerInvariant() -replace '\.', '-'
+ $vstsCommandString = "vso[task.setvariable variable=AzureVersion]$azureVersion"
+ Write-Host "sending " + $vstsCommandString
+ Write-Host "##$vstsCommandString"
+ displayName: Set container name
diff --git a/tools/releaseBuild/azureDevOps/templates/release-UpdateDepsJson.yml b/tools/releaseBuild/azureDevOps/templates/release-UpdateDepsJson.yml
new file mode 100644
index 00000000000..8c2b94dae12
--- /dev/null
+++ b/tools/releaseBuild/azureDevOps/templates/release-UpdateDepsJson.yml
@@ -0,0 +1,43 @@
+jobs:
+- job: UpdateDepsFiles
+ displayName: Update deps files
+ pool:
+ vmImage: windows-latest
+ variables:
+ - group: 'Azure Blob variable group'
+ steps:
+ - download: releasePipeline
+ artifact: finalResults
+ patterns: '**/PowerShell*-win-x64.zip'
+
+ - download: releasePipeline
+ artifact: BuildInfoJson
+
+ - pwsh: |
+ $fileName = (Get-Item "$ENV:PIPELINE_WORKSPACE/releasePipeline/BuildInfoJson/*.json").BaseName
+ if ($fileName -notin 'stable','preview')
+ {
+ throw "Unexpected fileName: $fileName"
+ }
+
+ $vstsCommand = "vso[task.setvariable variable=ContainerName]$fileName"
+ Write-Host "##$vstsCommand"
+ displayName: Determine container name
+
+ - pwsh: |
+ $zipFile = (Get-Item "$ENV:PIPELINE_WORKSPACE/releasePipeline/finalResults/PowerShell*-winx64.zip")
+ Expand-Archive -Path $zipFile -Destination "$ENV:PIPELINE_WORKSPACE/expanded"
+
+ $pwshDepsFile = Get-Item "$ENV:PIPELINE_WORKSPACE/expanded/pwsh.deps.json"
+ $vstsCommand = "vso[task.setvariable variable=FileToUpload]$pwshDepsFile"
+ Write-Host "##$vstsCommand"
+ displayName: Determine file to upload
+
+ - task: AzureFileCopy@2
+ displayName: 'AzureBlob pwsh.deps.json file Copy'
+ inputs:
+ SourcePath: '$(FileToUpload)'
+ azureSubscription: '$(AzureFileCopySubscription)'
+ Destination: AzureBlob
+ storage: '$(StorageAccount)'
+ ContainerName: ps-deps-json
diff --git a/tools/releaseBuild/azureDevOps/templates/release-ValidatePackageNames.yml b/tools/releaseBuild/azureDevOps/templates/release-ValidatePackageNames.yml
new file mode 100644
index 00000000000..d0276ab11fe
--- /dev/null
+++ b/tools/releaseBuild/azureDevOps/templates/release-ValidatePackageNames.yml
@@ -0,0 +1,88 @@
+steps:
+- pwsh: |
+ Get-ChildItem ENV:
+ displayName: Capture environment
+
+- template: release-SetReleaseTagAndContainerName.yml
+
+- pwsh: |
+ $name = "{0}_{1:x}" -f '$(releaseTag)', (Get-Date).Ticks
+ Write-Host $name
+ Write-Host "##vso[build.updatebuildnumber]$name"
+ displayName: Set Release Name
+
+- pwsh: |
+ $azcopy = "C:\Program Files (x86)\Microsoft SDKs\Azure\AzCopy\AzCopy.exe"
+
+ & $azcopy /Source:https://$(StorageAccount).blob.core.windows.net/$(AzureVersion) /Dest:$(System.ArtifactsDirectory) /S /SourceKey:$(StorageAccountKey)
+ displayName: Download Azure Artifacts
+
+- pwsh: |
+ Get-ChildItem $(System.ArtifactsDirectory)\* -recurse | Select-Object -ExpandProperty Name
+ displayName: Capture Artifact Listing
+
+- pwsh: |
+ $message = @()
+ Get-ChildItem $(System.ArtifactsDirectory)\* -recurse -filter *.rpm | ForEach-Object {
+ if($_.Name -notmatch 'powershell\-(preview-|lts-)?\d\.\d\.\d(_[a-z]*\.\d+)?-1.(rhel|centos).\d+\.x86_64\.rpm')
+ {
+ $messageInstance = "$($_.Name) is not a valid package name"
+ $message += $messageInstance
+ Write-Warning $messageInstance
+ }
+ }
+ if($message.count -gt 0){throw ($message | out-string)}
+ displayName: Validate RPM package names
+
+- pwsh: |
+ $message = @()
+ Get-ChildItem $(System.ArtifactsDirectory)\* -recurse -filter *.tar.gz | ForEach-Object {
+ if($_.Name -notmatch 'powershell-(lts-)?\d\.\d\.\d\-([a-z]*.\d+\-)?(linux|osx|linux-alpine)+\-(x64\-fxdependent|x64|arm32|arm64)\.(tar\.gz)')
+ {
+ $messageInstance = "$($_.Name) is not a valid package name"
+ $message += $messageInstance
+ Write-Warning $messageInstance
+ }
+ }
+ if($message.count -gt 0){throw ($message | out-string)}
+ displayName: Validate Tar.Gz Package Names
+
+- pwsh: |
+ $message = @()
+ Get-ChildItem $(System.ArtifactsDirectory)\* -recurse -filter *.pkg | ForEach-Object {
+ if($_.Name -notmatch 'powershell-(lts-)?\d\.\d\.\d\-([a-z]*.\d+\-)?osx(\.10\.12)?\-x64\.pkg')
+ {
+ $messageInstance = "$($_.Name) is not a valid package name"
+ $message += $messageInstance
+ Write-Warning $messageInstance
+ }
+ }
+ if($message.count -gt 0){throw ($message | out-string)}
+ displayName: Validate PKG Package Names
+
+- pwsh: |
+ $message = @()
+ Get-ChildItem $(System.ArtifactsDirectory)\* -recurse -include *.zip, *.msi | ForEach-Object {
+ if($_.Name -notmatch 'PowerShell-\d\.\d\.\d\-([a-z]*.\d+\-)?win\-(fxdependent|x64|arm32|arm64|x86|fxdependentWinDesktop)\.(msi|zip){1}')
+ {
+ $messageInstance = "$($_.Name) is not a valid package name"
+ $message += $messageInstance
+ Write-Warning $messageInstance
+ }
+ }
+
+ if($message.count -gt 0){throw ($message | out-string)}
+ displayName: Validate Zip and MSI Package Names
+
+- pwsh: |
+ $message = @()
+ Get-ChildItem $(System.ArtifactsDirectory)\* -recurse -filter *.deb | ForEach-Object {
+ if($_.Name -notmatch 'powershell(-preview|-lts)?_\d\.\d\.\d([\-~][a-z]*.\d+)?-\d\.(debian|ubuntu){1}\.\d+(\.\d+)?_amd64\.deb')
+ {
+ $messageInstance = "$($_.Name) is not a valid package name"
+ $message += $messageInstance
+ Write-Warning $messageInstance
+ }
+ }
+ if($message.count -gt 0){throw ($message | out-string)}
+ displayName: Validate Deb Package Names