@samitouri / QOSAMI-WSL / commits / 6bc39a6b

Split x64/arm64 builds into parallel pipeline stages (#14497)

* Split x64/arm64 builds into parallel pipeline stages Restructure the CI/CD pipeline to build x64 and arm64 in parallel instead of sequentially, reducing end-to-end build time. Pipeline shapes: - PR: build_x64 ∥ build_arm64 → test (uses installer.msix directly) - Nightly: build_x64 ∥ build_arm64 → package → test (dev-cert bundle) - Release: build_x64 ∥ build_arm64 → package → test (ESRP-signed bundle) Key changes: - Extract shared build-job.yml template parameterized by platform - Add package-stage.yml that creates msixbundle from both platform artifacts, ESRP-signs for release, dev-cert signs for nightly - PR tests run immediately after x64 build using installer.msix (no package stage, no bundle needed) - Release/nightly tests wait for the package stage and test the real signed bundle that gets published - CloudTest configs are parameterized: release tests pull the bundle from the [package] artifact, PR tests use installer.msix from [drop] - arm64 + formatting checks always run in parallel with x64 but don't block the PR test gate - CodeQL runs in the arm64 stage (off the critical path) - flight-stage and nuget-stage updated for new stage names * formatting * Remove redundant runtime conditions on release-only tasks These tasks are already wrapped in compile-time conditionals which prevent them from being added to the pipeline definition for non-release builds. The runtime condition checks can never evaluate to false at that point, so they are pure noise. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Address review feedback from OneBlue - Remove wslcsdk NuGet staging (wslc is not in master) - Always include [package] provider in TestMap.xml.in instead of conditionally injecting via PACKAGE_PROVIDER_BLOCK cmake variable. PR builds simply use [drop] as TEST_PACKAGE_PROVIDER; the [package] provider exists but is unused. - Add BUNDLE_ONLY cmake option so the package pipeline stage reuses cmake's existing bundle target instead of forking makeappx logic. This locks the SDK version via cmake (CMAKE_SYSTEM_VERSION) and keeps bundle creation logic in one place (msixinstaller/CMakeLists.txt). The pipeline now copies msix files to expected paths, runs a fast cmake configure with -DBUNDLE_ONLY=TRUE, and builds the bundle target. - Remove dead NuGet binary restore step in package stage. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Address PR review feedback - Fix PACKAGE_VERSION regex: escape dots so only A.B.C.D is accepted - Consolidate CMake defaults (build type, config types, output dir) before BUNDLE_ONLY block to avoid duplication - Nightly tests now use the full bundle from the package stage instead of installer.msix (new INCLUDE_PACKAGE_STAGE cmake variable) - Package stage reuses version output from build stage instead of recomputing it Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --------- Co-authored-by: Ben Hillis <benhill@ntdev.microsoft.com> Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Ben Hillis committed Apr 9, 2026 at 13:31 UTC 6bc39a6bd78297a8bf3eab29d7db43774ca930af
13 files changed +652 -373
.pipelines/build-job.yml new
+266
@@ -0,0 +1,266 @@
1 +# Shared build job template for x64 and arm64 platforms.
2 +# Parameterized by platform, with optional test artifact and CodeQL support.
3 +
4 +parameters:
5 + - name: platform
6 + type: string
7 +
8 + - name: isRelease
9 + type: boolean
10 +
11 + - name: isNightly
12 + type: boolean
13 +
14 + - name: packageVersion
15 + type: string
16 +
17 + - name: traceLoggingConfig
18 + type: string
19 + default: ''
20 +
21 + - name: targets
22 + type: object
23 +
24 + - name: pool
25 + type: string
26 + default: ''
27 +
28 + - name: vsoOrg
29 + type: string
30 +
31 + - name: vsoProject
32 + type: string
33 +
34 + - name: artifactSuffix
35 + type: string
36 +
37 + - name: esrp
38 + type: object
39 +
40 + # x64-only features
41 + - name: includeTestArtifacts
42 + type: boolean
43 + default: false
44 +
45 + - name: includeCodeQL
46 + type: boolean
47 + default: false
48 +
49 + - name: dependsOn
50 + type: string
51 + default: ''
52 +
53 +jobs:
54 + - job: build_${{ parameters.platform }}
55 + displayName: "Build WSL package (${{ parameters.platform }})"
56 + timeoutInMinutes: 120
57 + ${{ if ne(parameters.dependsOn, '') }}:
58 + dependsOn: ${{ parameters.dependsOn }}
59 +
60 + ${{ if eq(parameters.pool, '') }}:
61 + pool: {'type': 'windows'}
62 +
63 + ${{ else }}:
64 + pool: ${{ parameters.pool }}
65 +
66 + variables:
67 + NUGET_PLUGIN_HANDSHAKE_TIMEOUT_IN_SECONDS: 60
68 + NUGET_PLUGIN_REQUEST_TIMEOUT_IN_SECONDS: 60
69 + ob_outputDirectory: '$(Build.SourcesDirectory)\out'
70 + ob_artifactBaseName: 'drop_wsl'
71 + ob_artifactSuffix: '${{ parameters.artifactSuffix }}'
72 + packageStagingDir: '$(Build.SourcesDirectory)\packageStagingDir'
73 + ${{ if parameters.includeTestArtifacts }}:
74 + ob_sdl_codeSignValidation_excludes: -|**testbin\**
75 + ${{ if parameters.includeCodeQL }}:
76 + Codeql.PublishDatabaseLog: true
77 + Codeql.SourceRoot: src
78 + ${{ if eq(parameters.isRelease, true) }}:
79 + packageInputDirArg: '-DPACKAGE_INPUT_DIR=$(packageStagingDir)'
80 + ${{ else }}:
81 + packageInputDirArg: ''
82 +
83 + steps:
84 +
85 + - ${{ if parameters.includeCodeQL }}:
86 + - task: CodeQL3000Init@0
87 + inputs:
88 + Enabled: ${{ and(parameters.isNightly, eq(variables['Build.SourceBranch'], 'refs/heads/main'))}}
89 +
90 + - ${{ if eq(parameters.isRelease, true) }}:
91 + - task: UseDotNet@2
92 + displayName: Install .NET Core SDK (required by EsrpCodeSigning)
93 + inputs:
94 + packageType: "sdk"
95 +
96 + - task: PowerShell@2
97 + displayName: Set trace logging configuration
98 + condition: ne('${{ parameters.traceLoggingConfig }}', '')
99 + inputs:
100 + targetType: 'inline'
101 + script: 'Set-Content -Path src/windows/inc/traceloggingconfig.h -Value $env:config.replace("\n", "`n")'
102 + env:
103 + config: '${{ parameters.traceLoggingConfig }}'
104 +
105 + - task: PowerShell@2
106 + displayName: "Compute package version"
107 + name: version
108 + inputs:
109 + targetType: inline
110 + ${{ if eq(parameters.packageVersion, '') }}:
111 + script: |
112 + $gitversion_version = (Select-Xml -Path packages.config -XPath '/packages/package[@id=''GitVersion.CommandLine'']/@version').Node.Value
113 + $env:path = "packages/GitVersion.CommandLine.$gitversion_version/tools;$env:path"
114 + . .\tools\devops\version_functions.ps1
115 + $version = Get-VersionInfo -Nightly $${{ parameters.isNightly }}
116 + Write-Host "##vso[task.setvariable variable=WSL_PACKAGE_VERSION;isOutput=true]$($version.MsixVersion)"
117 + Write-Host "##vso[task.setvariable variable=WSL_NUGET_PACKAGE_VERSION;isOutput=true]$($version.NugetVersion)"
118 +
119 + ${{ else }}:
120 + script: |
121 + Write-Host "##vso[task.setvariable variable=WSL_PACKAGE_VERSION;isOutput=true]$([string]('${{ parameters.packageVersion }}' + '.0'))"
122 + Write-Host "##vso[task.setvariable variable=WSL_NUGET_PACKAGE_VERSION;isOutput=true]$([string]('${{ parameters.packageVersion }}'))"
123 +
124 + - task: CMake@1
125 + displayName: "CMake ${{ parameters.platform }}"
126 + inputs:
127 + workingDirectory: "."
128 + cmakeArgs: . --fresh -A ${{ parameters.platform }} -DCMAKE_BUILD_TYPE=Release -DCMAKE_SYSTEM_VERSION=10.0.26100.0 -DPACKAGE_VERSION=$(version.WSL_PACKAGE_VERSION) -DWSL_NUGET_PACKAGE_VERSION=$(version.WSL_NUGET_PACKAGE_VERSION) -DSKIP_PACKAGE_SIGNING=${{ parameters.isRelease }} -DOFFICIAL_BUILD=${{ parameters.isRelease }} -DINCLUDE_PACKAGE_STAGE=${{ or(parameters.isRelease, parameters.isNightly) }} -DPIPELINE_BUILD_ID=$(Build.BuildId) -DVSO_ORG=${{ parameters.vsoOrg }} -DVSO_PROJECT=${{ parameters.vsoProject }} -DWSL_BUILD_WSL_SETTINGS=true $(packageInputDirArg)\${{ parameters.platform }}
129 +
130 + # Workaround for WSL Settings NuGet restore authentication issue
131 + - script: _deps\nuget.exe restore -NonInteractive
132 +
133 + - ${{ each target in parameters.targets }}:
134 + - script: cmake --build . --config Release --target ${{ target.target }} -- -m
135 + displayName: "Build ${{ target.target }} (${{ parameters.platform }})"
136 +
137 + - ${{ if eq(parameters.isRelease, true) }}:
138 + - task: SFP.build-tasks.custom-build-task-1.EsrpCodeSigning@5
139 + displayName: "Sign ${{ target.target }} (${{ parameters.platform }})"
140 + inputs:
141 + ConnectedServiceName: ${{ parameters.esrp.ConnectedServiceName}}
142 + signConfigType: ${{ parameters.esrp.signConfigType }}
143 + SessionTimeout: ${{ parameters.esrp.SessionTimeout }}
144 + MaxConcurrency: ${{ parameters.esrp.MaxConcurrency }}
145 + MaxRetryAttempts: ${{ parameters.esrp.MaxRetryAttempts }}
146 + ServiceEndpointUrl: ${{ parameters.esrp.ServiceEndpointUrl }}
147 + AuthAKVName: ${{ parameters.esrp.AuthAKVName }}
148 + AuthSignCertName: ${{ parameters.esrp.AuthSignCertName }}
149 + AppRegistrationClientId: ${{ parameters.esrp.AppRegistrationClientId }}
150 + AppRegistrationTenantId: ${{ parameters.esrp.AppRegistrationTenantId }}
151 + FolderPath: "bin\\${{ parameters.platform }}\\Release"
152 + Pattern: "${{ target.pattern }}"
153 + UseMSIAuthentication: true
154 + EsrpClientId: ${{ parameters.esrp.EsrpClientId }}
155 + inlineOperation: |
156 + [
157 + {
158 + "KeyCode": "CP-230012",
159 + "OperationCode": "SigntoolSign",
160 + "Parameters" : {
161 + "OpusName" : "Microsoft",
162 + "OpusInfo" : "http://www.microsoft.com",
163 + "FileDigest" : "/fd \"SHA256\"",
164 + "PageHash" : "/NPH",
165 + "TimeStamp" : "/tr \"http://rfc3161.gtm.corp.microsoft.com/TSS/HttpTspServer\" /td sha256"
166 + },
167 + "ToolName" : "sign",
168 + "ToolVersion" : "1.0"
169 + },
170 + {
171 + "KeyCode" : "CP-230012",
172 + "OperationCode" : "SigntoolVerify",
173 + "Parameters" : {},
174 + "ToolName" : "sign",
175 + "ToolVersion" : "1.0"
176 + }
177 + ]
178 +
179 + - task: PowerShell@2
180 + displayName: "Copy signed ${{ target.target }} to staging (${{ parameters.platform }})"
181 + inputs:
182 + targetType: inline
183 + script: |
184 + $arch = '${{ parameters.platform }}'
185 + $pattern = '${{ target.pattern }}'
186 + $inputDir = "bin\$arch\Release"
187 + $outputDir = "$(packageStagingDir)\$arch"
188 + New-Item -ItemType Directory -Path "$outputDir\wslsettings" -Force
189 + foreach ($file in $pattern.Split(',')) {
190 + $sourcePath = Join-Path $inputDir $file
191 + if (Test-Path $sourcePath) {
192 + $destPath = Join-Path $outputDir $file
193 + Write-Host "Copying signed file: $sourcePath -> $destPath"
194 + Copy-Item -Path $sourcePath -Destination $destPath -Force
195 + } else {
196 + Write-Warning "File not found: $sourcePath"
197 + }
198 + }
199 +
200 + - script: cmake --build . --config Release -- -m
201 + displayName: "Build remaining targets (${{ parameters.platform }})"
202 +
203 + - task: PowerShell@2
204 + displayName: "Stage MSI and installer msix (${{ parameters.platform }})"
205 + inputs:
206 + targetType: inline
207 + script: |
208 + New-Item -ItemType Directory -Path "$(ob_outputDirectory)\bundle" -Force
209 + New-Item -ItemType Directory -Path "$(ob_outputDirectory)\installer" -Force
210 + Copy-Item -Path "bin\${{ parameters.platform }}\release\wsl.msi" -Destination "$(ob_outputDirectory)\bundle\wsl.$(version.WSL_PACKAGE_VERSION).${{ parameters.platform }}.msi"
211 + Copy-Item -Path "bin\${{ parameters.platform }}\release\installer.msix" -Destination "$(ob_outputDirectory)\installer\installer.${{ parameters.platform }}.msix"
212 + if (Test-Path "generated\dev-cert.pfx") { Copy-Item "generated\dev-cert.pfx" "$(ob_outputDirectory)\installer\dev-cert.pfx" }
213 +
214 + - powershell: |
215 + $binFolder = ".\bin\${{ parameters.platform }}\Release"
216 + $pdbFolder = Join-Path $(ob_outputDirectory) "pdb\${{ parameters.platform }}\Release"
217 + mkdir $pdbFolder
218 + foreach ($filter in @("*.pdb", "*.debug"))
219 + {
220 + foreach ($e in (Get-ChildItem -Recurse -Path $binFolder -Filter $filter)) {Move-Item -Path $e.fullname -Destination (Join-Path $pdbFolder $e.name)}
221 + }
222 + displayName: Collect symbols (${{ parameters.platform }})
223 +
224 + # Test artifacts (x64 only)
225 + - ${{ if parameters.includeTestArtifacts }}:
226 + - powershell: |
227 + $taefVersion = (Select-Xml -Path packages.config -XPath '/packages/package[@id=''Microsoft.Taef'']/@version').Node.Value
228 + New-Item -ItemType Directory -Path "$(ob_outputDirectory)\bundle" -Force
229 + mkdir $(ob_outputDirectory)\testbin\x64\release
230 +
231 + Move-Item -Path "bin\x64\release\wsltests.dll" -Destination "$(ob_outputDirectory)\testbin\x64\release\wsltests.dll"
232 + Move-Item -Path "bin\x64\release\testplugin.dll" -Destination "$(ob_outputDirectory)\testbin\x64\release\testplugin.dll"
233 + Copy-Item -Path "bin\x64\release\installer.msix" -Destination "$(ob_outputDirectory)\testbin\x64\release\installer.msix"
234 + Move-Item -Path "packages\Microsoft.Taef.$taefVersion\build\Binaries\x64" -Destination "$(ob_outputDirectory)\testbin\x64\release\taef"
235 +
236 + Move-Item -Path "bin\x64\cloudtest" -Destination "$(ob_outputDirectory)\testbin\x64\cloudtest"
237 +
238 + Move-Item -Path "tools\test\test-setup.ps1" -Destination "$(ob_outputDirectory)\testbin\test-setup.ps1"
239 + Move-Item -Path "tools\test\CloudTest-Setup.bat" -Destination "$(ob_outputDirectory)\testbin\CloudTest-Setup.bat"
240 + Move-Item -Path "diagnostics\wsl.wprp" -Destination "$(ob_outputDirectory)\testbin\wsl.wprp"
241 + Move-Item -Path "test\linux\unit_tests" -Destination "$(ob_outputDirectory)\testbin\unit_tests"
242 +
243 + $TestDistroVersion = (Select-Xml -Path packages.config -XPath '/packages/package[@id=''Microsoft.WSL.TestDistro'']/@version').Node.Value
244 + Copy-Item "packages\Microsoft.WSL.TestDistro.$TestDistroVersion\x64\test_distro.tar.xz" "$(ob_outputDirectory)\testbin\x64"
245 +
246 + displayName: Stage test artifacts
247 +
248 + - task: PublishSymbols@2
249 + displayName: Publish symbols (${{ parameters.platform }})
250 + inputs:
251 + SymbolServerType: "TeamServices"
252 + TreatNotIndexedAsWarning: true
253 + SymbolsProduct: WSL
254 + SymbolsVersion: $(version.WSL_PACKAGE_VERSION)
255 + SearchPattern: |
256 + $(ob_outputDirectory)/pdb/**/*.pdb
257 +
258 + - ${{ if ne(parameters.pool, '') }}:
259 + - task: PublishPipelineArtifact@1
260 + inputs:
261 + targetPath: $(ob_outputDirectory)
262 + artifactName: $(ob_artifactBaseName)$(ob_artifactSuffix)
263 +
264 + - ${{ if parameters.includeCodeQL }}:
265 + - task: CodeQL3000Finalize@0
266 + condition: ${{ and(parameters.isNightly, eq(variables['Build.SourceBranch'], 'refs/heads/main'))}}
.pipelines/build-stage.yml
+56 -336
@@ -11,14 +11,18 @@ parameters:
11 type: boolean
12 default: false
13
14 + - name: traceLoggingConfig
15 + type: string
16 + default: ''
17 +
18 - name: nugetPackages
19 type: object
20 default:
21 - Microsoft.WSL.PluginApi.nuspec
22
19 - - name: traceLoggingConfig
20 - type: string
21 - default: ''
23 + - name: includePackageStage
24 + type: boolean
25 + default: true
26
27 - name: targets
28 type: object
@@ -30,12 +34,6 @@ parameters:
34 - target: "msipackage"
35 pattern: "wsl.msi"
36
33 - - name: platforms
34 - type: object
35 - default:
36 - - x64
37 - - arm64
38 -
37 - name: pool
38 type: string
39 default: ''
@@ -62,10 +60,32 @@ parameters:
60 EsrpClientId: $(EsrpClientId)
61
62 stages:
65 - - stage: build
63 + # ── x64 build stage ──────────────────────────────────────────────────
64 + # Runs in parallel with build_arm64. Tests depend on this stage.
65 + - stage: build_x64
66 + dependsOn: []
67 jobs:
67 - - job:
68 -
68 + - template: build-job.yml
69 + parameters:
70 + platform: x64
71 + artifactSuffix: '_build'
72 + isRelease: ${{ parameters.isRelease }}
73 + isNightly: ${{ parameters.isNightly }}
74 + packageVersion: ${{ parameters.packageVersion }}
75 + traceLoggingConfig: ${{ parameters.traceLoggingConfig }}
76 + targets: ${{ parameters.targets }}
77 + pool: ${{ parameters.pool }}
78 + vsoOrg: ${{ parameters.vsoOrg }}
79 + vsoProject: ${{ parameters.vsoProject }}
80 + esrp: ${{ parameters.esrp }}
81 + includeTestArtifacts: true
82 +
83 + # ── arm64 build stage (runs in parallel with build_x64) ──────────────
84 + # Includes formatting/localization checks as a first job, then arm64 build.
85 + - stage: build_arm64
86 + dependsOn: []
87 + jobs:
88 + - job: checks
89 displayName: "Formatting & localization checks"
90 timeoutInMinutes: 30
91
@@ -101,328 +121,28 @@ stages:
121 arguments: "-ModifiedOnly $false -Verify $true"
122 displayName: "Clang-format check"
123
104 - - job: build
105 - displayName: "Build WSL package"
106 - timeoutInMinutes: 120
107 -
108 - ${{ if eq(parameters.pool, '') }}:
109 - pool: {'type': 'windows'}
110 -
111 - ${{ else }}:
124 + - template: build-job.yml
125 + parameters:
126 + platform: arm64
127 + artifactSuffix: '_build_arm64'
128 + dependsOn: checks
129 + isRelease: ${{ parameters.isRelease }}
130 + isNightly: ${{ parameters.isNightly }}
131 + packageVersion: ${{ parameters.packageVersion }}
132 + traceLoggingConfig: ${{ parameters.traceLoggingConfig }}
133 + targets: ${{ parameters.targets }}
134 pool: ${{ parameters.pool }}
113 -
114 - variables:
115 - NUGET_PLUGIN_HANDSHAKE_TIMEOUT_IN_SECONDS: 60
116 - NUGET_PLUGIN_REQUEST_TIMEOUT_IN_SECONDS: 60
117 - ob_outputDirectory: '$(Build.SourcesDirectory)\out'
118 - ob_artifactBaseName: 'drop_wsl'
119 - ob_artifactSuffix: '_build'
120 - ob_sdl_codeSignValidation_excludes: -|**testbin\**
121 - Codeql.PublishDatabaseLog: true
122 - Codeql.SourceRoot: src
123 - packageStagingDir: '$(Build.SourcesDirectory)\packageStagingDir'
124 - ${{ if eq(parameters.isRelease, 'true') }}:
125 - packageInputDirArg: '-DPACKAGE_INPUT_DIR=$(packageStagingDir)'
126 - ${{ else }}:
127 - packageInputDirArg: ''
128 -
129 - steps:
130 -
131 - - task: CodeQL3000Init@0
132 - inputs:
133 - Enabled: ${{ and(parameters.isNightly, eq(variables['Build.SourceBranch'], 'refs/heads/main'))}}
134 -
135 - - task: UseDotNet@2
136 - displayName: Install .NET Core SDK (required by EsrpCodeSigning)
137 - condition: and(succeeded(), eq('${{ parameters.isRelease }}', true))
138 - inputs:
139 - packageType: "sdk"
140 -
141 - - task: PowerShell@2
142 - displayName: Set trace logging configuration
143 - condition: ne('${{ parameters.traceLoggingConfig }}', '')
144 - inputs:
145 - targetType: 'inline'
146 - script: 'Set-Content -Path src/windows/inc/traceloggingconfig.h -Value $env:config.replace("\n", "`n")'
147 - env:
148 - config: '${{ parameters.traceLoggingConfig }}'
149 -
150 - - task: PowerShell@2
151 - displayName: "Compute package version"
152 - name: version
153 - inputs:
154 - targetType: inline
155 - ${{ if eq(parameters.packageVersion, '') }}:
156 - script: |
157 - $gitversion_version = (Select-Xml -Path packages.config -XPath '/packages/package[@id=''GitVersion.CommandLine'']/@version').Node.Value
158 - $env:path = "packages/GitVersion.CommandLine.$gitversion_version/tools;$env:path"
159 - . .\tools\devops\version_functions.ps1
160 - $version = Get-VersionInfo -Nightly $${{ parameters.isNightly }}
161 - Write-Host "##vso[task.setvariable variable=WSL_PACKAGE_VERSION;isOutput=true]$($version.MsixVersion)"
162 - Write-Host "##vso[task.setvariable variable=WSL_NUGET_PACKAGE_VERSION;isOutput=true]$($version.NugetVersion)"
163 -
164 - ${{ else }}:
165 - script: |
166 - Write-Host "##vso[task.setvariable variable=WSL_PACKAGE_VERSION;isOutput=true]$([string]('${{ parameters.packageVersion }}' + '.0'))"
167 - Write-Host "##vso[task.setvariable variable=WSL_NUGET_PACKAGE_VERSION;isOutput=true]$([string]('${{ parameters.packageVersion }}'))"
168 -
169 - - ${{ each platform in parameters.platforms }}:
170 - - task: CMake@1
171 - displayName: "CMake ${{ platform }}"
172 - inputs:
173 - workingDirectory: "."
174 - cmakeArgs: . --fresh -A ${{ platform }} -DCMAKE_BUILD_TYPE=Release -DCMAKE_SYSTEM_VERSION=10.0.26100.0 -DPACKAGE_VERSION=$(version.WSL_PACKAGE_VERSION) -DWSL_NUGET_PACKAGE_VERSION=$(version.WSL_NUGET_PACKAGE_VERSION) -DSKIP_PACKAGE_SIGNING=${{ parameters.isRelease }} -DOFFICIAL_BUILD=${{ parameters.isRelease }} -DPIPELINE_BUILD_ID=$(Build.BuildId) -DVSO_ORG=${{ parameters.vsoOrg }} -DVSO_PROJECT=${{ parameters.vsoProject }} -DWSL_BUILD_WSL_SETTINGS=true $(packageInputDirArg)\${{ platform }}
175 -
176 - # This additional Restore NuGet package task is added as a workaround for WSL Settings to have its packages restored properly.
177 - # Without this, building wsl settings may encounter the following error:
178 - #
179 - # The plugin credential provider could not acquire credentials. Authentication may require manual action.
180 - # Consider re-running the command with --interactive for `dotnet`, /p:NuGetInteractive="true" for MSBuild or removing the -NonInteractive switch for `NuGet`
181 - # Response status code does not indicate success: 401 (Unauthorized)
182 - - script: _deps\nuget.exe restore -NonInteractive
183 -
184 - - ${{ each target in parameters.targets }}:
185 - - script: cmake --build . --config Release --target ${{ target.target }} -- -m
186 - condition: and(succeeded(), eq('${{ parameters.isRelease }}', true))
187 - displayName: "Build ${{ target.target }} (${{ platform }})"
188 -
189 - - ${{ if eq(parameters.isRelease, 'true') }}:
190 - - task: SFP.build-tasks.custom-build-task-1.EsrpCodeSigning@5
191 - displayName: "Sign ${{ target.target }} (${{ platform }})"
192 - condition: and(succeeded(), eq('${{ parameters.isRelease }}', true))
193 - inputs:
194 - ConnectedServiceName: ${{ parameters.esrp.ConnectedServiceName}}
195 - signConfigType: ${{ parameters.esrp.signConfigType }}
196 - SessionTimeout: ${{ parameters.esrp.SessionTimeout }}
197 - MaxConcurrency: ${{ parameters.esrp.MaxConcurrency }}
198 - MaxRetryAttempts: ${{ parameters.esrp.MaxRetryAttempts }}
199 - ServiceEndpointUrl: ${{ parameters.esrp.ServiceEndpointUrl }}
200 - AuthAKVName: ${{ parameters.esrp.AuthAKVName }}
201 - AuthSignCertName: ${{ parameters.esrp.AuthSignCertName }}
202 - AppRegistrationClientId: ${{ parameters.esrp.AppRegistrationClientId }}
203 - AppRegistrationTenantId: ${{ parameters.esrp.AppRegistrationTenantId }}
204 - FolderPath: "bin\\${{ platform }}\\Release"
205 - Pattern: "${{ target.pattern }}"
206 - UseMSIAuthentication: true
207 - EsrpClientId: ${{ parameters.esrp.EsrpClientId }}
208 - inlineOperation: |
209 - [
210 - {
211 - "KeyCode": "CP-230012",
212 - "OperationCode": "SigntoolSign",
213 - "Parameters" : {
214 - "OpusName" : "Microsoft",
215 - "OpusInfo" : "http://www.microsoft.com",
216 - "FileDigest" : "/fd \"SHA256\"",
217 - "PageHash" : "/NPH",
218 - "TimeStamp" : "/tr \"http://rfc3161.gtm.corp.microsoft.com/TSS/HttpTspServer\" /td sha256"
219 - },
220 - "ToolName" : "sign",
221 - "ToolVersion" : "1.0"
222 - },
223 - {
224 - "KeyCode" : "CP-230012",
225 - "OperationCode" : "SigntoolVerify",
226 - "Parameters" : {},
227 - "ToolName" : "sign",
228 - "ToolVersion" : "1.0"
229 - }
230 - ]
231 -
232 - - task: PowerShell@2
233 - displayName: "Copy signed ${{ target.target }} to staging (${{ platform }})"
234 - condition: and(succeeded(), eq('${{ parameters.isRelease }}', true))
235 - inputs:
236 - targetType: inline
237 - script: |
238 - $arch = '${{ platform }}'
239 - $pattern = '${{ target.pattern }}'
240 - $inputDir = "bin\$arch\Release"
241 - $outputDir = "$(packageStagingDir)\$arch"
242 - New-Item -ItemType Directory -Path "$outputDir\wslsettings" -Force
243 - foreach ($file in $pattern.Split(',')) {
244 - $sourcePath = Join-Path $inputDir $file
245 - if (Test-Path $sourcePath) {
246 - $destPath = Join-Path $outputDir $file
247 - Write-Host "Copying signed file: $sourcePath -> $destPath"
248 - Copy-Item -Path $sourcePath -Destination $destPath -Force
249 - } else {
250 - Write-Warning "File not found: $sourcePath"
251 - }
252 - }
253 -
254 - - script: cmake --build . --config Release -- -m
255 - displayName: "Build installer msix and tests (${{ platform }})"
256 -
257 - - task: PowerShell@2
258 - displayName: "Move ${{ platform }} installer msi to output directory"
259 - inputs:
260 - targetType: inline
261 - script: |
262 - New-Item -ItemType Directory -Path "$(ob_outputDirectory)\bundle" -Force
263 - $arch = '${{ platform }}'
264 - Copy-Item -Path "bin\$arch\release\wsl.msi" -Destination "$(ob_outputDirectory)\bundle\wsl.$(version.WSL_PACKAGE_VERSION).$arch.msi"
265 -
266 - - ${{ if eq(parameters.isRelease, 'true') }}:
267 - - task: SFP.build-tasks.custom-build-task-1.EsrpCodeSigning@5
268 - displayName: "Sign the bundle"
269 - condition: and(succeeded(), eq('${{ parameters.isRelease }}', true))
270 - inputs:
271 - ConnectedServiceName: ${{ parameters.esrp.ConnectedServiceName}}
272 - signConfigType: ${{ parameters.esrp.signConfigType }}
273 - SessionTimeout: ${{ parameters.esrp.SessionTimeout }}
274 - MaxConcurrency: ${{ parameters.esrp.MaxConcurrency }}
275 - MaxRetryAttempts: ${{ parameters.esrp.MaxRetryAttempts }}
276 - ServiceEndpointUrl: ${{ parameters.esrp.ServiceEndpointUrl }}
277 - AuthAKVName: ${{ parameters.esrp.AuthAKVName }}
278 - AuthSignCertName: ${{ parameters.esrp.AuthSignCertName }}
279 - AppRegistrationClientId: ${{ parameters.esrp.AppRegistrationClientId }}
280 - AppRegistrationTenantId: ${{ parameters.esrp.AppRegistrationTenantId }}
281 - FolderPath: "bundle"
282 - Pattern: "*.msixbundle"
283 - UseMSIAuthentication: true
284 - EsrpClientId: ${{ parameters.esrp.EsrpClientId }}
285 - inlineOperation: |
286 - [
287 - {
288 - "KeyCode": "CP-230012",
289 - "OperationCode": "SigntoolSign",
290 - "Parameters" : {
291 - "OpusName" : "Microsoft",
292 - "OpusInfo" : "http://www.microsoft.com",
293 - "FileDigest" : "/fd \"SHA256\"",
294 - "PageHash" : "/NPH",
295 - "TimeStamp" : "/tr \"http://rfc3161.gtm.corp.microsoft.com/TSS/HttpTspServer\" /td sha256"
296 - },
297 - "ToolName" : "sign",
298 - "ToolVersion" : "1.0"
299 - },
300 - {
301 - "KeyCode" : "CP-230012",
302 - "OperationCode" : "SigntoolVerify",
303 - "Parameters" : {},
304 - "ToolName" : "sign",
305 - "ToolVersion" : "1.0"
306 - }
307 - ]
308 -
309 - - script: md.exe $(ob_outputDirectory)\bin\nuget
310 - displayName: "Create the nuget directory"
311 -
312 - - ${{ each package in parameters.nugetPackages }}:
313 - - script: nuget.exe pack ${{ package }} -OutputDirectory $(ob_outputDirectory)\bin\nuget -NonInteractive
314 - displayName: Build ${{ package }}
315 -
316 - - ${{ if eq(parameters.isRelease, 'true') }}:
317 - - task: SFP.build-tasks.custom-build-task-1.EsrpCodeSigning@5
318 - displayName: "Sign nuget packages"
319 - condition: and(succeeded(), eq('${{ parameters.isRelease }}', true))
320 - inputs:
321 - ConnectedServiceName: ${{ parameters.esrp.ConnectedServiceName}}
322 - signConfigType: ${{ parameters.esrp.signConfigType }}
323 - SessionTimeout: ${{ parameters.esrp.SessionTimeout }}
324 - MaxConcurrency: ${{ parameters.esrp.MaxConcurrency }}
325 - MaxRetryAttempts: ${{ parameters.esrp.MaxRetryAttempts }}
326 - ServiceEndpointUrl: ${{ parameters.esrp.ServiceEndpointUrl }}
327 - AuthAKVName: ${{ parameters.esrp.AuthAKVName }}
328 - AuthSignCertName: ${{ parameters.esrp.AuthSignCertName }}
329 - AppRegistrationClientId: ${{ parameters.esrp.AppRegistrationClientId }}
330 - AppRegistrationTenantId: ${{ parameters.esrp.AppRegistrationTenantId }}
331 - FolderPath: '$(ob_outputDirectory)\bin\nuget'
332 - Pattern: "*.nupkg"
333 - UseMSIAuthentication: true
334 - EsrpClientId: ${{ parameters.esrp.EsrpClientId }}
335 - inlineOperation: |
336 - [
337 - {
338 - "KeyCode": "CP-401405",
339 - "OperationCode": "NuGetSign",
340 - "Parameters" : {},
341 - "ToolName" : "sign",
342 - "ToolVersion" : "1.0"
343 - },
344 - {
345 - "KeyCode" : "CP-401405",
346 - "OperationCode" : "NuGetVerify",
347 - "Parameters" : {},
348 - "ToolName" : "sign",
349 - "ToolVersion" : "1.0"
350 - }
351 - ]
352 -
353 - - powershell: |
354 - foreach ($arch in @("x64", "ARM64"))
355 - {
356 - $binFolder = ".\bin\$arch\Release"
357 - $pdbFolder = Join-Path $(ob_outputDirectory) "pdb\$arch\Release"
358 - mkdir $pdbFolder
359 - foreach ($filter in @("*.pdb", "*.debug"))
360 - {
361 - foreach ($e in (Get-ChildItem -Recurse -Path $binFolder -Filter $filter)) {Move-Item -Path $e.fullname -Destination (Join-Path $pdbFolder $e.name)}
362 - }
363 - }
364 -
365 - displayName: Collect symbols
366 -
367 - - powershell: |
368 - mkdir appxsym
369 - foreach ($arch in @("x64", "ARM64"))
370 - {
371 - Get-ChildItem -Path $(ob_outputDirectory)\pdb\$arch\release\*.pdb -Exclude wsltests.pdb | Compress-Archive -DestinationPath appxsym/Microsoft.WSL_$(version.WSL_PACKAGE_VERSION)_$arch.zip
372 - Copy-Item -Path appxsym/Microsoft.WSL_$(version.WSL_PACKAGE_VERSION)_$arch.zip -Destination appxsym/Microsoft.WSL_$(version.WSL_PACKAGE_VERSION)_$arch.appxsym
373 - }
374 - mkdir $(ob_outputDirectory)/appxupload
375 - Get-ChildItem -Path appxsym/*.appxsym,bundle/release/Microsoft.WSL_$(version.WSL_PACKAGE_VERSION)_x64_ARM64.msixbundle | Compress-Archive -DestinationPath $(ob_outputDirectory)/appxupload/Microsoft.WSL_$(version.WSL_PACKAGE_VERSION)_x64_ARM64.zip
376 - Move-Item -Path $(ob_outputDirectory)/appxupload/Microsoft.WSL_$(version.WSL_PACKAGE_VERSION)_x64_ARM64.zip -Destination $(ob_outputDirectory)/appxupload/Microsoft.WSL_$(version.WSL_PACKAGE_VERSION)_x64_ARM64.appxupload
377 - rm appxsym/*.appxsym
378 -
379 - displayName: Create appxupload
380 - condition: and(succeeded(), eq('${{ parameters.isRelease }}', true))
381 -
382 - - powershell: |
383 - $taefVersion = (Select-Xml -Path packages.config -XPath '/packages/package[@id=''Microsoft.Taef'']/@version').Node.Value
384 - New-Item -ItemType Directory -Path "$(ob_outputDirectory)\bundle" -Force
385 - # TODO: Add "ARM64" to this list once arm64 testing is supported
386 - foreach ($arch in @("x64"))
387 - {
388 - mkdir $(ob_outputDirectory)\testbin\$arch\release
389 -
390 - Move-Item -Path "bin\$arch\release\wsltests.dll" -Destination "$(ob_outputDirectory)\testbin\$arch\release\wsltests.dll"
391 - Move-Item -Path "bin\$arch\release\testplugin.dll" -Destination "$(ob_outputDirectory)\testbin\$arch\release\testplugin.dll"
392 - Move-Item -Path "packages\Microsoft.Taef.$taefVersion\build\Binaries\$arch" -Destination "$(ob_outputDirectory)\testbin\$arch\release\taef"
393 - Move-Item -Path "bin\$arch\cloudtest" -Destination "$(ob_outputDirectory)\testbin\$arch\cloudtest"
394 -
395 - $TestDistroVersion = (Select-Xml -Path packages.config -XPath '/packages/package[@id=''Microsoft.WSL.TestDistro'']/@version').Node.Value
396 - Copy-Item "packages\Microsoft.WSL.TestDistro.$TestDistroVersion\$arch\test_distro.tar.xz" "$(ob_outputDirectory)\testbin\$arch"
397 - }
398 -
399 - Move-Item -Path "tools\test\test-setup.ps1" -Destination "$(ob_outputDirectory)\testbin\test-setup.ps1"
400 - Move-Item -Path "tools\test\CloudTest-Setup.bat" -Destination "$(ob_outputDirectory)\testbin\CloudTest-Setup.bat"
401 - Move-Item -Path "diagnostics\wsl.wprp" -Destination "$(ob_outputDirectory)\testbin\wsl.wprp"
402 - Move-Item -Path "test\linux\unit_tests" -Destination "$(ob_outputDirectory)\testbin\unit_tests"
403 -
404 - Move-Item -Path bundle\release\* -Destination $(ob_outputDirectory)\bundle
405 -
406 - displayName: Move artifacts to drop directory
407 -
408 - - task: PublishSymbols@2
409 - displayName: Publish symbols
410 - inputs:
411 - SymbolServerType: "TeamServices"
412 - TreatNotIndexedAsWarning: true
413 - SymbolsProduct: WSL
414 - SymbolsVersion: $(version.WSL_PACKAGE_VERSION)
415 - SearchPattern: |
416 - $(ob_outputDirectory)/pdb/**/*.pdb
417 - $(ob_outputDirectory)/bin/**/*.exe
418 - $(ob_outputDirectory)/bin/**/*.dll
419 -
420 - - ${{ if ne(parameters.pool, '') }}:
421 - - task: PublishPipelineArtifact@1
422 - inputs:
423 - targetPath: $(ob_outputDirectory)
424 - artifactName: $(ob_artifactBaseName)$(ob_artifactSuffix)
425 -
426 - - task: CodeQL3000Finalize@0
427 - condition: ${{ and(parameters.isNightly, eq(variables['Build.SourceBranch'], 'refs/heads/main'))}}
428 -
135 + vsoOrg: ${{ parameters.vsoOrg }}
136 + vsoProject: ${{ parameters.vsoProject }}
137 + esrp: ${{ parameters.esrp }}
138 + includeCodeQL: true
139 +
140 + # ── package stage (release and nightly only) ─────────────────────────
141 + # Creates msixbundle, appxupload, and NuGet packages from both platform artifacts.
142 + - ${{ if parameters.includePackageStage }}:
143 + - template: package-stage.yml
144 + parameters:
145 + isRelease: ${{ parameters.isRelease }}
146 + pool: ${{ parameters.pool }}
147 + esrp: ${{ parameters.esrp }}
148 + nugetPackages: ${{ parameters.nugetPackages }}
.pipelines/flight-stage.yml
+4 -5
@@ -14,9 +14,9 @@ parameters:
14 stages:
15 - stage: flight
16 ${{ if eq(parameters.bypassTests, true) }}:
17 - dependsOn: [build]
17 + dependsOn: [package]
18 ${{ else }}:
19 - dependsOn: [test]
19 + dependsOn: [test, package]
20
21 jobs:
22 - job: flight
@@ -30,7 +30,6 @@ stages:
30 FlightId: $(StoreBrokerFlightId)
31 StoreBrokerPath: $(Build.SourcesDirectory)\storebroker # location of StoreBroker information in the repo
32 StoreBrokerPayloadPath: $(Build.ArtifactStagingDirectory)\StoreBrokerPayload # location of package created for flight
33 - WSL_PACKAGE_VERSION: $[ dependencies.build.outputs['version.WSL_PACKAGE_VERSION'] ]
33 ob_outputDirectory: '$(Build.SourcesDirectory)\out'
34 ob_artifactBaseName: 'drop_wsl'
35 ob_artifactSuffix: '_flight'
@@ -55,9 +54,9 @@ stages:
54
55 # Download the build drop
56 - task: DownloadPipelineArtifact@2
58 - displayName: Download Bundle artifact
57 + displayName: Download package artifact
58 inputs:
60 - artifact: "drop_wsl_build"
59 + artifact: "drop_wsl_package"
60 path: drop
61
62 # copy the appxupload folder to the storebroker folder
.pipelines/nuget-stage.yml
+4 -4
@@ -10,7 +10,7 @@ parameters:
10
11 stages:
12 - stage: nuget
13 - dependsOn: [build, test]
13 + dependsOn: [package, test]
14 jobs:
15 - job: nuget
16 displayName: 'Publish nuget packages'
@@ -21,7 +21,7 @@ stages:
21 type: windows
22
23 variables:
24 - WSL_NUGET_PACKAGE_VERSION: $[ dependencies.build.outputs['version.WSL_NUGET_PACKAGE_VERSION'] ]
24 + WSL_NUGET_PACKAGE_VERSION: $[ stageDependencies.package.package.outputs['version.WSL_NUGET_PACKAGE_VERSION'] ]
25 NUGET_PLUGIN_HANDSHAKE_TIMEOUT_IN_SECONDS: 60
26 NUGET_PLUGIN_REQUEST_TIMEOUT_IN_SECONDS: 60
27 ob_outputDirectory: '$(Build.SourcesDirectory)\out'
@@ -32,8 +32,8 @@ stages:
32
33 - task: DownloadPipelineArtifact@2
34 displayName: Download nuget artifacts
35 - inputs:
36 - artifact: "drop_wsl_build"
35 + inputs:
36 + artifact: "drop_wsl_package"
37 path: drop
38
39 # Note: this task might fail if there's been no commits between two nightly pipelines, which is fine.
.pipelines/package-stage.yml new
+239
@@ -0,0 +1,239 @@
1 +parameters:
2 + - name: isRelease
3 + type: boolean
4 + default: true
5 +
6 + - name: nugetPackages
7 + type: object
8 + default:
9 + - Microsoft.WSL.PluginApi.nuspec
10 +
11 + - name: pool
12 + type: string
13 + default: ''
14 +
15 + - name: esrp
16 + type: object
17 + default:
18 + ConnectedServiceName: "AzureConnection-AME"
19 + signConfigType: "inlineSignParams"
20 + SessionTimeout: 60
21 + MaxConcurrency: 50
22 + MaxRetryAttempts: 5
23 + ServiceEndpointUrl: $(EsrpServiceEndpointUrl)
24 + AuthAKVName: $(EsrpAuthAKVName)
25 + AuthSignCertName: $(EsrpAuthSignCertName)
26 + AppRegistrationClientId: $(EsrpAppRegistrationClientId)
27 + AppRegistrationTenantId: $(EsrpAppRegistrationTenantId)
28 + EsrpClientId: $(EsrpClientId)
29 +
30 +stages:
31 + - stage: package
32 + dependsOn: [build_x64, build_arm64]
33 + jobs:
34 + - job: package
35 + displayName: "Create msixbundle & appxupload"
36 + timeoutInMinutes: 60
37 +
38 + ${{ if eq(parameters.pool, '') }}:
39 + pool: {'type': 'windows'}
40 +
41 + ${{ else }}:
42 + pool: ${{ parameters.pool }}
43 +
44 + variables:
45 + ob_outputDirectory: '$(Build.SourcesDirectory)\out'
46 + ob_artifactBaseName: 'drop_wsl'
47 + ob_artifactSuffix: '_package'
48 + buildStagePackageVersion: $[ stageDependencies.build_x64.build_x64.outputs['version.WSL_PACKAGE_VERSION'] ]
49 + buildStageNugetVersion: $[ stageDependencies.build_x64.build_x64.outputs['version.WSL_NUGET_PACKAGE_VERSION'] ]
50 +
51 + steps:
52 + - task: PowerShell@2
53 + displayName: "Set package version from build stage"
54 + name: version
55 + inputs:
56 + targetType: inline
57 + script: |
58 + Write-Host "##vso[task.setvariable variable=WSL_PACKAGE_VERSION;isOutput=true]$(buildStagePackageVersion)"
59 + Write-Host "##vso[task.setvariable variable=WSL_NUGET_PACKAGE_VERSION;isOutput=true]$(buildStageNugetVersion)"
60 +
61 + - task: DownloadPipelineArtifact@2
62 + displayName: Download x64 build artifact
63 + inputs:
64 + artifact: "drop_wsl_build"
65 + path: $(Pipeline.Workspace)\drop_x64
66 +
67 + - task: DownloadPipelineArtifact@2
68 + displayName: Download arm64 build artifact
69 + inputs:
70 + artifact: "drop_wsl_build_arm64"
71 + path: $(Pipeline.Workspace)\drop_arm64
72 +
73 + - powershell: |
74 + $version = "$(version.WSL_PACKAGE_VERSION)"
75 +
76 + # Place per-platform msix files where cmake's bundle target expects them
77 + foreach ($arch in @(
78 + @{platform="x64"; dir="X64"},
79 + @{platform="arm64"; dir="arm64"}
80 + )) {
81 + $dest = "bin\$($arch.dir)\Release"
82 + New-Item -ItemType Directory -Path $dest -Force
83 + Copy-Item "$(Pipeline.Workspace)\drop_$($arch.platform)\installer\installer.$($arch.platform).msix" "$dest\installer.msix"
84 + }
85 +
86 + # Copy MSIs to the output bundle directory
87 + $bundleDir = "$(ob_outputDirectory)\bundle"
88 + New-Item -ItemType Directory -Path $bundleDir -Force
89 + Copy-Item "$(Pipeline.Workspace)\drop_x64\bundle\wsl.${version}.x64.msi" "$bundleDir\"
90 + Copy-Item "$(Pipeline.Workspace)\drop_arm64\bundle\wsl.${version}.arm64.msi" "$bundleDir\"
91 + displayName: Stage build artifacts
92 +
93 + - task: CMake@1
94 + displayName: "CMake configure (bundle-only)"
95 + inputs:
96 + workingDirectory: "."
97 + cmakeArgs: . -DBUNDLE_ONLY=TRUE -DCMAKE_BUILD_TYPE=Release -DPACKAGE_VERSION=$(version.WSL_PACKAGE_VERSION) -DWSL_NUGET_PACKAGE_VERSION=$(version.WSL_NUGET_PACKAGE_VERSION)
98 +
99 + - script: cmake --build . --config Release --target bundle -- -m
100 + displayName: Create msixbundle
101 +
102 + - powershell: |
103 + Copy-Item "bundle\Release\*.msixbundle" "$(ob_outputDirectory)\bundle\"
104 + displayName: Copy bundle to output
105 +
106 + - ${{ if eq(parameters.isRelease, true) }}:
107 + - task: SFP.build-tasks.custom-build-task-1.EsrpCodeSigning@5
108 + displayName: "Sign the bundle"
109 + inputs:
110 + ConnectedServiceName: ${{ parameters.esrp.ConnectedServiceName}}
111 + signConfigType: ${{ parameters.esrp.signConfigType }}
112 + SessionTimeout: ${{ parameters.esrp.SessionTimeout }}
113 + MaxConcurrency: ${{ parameters.esrp.MaxConcurrency }}
114 + MaxRetryAttempts: ${{ parameters.esrp.MaxRetryAttempts }}
115 + ServiceEndpointUrl: ${{ parameters.esrp.ServiceEndpointUrl }}
116 + AuthAKVName: ${{ parameters.esrp.AuthAKVName }}
117 + AuthSignCertName: ${{ parameters.esrp.AuthSignCertName }}
118 + AppRegistrationClientId: ${{ parameters.esrp.AppRegistrationClientId }}
119 + AppRegistrationTenantId: ${{ parameters.esrp.AppRegistrationTenantId }}
120 + FolderPath: "$(ob_outputDirectory)\\bundle"
121 + Pattern: "*.msixbundle"
122 + UseMSIAuthentication: true
123 + EsrpClientId: ${{ parameters.esrp.EsrpClientId }}
124 + inlineOperation: |
125 + [
126 + {
127 + "KeyCode": "CP-230012",
128 + "OperationCode": "SigntoolSign",
129 + "Parameters" : {
130 + "OpusName" : "Microsoft",
131 + "OpusInfo" : "http://www.microsoft.com",
132 + "FileDigest" : "/fd \"SHA256\"",
133 + "PageHash" : "/NPH",
134 + "TimeStamp" : "/tr \"http://rfc3161.gtm.corp.microsoft.com/TSS/HttpTspServer\" /td sha256"
135 + },
136 + "ToolName" : "sign",
137 + "ToolVersion" : "1.0"
138 + },
139 + {
140 + "KeyCode" : "CP-230012",
141 + "OperationCode" : "SigntoolVerify",
142 + "Parameters" : {},
143 + "ToolName" : "sign",
144 + "ToolVersion" : "1.0"
145 + }
146 + ]
147 +
148 + - ${{ if not(eq(parameters.isRelease, true)) }}:
149 + - powershell: |
150 + # Sign the bundle with the dev cert from the x64 build
151 + $cert = "$(Pipeline.Workspace)\drop_x64\installer\dev-cert.pfx"
152 + if (-not (Test-Path $cert)) { throw "Dev cert not found at $cert" }
153 + $bundle = Get-ChildItem "$(ob_outputDirectory)\bundle\*.msixbundle" | Select-Object -First 1
154 + $signtool = Get-ChildItem "${env:ProgramFiles(x86)}\Windows Kits\10\bin\*\x64\signtool.exe" -ErrorAction Stop | Sort-Object FullName -Descending | Select-Object -First 1 -ExpandProperty FullName
155 + & $signtool sign /a /v /fd SHA256 /f $cert $bundle.FullName
156 + displayName: Sign bundle with dev cert
157 +
158 + - powershell: |
159 + $version = "$(version.WSL_PACKAGE_VERSION)"
160 +
161 + # Collect PDBs from both platforms
162 + foreach ($source in @(
163 + @{arch="x64"; drop="$(Pipeline.Workspace)\drop_x64\pdb\x64\Release"},
164 + @{arch="ARM64"; drop="$(Pipeline.Workspace)\drop_arm64\pdb\arm64\Release"}
165 + ))
166 + {
167 + $pdbFolder = Join-Path $(ob_outputDirectory) "pdb\$($source.arch)\Release"
168 + New-Item -ItemType Directory -Path $pdbFolder -Force
169 + if (Test-Path $source.drop) {
170 + Copy-Item -Path "$($source.drop)\*" -Destination $pdbFolder -Recurse
171 + }
172 + }
173 + displayName: Collect symbols from both platforms
174 +
175 + - powershell: |
176 + $version = "$(version.WSL_PACKAGE_VERSION)"
177 +
178 + mkdir appxsym
179 + foreach ($arch in @("x64", "ARM64"))
180 + {
181 + Get-ChildItem -Path $(ob_outputDirectory)\pdb\$arch\release\*.pdb -Exclude wsltests.pdb | Compress-Archive -DestinationPath appxsym/Microsoft.WSL_${version}_$arch.zip
182 + Copy-Item -Path appxsym/Microsoft.WSL_${version}_$arch.zip -Destination appxsym/Microsoft.WSL_${version}_$arch.appxsym
183 + }
184 + mkdir $(ob_outputDirectory)/appxupload
185 + Get-ChildItem -Path appxsym/*.appxsym,$(ob_outputDirectory)/bundle/Microsoft.WSL_${version}_x64_ARM64.msixbundle | Compress-Archive -DestinationPath $(ob_outputDirectory)/appxupload/Microsoft.WSL_${version}_x64_ARM64.zip
186 + Move-Item -Path $(ob_outputDirectory)/appxupload/Microsoft.WSL_${version}_x64_ARM64.zip -Destination $(ob_outputDirectory)/appxupload/Microsoft.WSL_${version}_x64_ARM64.appxupload
187 + rm appxsym/*.appxsym
188 +
189 + displayName: Create appxupload
190 + condition: and(succeeded(), eq('${{ parameters.isRelease }}', true))
191 +
192 + - script: md.exe $(ob_outputDirectory)\nuget
193 + displayName: "Create the nuget directory"
194 +
195 + - ${{ each package in parameters.nugetPackages }}:
196 + - script: nuget.exe pack ${{ package }} -OutputDirectory $(ob_outputDirectory)\nuget -NonInteractive
197 + displayName: Build ${{ package }}
198 +
199 + - ${{ if eq(parameters.isRelease, true) }}:
200 + - task: SFP.build-tasks.custom-build-task-1.EsrpCodeSigning@5
201 + displayName: "Sign nuget packages"
202 + inputs:
203 + ConnectedServiceName: ${{ parameters.esrp.ConnectedServiceName}}
204 + signConfigType: ${{ parameters.esrp.signConfigType }}
205 + SessionTimeout: ${{ parameters.esrp.SessionTimeout }}
206 + MaxConcurrency: ${{ parameters.esrp.MaxConcurrency }}
207 + MaxRetryAttempts: ${{ parameters.esrp.MaxRetryAttempts }}
208 + ServiceEndpointUrl: ${{ parameters.esrp.ServiceEndpointUrl }}
209 + AuthAKVName: ${{ parameters.esrp.AuthAKVName }}
210 + AuthSignCertName: ${{ parameters.esrp.AuthSignCertName }}
211 + AppRegistrationClientId: ${{ parameters.esrp.AppRegistrationClientId }}
212 + AppRegistrationTenantId: ${{ parameters.esrp.AppRegistrationTenantId }}
213 + FolderPath: '$(ob_outputDirectory)\nuget'
214 + Pattern: "*.nupkg"
215 + UseMSIAuthentication: true
216 + EsrpClientId: ${{ parameters.esrp.EsrpClientId }}
217 + inlineOperation: |
218 + [
219 + {
220 + "KeyCode": "CP-401405",
221 + "OperationCode": "NuGetSign",
222 + "Parameters" : {},
223 + "ToolName" : "sign",
224 + "ToolVersion" : "1.0"
225 + },
226 + {
227 + "KeyCode" : "CP-401405",
228 + "OperationCode" : "NuGetVerify",
229 + "Parameters" : {},
230 + "ToolName" : "sign",
231 + "ToolVersion" : "1.0"
232 + }
233 + ]
234 +
235 + - ${{ if ne(parameters.pool, '') }}:
236 + - task: PublishPipelineArtifact@1
237 + inputs:
238 + targetPath: $(ob_outputDirectory)
239 + artifactName: $(ob_artifactBaseName)$(ob_artifactSuffix)
.pipelines/test-stage.yml
+8 -1
@@ -3,6 +3,10 @@ parameters:
3 type: boolean
4 default: false
5
6 + - name: includePackageStage
7 + type: boolean
8 + default: true
9 +
10 - name: pool
11 type: string
12 default: ''
@@ -29,7 +33,10 @@ parameters:
33
34 stages:
35 - stage: test
32 - dependsOn: [build]
36 + ${{ if parameters.includePackageStage }}:
37 + dependsOn: [build_x64, package]
38 + ${{ else }}:
39 + dependsOn: [build_x64]
40 jobs:
41 - ${{ each version in parameters.versions }}:
42 - ${{ each image in parameters.test_images }}:
.pipelines/wsl-build-pr-onebranch.yml
+2
@@ -46,7 +46,9 @@ extends:
46 - template: build-stage.yml@self
47 parameters:
48 isRelease: false
49 + includePackageStage: false
50
51 - template: test-stage.yml@self
52 parameters:
53 + includePackageStage: false
54 rs_prerelease_only: true
.pipelines/wsl-build-pr.yml
+2
@@ -8,11 +8,13 @@ stages:
8 - template: build-stage.yml@self
9 parameters:
10 isRelease: false
11 + includePackageStage: false
12 pool: 'wsl-build'
13 vsoOrg: shine-oss
14 vsoProject: wsl
15
16 - template: test-stage.yml@self
17 parameters:
18 + includePackageStage: false
19 rs_prerelease_only: true
20 pool: server
CMakeLists.txt
+34 -13
@@ -27,6 +27,36 @@ if (NOT ${CMAKE_VS_WINDOWS_TARGET_PLATFORM_VERSION} STREQUAL ${CMAKE_SYSTEM_VERS
27 message(FATAL_ERROR "Incorrect Windows SDK version: ${CMAKE_VS_WINDOWS_TARGET_PLATFORM_VERSION}, requires ${CMAKE_SYSTEM_VERSION}")
28 endif()
29
30 +# Default to debug build if unspecified (pipeline always passes -DCMAKE_BUILD_TYPE=Release)
31 +if(NOT CMAKE_BUILD_TYPE)
32 + set(CMAKE_BUILD_TYPE "Debug")
33 +endif()
34 +
35 +# Only generate the build configuration that CMake is configured for
36 +set(CMAKE_CONFIGURATION_TYPES "${CMAKE_BUILD_TYPE}" CACHE STRING "" FORCE)
37 +
38 +# Configure output directories
39 +set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin/${TARGET_PLATFORM})
40 +
41 +# BUNDLE_ONLY mode: skip all source builds; only configure the msixbundle target and nuspec files.
42 +# Used by the package pipeline stage to create the bundle from pre-built per-platform msix files.
43 +option(BUNDLE_ONLY "Only configure the msixbundle target, skip all source builds" OFF)
44 +if (BUNDLE_ONLY)
45 + if (NOT PACKAGE_VERSION)
46 + message(FATAL_ERROR "PACKAGE_VERSION is required for BUNDLE_ONLY mode")
47 + endif()
48 +
49 + if (NOT PACKAGE_VERSION MATCHES "^([0-9]+)\\.([0-9]+)\\.([0-9]+)\\.([0-9]+)$")
50 + message(FATAL_ERROR "PACKAGE_VERSION is invalid: '${PACKAGE_VERSION}'")
51 + endif()
52 +
53 + set(PACKAGE_SIGN_COMMAND echo Skipped package signing for:)
54 +
55 + add_subdirectory(nuget)
56 + add_subdirectory(msixinstaller)
57 + return()
58 +endif()
59 +
60 include(FetchContent)
61
62 # Import GSL and nlohmannjson
@@ -103,11 +133,6 @@ find_nuget_package(WinUIEx WINUIEX /)
133
134 set(WSLG_TS_PLUGIN_DLL "WSLDVCPlugin.dll")
135
106 -# Default to debug build if unspecified
107 -if(NOT CMAKE_BUILD_TYPE)
108 - set(CMAKE_BUILD_TYPE "Debug")
109 -endif()
110 -
136 set(SUPPORTED_LANGS cs-CZ;da-DK;de-DE;en-GB;en-US;es-ES;fi-FI;fr-FR;hu-HU;it-IT;ja-JP;ko-KR;nb-NO;nl-NL;pl-PL;pt-BR;pt-PT;ru-RU;sv-SE;tr-TR;zh-CN;zh-TW)
137
138 if (EXISTS "${CMAKE_CURRENT_LIST_DIR}/UserConfig.cmake")
@@ -120,17 +145,14 @@ if (NOT DEFINED WSL_BUILD_WSL_SETTINGS)
145 set(WSL_BUILD_WSL_SETTINGS false)
146 endif ()
147
123 -# Only generate the build configuration that CMake is configured for
124 -set(CMAKE_CONFIGURATION_TYPES "${CMAKE_BUILD_TYPE}" CACHE STRING "" FORCE)
125 -
148 find_commit_hash(COMMIT_HASH)
149
150 if (NOT PACKAGE_VERSION)
151 find_version(PACKAGE_VERSION WSL_NUGET_PACKAGE_VERSION) # Fetch the package version from git if not specified
152 endif ()
153
132 -if (NOT PACKAGE_VERSION MATCHES "^([0-9]+).([0-9]+).([0-9]+).([0-9]+)$")
133 - message(FATAL_ERROR "PACKAGE_VERSION is invalid: '${PACKAGE_VERSION}'. Needs to match '([0-9]+).([0-9]+).([0-9]+).([0-9]+)'")
154 +if (NOT PACKAGE_VERSION MATCHES "^([0-9]+)\\.([0-9]+)\\.([0-9]+)\\.([0-9]+)$")
155 + message(FATAL_ERROR "PACKAGE_VERSION must be four dot-separated numeric components (e.g., '1.2.3.4'): got '${PACKAGE_VERSION}'")
156 endif()
157
158 set(PACKAGE_VERSION_MAJOR ${CMAKE_MATCH_1})
@@ -138,12 +160,11 @@ set(PACKAGE_VERSION_MINOR ${CMAKE_MATCH_2})
160 set(PACKAGE_VERSION_REVISION ${CMAKE_MATCH_3})
161
162 # The store requires the revision number to be 0, so enforce this on official builds
141 -if (OFFICIAL_BUILD AND NOT PACKAGE_VERSION MATCHES "^([0-9]+).([0-9]+).([0-9]+).0$")
163 +if (OFFICIAL_BUILD AND NOT PACKAGE_VERSION MATCHES "^([0-9]+)\\.([0-9]+)\\.([0-9]+)\\.0$")
164 message(FATAL_ERROR "PACKAGE_VERSION is invalid: '${PACKAGE_VERSION}'. Needs to match '([0-9]+).([0-9]+).([0-9]+).0' for official builds")
165 endif()
166
145 -# Configure output directories
146 -set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin/${TARGET_PLATFORM})
167 +# Configure per-config output directories
168 set(CMAKE_RUNTIME_OUTPUT_DIRECTORY_DEBUG ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/Debug)
169 set(CMAKE_RUNTIME_OUTPUT_DIRECTORY_RELEASE ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/Release)
170
cloudtest/CMakeLists.txt
+12
@@ -24,6 +24,18 @@ else()
24 set(ALLOW_UNSIGNED_PACKAGE "1")
25 endif()
26
27 +# When the package stage is included (release + nightly), test the real bundle from that stage.
28 +# PR builds test the installer.msix directly from the build artifact (no bundle needed).
29 +if (INCLUDE_PACKAGE_STAGE)
30 + set(TEST_PACKAGE_PROVIDER "[package]")
31 + set(TEST_PACKAGE_PATH "bundle\\Microsoft.WSL_${PACKAGE_VERSION}_x64_ARM64.msixbundle")
32 + set(TEST_PACKAGE_FILE "Microsoft.WSL_${PACKAGE_VERSION}_x64_ARM64.msixbundle")
33 +else()
34 + set(TEST_PACKAGE_PROVIDER "[drop]")
35 + set(TEST_PACKAGE_PATH "testbin\\x64\\release\\installer.msix")
36 + set(TEST_PACKAGE_FILE "installer.msix")
37 +endif()
38 +
39 function(add_test_group image version)
40 set(DIR ${OUT}/${image}-wsl${version})
41
cloudtest/TestGroup.xml.in
+2 -2
@@ -5,7 +5,7 @@
5 </ResourceSpec>
6 <Setup TimeoutMins="30">
7 <BuildFiles>
8 - <Copy Src="[drop]\bundle\Microsoft.WSL_${PACKAGE_VERSION}_x64_ARM64.msixbundle" Dest="[WorkingDirectory]" IsRecursive="false"/>
8 + <Copy Src="${TEST_PACKAGE_PROVIDER}\${TEST_PACKAGE_PATH}" Dest="[WorkingDirectory]" IsRecursive="false"/>
9 <Copy Src="[drop]\testbin\${TARGET_PLATFORM}\release\*" Dest="[WorkingDirectory]\" IsRecursive="true" Writable="true"/>
10 <Copy Src="[drop]\testbin\${TARGET_PLATFORM}\test_distro.tar.xz" Dest="[WorkingDirectory]" IsRecursive="false" Writable="true"/>
11 <Copy Src="[drop]\testbin\test-setup.ps1" Dest="[WorkingDirectory]\" IsRecursive="false" />
@@ -21,6 +21,6 @@
21 </Setup>
22
23 <TestJob Name="CloudTest.Taef" TimeoutMins="240">
24 - <Execution Type="TAEF" Path="[WorkingDirectory]\wsltests.dll" Args="/p:bugReportDirectory=[LoggingDirectory]\BugReportOutput /errorOnCrash /testmode:etwlogger /EtwLogger:WPRProfileFile=[WorkingDirectory]\wsl.wprp /EtwLogger:WPRProfile=WSL /EtwLogger:SavePoint=ExecutionComplete /EtwLogger:RecordingScope=Execution /p:SetupScript=.\test-setup.ps1 /p:Package=[WorkingDirectory]\Microsoft.WSL_${PACKAGE_VERSION}_x64_ARM64.msixbundle /p:Version=${version} /p:AllowUnsigned=${ALLOW_UNSIGNED_PACKAGE} /p:UnitTestsPath=[WorkingDirectory]\unit_tests /p:DistroPath=[WorkingDirectory]\test_distro.tar.xz /p:DistroName=test_distro /logOutput:High /p:RedirectStdout=[LoggingDirectory]\stdout.txt /p:RedirectStderr=[LoggingDirectory]\stderr.txt /p:KernelLogs=[LoggingDirectory]\dmesg.txt /p:DumpFolder=[LoggingDirectory] /p:WerReport /p:LogDmesg /p:PipelineBuildId=${PIPELINE_BUILD_ID} /p:DumpTool=DumpTool.exe" />
24 + <Execution Type="TAEF" Path="[WorkingDirectory]\wsltests.dll" Args="/p:bugReportDirectory=[LoggingDirectory]\BugReportOutput /errorOnCrash /testmode:etwlogger /EtwLogger:WPRProfileFile=[WorkingDirectory]\wsl.wprp /EtwLogger:WPRProfile=WSL /EtwLogger:SavePoint=ExecutionComplete /EtwLogger:RecordingScope=Execution /p:SetupScript=.\test-setup.ps1 /p:Package=[WorkingDirectory]\${TEST_PACKAGE_FILE} /p:Version=${version} /p:AllowUnsigned=${ALLOW_UNSIGNED_PACKAGE} /p:UnitTestsPath=[WorkingDirectory]\unit_tests /p:DistroPath=[WorkingDirectory]\test_distro.tar.xz /p:DistroName=test_distro /logOutput:High /p:RedirectStdout=[LoggingDirectory]\stdout.txt /p:RedirectStderr=[LoggingDirectory]\stderr.txt /p:KernelLogs=[LoggingDirectory]\dmesg.txt /p:DumpFolder=[LoggingDirectory] /p:WerReport /p:LogDmesg /p:PipelineBuildId=${PIPELINE_BUILD_ID} /p:DumpTool=DumpTool.exe" />
25 </TestJob>
26 </TestJobGroup>
cloudtest/TestMap.xml.in
+7
@@ -9,6 +9,13 @@
9 <Add Name="PipelineArtifactName" Value="drop_wsl_build"/>
10 </Properties>
11 </Provider>
12 + <Provider Type="PipelineArtifacts">
13 + <Properties>
14 + <Add Name="CloudTest.ProviderCustomName" Value="[package]" />
15 + <Add Name="PipelineArtifactBuildUrl" Value="https://${VSO_ORG}.visualstudio.com/${VSO_PROJECT}/_build/results?buildId=${PIPELINE_BUILD_ID}"/>
16 + <Add Name="PipelineArtifactName" Value="drop_wsl_package"/>
17 + </Properties>
18 + </Provider>
19 <Provider Type="VSODrop">
20 <Properties>
21 <Add Name="CloudTest.ProviderCustomName" Value="[test_distro]" />
msixinstaller/CMakeLists.txt
+16 -12
@@ -1,16 +1,18 @@
1 -set(DEPENDENCIES "wsl.exe;wslinstaller.exe;wslinstallerproxystub.dll")
2 -
3 -# Don't include the MSI if building a thin package.
4 -if (NOT "${WSL_BUILD_THIN_PACKAGE}")
5 - list(APPEND DEPENDENCIES "wsl.msi")
1 +if (NOT BUNDLE_ONLY)
2 + set(DEPENDENCIES "wsl.exe;wslinstaller.exe;wslinstallerproxystub.dll")
3 +
4 + # Don't include the MSI if building a thin package.
5 + if (NOT "${WSL_BUILD_THIN_PACKAGE}")
6 + list(APPEND DEPENDENCIES "wsl.msi")
7 + endif()
8 +
9 + add_appx_target(msixinstallerpackage
10 + "${DEPENDENCIES}"
11 + "${CMAKE_CURRENT_LIST_DIR}/AppxManifest.in"
12 + "${BIN}/installer.msix"
13 + "wsl;wslinstaller;msipackage;wslinstallerproxystub")
14 endif()
15
8 -add_appx_target(msixinstallerpackage
9 - "${DEPENDENCIES}"
10 - "${CMAKE_CURRENT_LIST_DIR}/AppxManifest.in"
11 - "${BIN}/installer.msix"
12 - "wsl;wslinstaller;msipackage;wslinstallerproxystub")
13 -
16 set(ARM64_BUNDLE_PATH ${CMAKE_SOURCE_DIR}/bin/arm64/${CMAKE_BUILD_TYPE}/installer.msix)
17 set(X64_BUNDLE_PATH ${CMAKE_SOURCE_DIR}/bin/X64/${CMAKE_BUILD_TYPE}/installer.msix)
18
@@ -38,6 +40,8 @@ if ((TARGET_PLATFORM STREQUAL "arm64" AND EXISTS ${X64_BUNDLE_PATH}) OR (TARGET_
40
41 set_source_files_properties(${OUTPUT_BUNDLE} PROPERTIES GENERATED TRUE)
42 add_custom_target(bundle DEPENDS ${OUTPUT_BUNDLE})
41 - add_dependencies(bundle msixinstallerpackage)
43 + if (NOT BUNDLE_ONLY)
44 + add_dependencies(bundle msixinstallerpackage)
45 + endif()
46 set_target_properties(bundle PROPERTIES EXCLUDE_FROM_ALL FALSE)
47 endif()
\ No newline at end of file