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