From 86ad3c84cbdd8afe26fb002abe71546a4d1ba3a8 Mon Sep 17 00:00:00 2001 From: "Dustin L. Howett" Date: Fri, 22 Sep 2023 15:36:39 -0500 Subject: [PATCH] nightly: deploy an appinstaller to an Azure storage account (!) (#16013) After the nightly build completes, we'll automatically generate a .appinstaller and publich it plus the msixbundle to an Azure Storage account. I had to add step/job customization to the publish step in the full release pipeline template. The .appinstaller hardcodes our XAML dependency, which makes it a bit of a pain. We can revisit this later, and publish our dependencies directly and automatically instead of hardcoding them. I am considering moving the appinstaller generation step to the MSIX bundling job, but this works right now and is not too terrible. Closes #774 --- .github/actions/spelling/allow/microsoft.txt | 1 + build/config/template.appinstaller | 38 ++++++++++ build/pipelines/nightly.yml | 20 ++++++ .../job-deploy-to-azure-storage.yml | 70 +++++++++++++++++++ .../job-merge-msix-into-bundle.yml | 2 + .../pipeline-full-release-build.yml | 4 ++ .../New-AppInstallerFromTemplateAndBundle.ps1 | 42 +++++++++++ 7 files changed, 177 insertions(+) create mode 100644 build/config/template.appinstaller create mode 100644 build/pipelines/templates-v2/job-deploy-to-azure-storage.yml create mode 100644 build/scripts/New-AppInstallerFromTemplateAndBundle.ps1 diff --git a/.github/actions/spelling/allow/microsoft.txt b/.github/actions/spelling/allow/microsoft.txt index 80335179b32..1ed1c6da897 100644 --- a/.github/actions/spelling/allow/microsoft.txt +++ b/.github/actions/spelling/allow/microsoft.txt @@ -4,6 +4,7 @@ advapi altform altforms appendwttlogging +appinstaller appx appxbundle appxerror diff --git a/build/config/template.appinstaller b/build/config/template.appinstaller new file mode 100644 index 00000000000..7977fd950b8 --- /dev/null +++ b/build/config/template.appinstaller @@ -0,0 +1,38 @@ + + + + + + + + + + + + + + + diff --git a/build/pipelines/nightly.yml b/build/pipelines/nightly.yml index 025a643509f..6ec737157fd 100644 --- a/build/pipelines/nightly.yml +++ b/build/pipelines/nightly.yml @@ -10,6 +10,12 @@ schedules: name: $(BuildDefinitionName)_$(date:yyMM).$(date:dd)$(rev:rrr) +parameters: + - name: publishToAzure + displayName: "Deploy to **PUBLIC** Azure Storage" + type: boolean + default: true + extends: template: templates-v2\pipeline-full-release-build.yml parameters: @@ -21,3 +27,17 @@ extends: publishSymbolsToPublic: true publishVpackToWindows: false symbolExpiryTime: 15 # Nightly builds do not keep symbols for very long! + ${{ if eq(true, parameters.publishToAzure) }}: + extraPublishJobs: + - template: job-deploy-to-azure-storage.yml + parameters: + pool: + name: SHINE-INT-S + dependsOn: [PublishSymbols] + storagePublicRootURL: $(AppInstallerRootURL) + subscription: $(AzureSubscriptionName) + storageAccount: $(AzureStorageAccount) + storageContainer: $(AzureStorageContainer) + buildConfiguration: Release + environment: production-canary + diff --git a/build/pipelines/templates-v2/job-deploy-to-azure-storage.yml b/build/pipelines/templates-v2/job-deploy-to-azure-storage.yml new file mode 100644 index 00000000000..140a513ee14 --- /dev/null +++ b/build/pipelines/templates-v2/job-deploy-to-azure-storage.yml @@ -0,0 +1,70 @@ +parameters: + - name: buildConfiguration + type: string + - name: pool + type: object + default: [] + - name: dependsOn + type: object + default: null + - name: artifactStem + type: string + default: '' + - name: variables + type: object + default: {} + - name: environment + type: string + - name: storagePublicRootURL + type: string + - name: subscription + type: string + - name: storageAccount + type: string + - name: storageContainer + type: string + +jobs: +- deployment: DeployAzure + ${{ if ne(length(parameters.pool), 0) }}: + pool: ${{ parameters.pool }} + displayName: Publish to Azure Storage (Prod) + dependsOn: ${{ parameters.dependsOn }} + variables: + ${{ insert }}: ${{ parameters.variables }} + environment: ${{ parameters.environment }} + strategy: + runOnce: + deploy: + steps: + - download: none + + - checkout: self + clean: true + fetchDepth: 1 + fetchTags: false # Tags still result in depth > 1 fetch; we don't need them here + submodules: true + persistCredentials: True + + - task: DownloadPipelineArtifact@2 + displayName: Download MSIX Bundle Artifact + inputs: + artifactName: appxbundle-${{ parameters.buildConfiguration }}${{ parameters.artifactStem }} + downloadPath: '$(Build.SourcesDirectory)/_out' + itemPattern: '**/*.msixbundle' + + - pwsh: |- + $b = Get-Item _out/*.msixbundle + ./build/scripts/New-AppInstallerFromTemplateAndBundle.ps1 -BundlePath $b.FullName -AppInstallerTemplatePath ./build/config/template.appinstaller -AppInstallerRoot "${{ parameters.storagePublicRootURL }}" -OutputPath _out/Microsoft.WindowsTerminalCanary.appinstaller + displayName: "Produce AppInstaller for MSIX bundle" + + - task: AzureFileCopy@5 + displayName: Publish to Storage Account + inputs: + sourcePath: _out/* + Destination: AzureBlob + azureSubscription: ${{ parameters.subscription }} + storage: ${{ parameters.storageAccount }} + ContainerName: ${{ parameters.storageContainer }} + AdditionalArgumentsForBlobCopy: "--content-type application/octet-stream" + diff --git a/build/pipelines/templates-v2/job-merge-msix-into-bundle.yml b/build/pipelines/templates-v2/job-merge-msix-into-bundle.yml index 121e07b4417..1457178f973 100644 --- a/build/pipelines/templates-v2/job-merge-msix-into-bundle.yml +++ b/build/pipelines/templates-v2/job-merge-msix-into-bundle.yml @@ -48,6 +48,8 @@ jobs: BundleStemName: Microsoft.WindowsTerminal ${{ elseif eq(parameters.branding, 'Preview') }}: BundleStemName: Microsoft.WindowsTerminalPreview + ${{ elseif eq(parameters.branding, 'Canary') }}: + BundleStemName: Microsoft.WindowsTerminalCanary ${{ else }}: BundleStemName: WindowsTerminalDev JobOutputDirectory: '$(System.ArtifactsDirectory)/bundle' diff --git a/build/pipelines/templates-v2/pipeline-full-release-build.yml b/build/pipelines/templates-v2/pipeline-full-release-build.yml index 42c5c6250d8..b785deef64e 100644 --- a/build/pipelines/templates-v2/pipeline-full-release-build.yml +++ b/build/pipelines/templates-v2/pipeline-full-release-build.yml @@ -51,6 +51,9 @@ parameters: type: boolean default: false + - name: extraPublishJobs + type: object + default: [] - name: pool type: object default: @@ -193,4 +196,5 @@ stages: includePublicSymbolServer: ${{ parameters.publishSymbolsToPublic }} symbolExpiryTime: ${{ parameters.symbolExpiryTime }} + - ${{ parameters.extraPublishJobs }} ... diff --git a/build/scripts/New-AppInstallerFromTemplateAndBundle.ps1 b/build/scripts/New-AppInstallerFromTemplateAndBundle.ps1 new file mode 100644 index 00000000000..b49ad244ffb --- /dev/null +++ b/build/scripts/New-AppInstallerFromTemplateAndBundle.ps1 @@ -0,0 +1,42 @@ +[CmdletBinding()] +Param( + [Parameter(Mandatory, + HelpMessage="Path to the .msixbundle")] + [string] + $BundlePath, + + [Parameter(Mandatory, + HelpMessage="Path to the .appinstaller template")] + [string] + $AppInstallerTemplatePath, + + [string] + $AppInstallerRoot, + + [Parameter(Mandatory, + HelpMessage="Output Path")] + [string] + $OutputPath +) + +$ErrorActionPreference = "Stop" + +$sentinelFile = New-TemporaryFile +$directory = New-Item -Type Directory "$($sentinelFile.FullName)_Package" +Remove-Item $sentinelFile -Force -EA:Ignore + +$bundle = (Get-Item $BundlePath) +& tar.exe -x -f $bundle.FullName -C $directory AppxMetadata/AppxBundleManifest.xml + +$xml = [xml](Get-Content (Join-Path $directory "AppxMetadata\AppxBundleManifest.xml")) +$name = $xml.Bundle.Identity.Name +$version = $xml.Bundle.Identity.Version + +$doc = (Get-Content -ReadCount 0 $AppInstallerTemplatePath) +$doc = $doc -Replace '\$\$ROOT\$\$',$AppInstallerRoot +$doc = $doc -Replace '\$\$NAME\$\$',$name +$doc = $doc -Replace '\$\$VERSION\$\$',$version +$doc = $doc -Replace '\$\$PACKAGE\$\$',$bundle.Name +$doc | Out-File -Encoding utf8NoBOM $OutputPath + +Get-Item $OutputPath