Skip to content

Commit abfbf42

Browse files
authored
Create OneBranch build and release pipeline (#1605)
1 parent ee88277 commit abfbf42

File tree

4 files changed

+351
-2
lines changed

4 files changed

+351
-2
lines changed

.config/tsaoptions.json

+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
{
2+
"instanceUrl": "https://msazure.visualstudio.com",
3+
"projectName": "One",
4+
"areaPath": "One\\MGMT\\Compute\\Powershell\\Powershell\\PowerShell Core",
5+
"notificationAliases": [
6+
7+
8+
9+
]
10+
}

.pipelines/PSResourceGet-Official.yml

+334
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,334 @@
1+
#################################################################################
2+
# OneBranch Pipelines #
3+
# This pipeline was created by EasyStart from a sample located at: #
4+
# https://aka.ms/obpipelines/easystart/samples #
5+
# Documentation: https://aka.ms/obpipelines #
6+
# Yaml Schema: https://aka.ms/obpipelines/yaml/schema #
7+
# Retail Tasks: https://aka.ms/obpipelines/tasks #
8+
# Support: https://aka.ms/onebranchsup #
9+
#################################################################################
10+
name: PSResourceGet-Release-$(Build.BuildId)
11+
trigger: none # https://aka.ms/obpipelines/triggers
12+
pr:
13+
branches:
14+
include:
15+
- main
16+
- release*
17+
parameters: # parameters are shown up in ADO UI in a build queue time
18+
- name: 'debug'
19+
displayName: 'Enable debug output'
20+
type: boolean
21+
default: false
22+
23+
variables:
24+
- name: DOTNET_CLI_TELEMETRY_OPTOUT
25+
value: 1
26+
- name: POWERSHELL_TELEMETRY_OPTOUT
27+
value: 1
28+
- name: WindowsContainerImage
29+
value: onebranch.azurecr.io/windows/ltsc2022/vse2022:latest # Docker image which is used to build the project https://aka.ms/obpipelines/containers
30+
31+
resources:
32+
repositories:
33+
- repository: onebranchTemplates
34+
type: git
35+
name: OneBranch.Pipelines/GovernedTemplates
36+
ref: refs/heads/main
37+
38+
extends:
39+
template: v2/OneBranch.Official.CrossPlat.yml@onebranchTemplates # https://aka.ms/obpipelines/templates
40+
parameters:
41+
featureFlags:
42+
WindowsHostVersion: '1ESWindows2022'
43+
customTags: 'ES365AIMigrationTooling'
44+
globalSdl:
45+
disableLegacyManifest: true
46+
sbom:
47+
enabled: true
48+
packageName: Microsoft.PowerShell.PSResourceGet
49+
codeql:
50+
compiled:
51+
enabled: true
52+
asyncSdl: # https://aka.ms/obpipelines/asyncsdl
53+
enabled: true
54+
forStages: [stagebuild]
55+
credscan:
56+
enabled: true
57+
scanFolder: $(Build.SourcesDirectory)\PSResourceGet
58+
binskim:
59+
enabled: true
60+
apiscan:
61+
enabled: false
62+
63+
stages:
64+
- stage: stagebuild
65+
displayName: Build and Package Microsoft.PowerShell.PSResourceGet
66+
jobs:
67+
- job: jobbuild
68+
displayName: Build Microsoft.PowerShell.PSResourceGet Files
69+
variables: # More settings at https://aka.ms/obpipelines/yaml/jobs
70+
- name: ob_outputDirectory
71+
value: '$(Build.ArtifactStagingDirectory)/ONEBRANCH_ARTIFACT'
72+
- name: repoRoot
73+
value: $(Build.SourcesDirectory)\PSResourceGet
74+
- name: ob_sdl_tsa_configFile
75+
value: $(Build.SourcesDirectory)\PSResourceGet\.config\tsaoptions.json
76+
- name: signSrcPath
77+
value: $(repoRoot)/out
78+
- name: depsPath
79+
value: $(signSrcPath)\Microsoft.PowerShell.PSResourceGet\Dependencies
80+
- name: ob_sdl_sbom_enabled
81+
value: true
82+
- name: ob_signing_setup_enabled
83+
value: true
84+
#CodeQL tasks added manually to workaround signing failures
85+
- name: ob_sdl_codeql_compiled_enabled
86+
value: false
87+
pool:
88+
type: windows
89+
steps:
90+
- checkout: self
91+
env:
92+
ob_restore_phase: true # Set ob_restore_phase to run this step before '🔒 Setup Signing' step.
93+
94+
- pwsh: |
95+
if (-not (Test-Path $(repoRoot)/.config/tsaoptions.json)) {
96+
Get-ChildItem $(Build.SourcesDirectory) -recurse -ErrorAction SilentlyContinue
97+
throw "tsaoptions.json does not exist under $(repoRoot)/.config"
98+
}
99+
displayName: Test if tsaoptions.json exists
100+
env:
101+
ob_restore_phase: true # Set ob_restore_phase to run this step before '🔒 Setup Signing' step.
102+
103+
- pwsh: |
104+
Get-ChildItem env:
105+
displayName: Capture Environment
106+
env:
107+
ob_restore_phase: true # Set ob_restore_phase to run this step before '🔒 Setup Signing' step.
108+
109+
- task: UseDotNet@2
110+
displayName: 'Install .NET dependencies'
111+
env:
112+
ob_restore_phase: true # Set ob_restore_phase to run this step before '🔒 Setup Signing' step.
113+
inputs:
114+
packageType: 'sdk'
115+
useGlobalJson: true
116+
# this is to ensure that we are installing the dotnet at the same location as container by default install the dotnet sdks
117+
installationPath: 'C:\Program Files\dotnet\'
118+
workingDirectory: $(repoRoot)
119+
120+
- task: CodeQL3000Init@0 # Add CodeQL Init task right before your 'Build' step.
121+
env:
122+
ob_restore_phase: true # Set ob_restore_phase to run this step before '🔒 Setup Signing' step.
123+
inputs:
124+
Enabled: true
125+
AnalyzeInPipeline: true
126+
Language: csharp
127+
128+
- pwsh: |
129+
$module = 'Microsoft.PowerShell.PSResourceGet'
130+
Write-Verbose "installing $module..." -verbose
131+
$ProgressPreference = 'SilentlyContinue'
132+
Install-Module $module -AllowClobber -Force
133+
displayName: Install PSResourceGet 0.9.0 or above for build.psm1
134+
env:
135+
ob_restore_phase: true # Set ob_restore_phase to run this step before '🔒 Setup Signing' step.
136+
137+
# this is installing .NET
138+
- pwsh: |
139+
Set-Location "$(repoRoot)"
140+
try { ./build.ps1 -Build -Clean -BuildConfiguration Release -BuildFramework 'net472'} catch { throw $_ }
141+
displayName: Execute build
142+
env:
143+
ob_restore_phase: true # Set ob_restore_phase to run this step before '🔒 Setup Signing' step.
144+
145+
- task: CodeQL3000Finalize@0 # Add CodeQL Finalize task right after your 'Build' step.
146+
condition: always()
147+
env:
148+
ob_restore_phase: true # Set ob_restore_phase to run this step before '🔒 Setup Signing' step.
149+
150+
- task: onebranch.pipeline.signing@1
151+
displayName: Sign 1st party files
152+
inputs:
153+
command: 'sign'
154+
signing_profile: external_distribution
155+
files_to_sign: '**\*.psd1;**\*.psm1;**\*.ps1xml;**\Microsoft*.dll'
156+
search_root: $(signSrcPath)
157+
158+
- pwsh: |
159+
$unsignedDepsPath = Join-Path -Path $(signSrcPath) -ChildPath "Microsoft.PowerShell.PSResourceGet" -AdditionalChildPath "UnsignedDependencies"
160+
New-Item -Path $unsignedDepsPath -ItemType Directory -Force
161+
162+
Get-ChildItem -Path $(depsPath) -Filter '*.dll' | Foreach-Object {
163+
$sig = Get-AuthenticodeSignature -FilePath $_.FullName
164+
if ($sig.Status -ne 'Valid' -or $sig.SignerCertificate.Subject -notlike '*Microsoft*' -or $sig.SignerCertificate.Issuer -notlike '*Microsoft Code Signing PCA*') {
165+
# Copy for third party signing
166+
Copy-Item -Path $_.FullName -Dest $unsignedDepsPath -Force -Verbose
167+
}
168+
}
169+
displayName: Find all 3rd party files that need to be signed
170+
171+
- task: onebranch.pipeline.signing@1
172+
displayName: Sign 3rd Party files
173+
inputs:
174+
command: 'sign'
175+
signing_profile: 135020002
176+
files_to_sign: '*.dll'
177+
search_root: $(signSrcPath)/Microsoft.PowerShell.PSResourceGet/UnsignedDependencies
178+
179+
- pwsh: |
180+
$newlySignedDepsPath = Join-Path -Path $(signSrcPath) -ChildPath "Microsoft.PowerShell.PSResourceGet" -AdditionalChildPath "UnsignedDependencies"
181+
Get-ChildItem -Path $newlySignedDepsPath -Filter '*.dll' | Foreach-Object {
182+
$sig = Get-AuthenticodeSignature -FilePath $_.FullName
183+
if ($sig.Status -ne 'Valid' -or $sig.SignerCertificate.Subject -notlike '*Microsoft*' -or $sig.SignerCertificate.Issuer -notlike '*Microsoft Windows Production PCA*') {
184+
Write-Error "File $($_.FileName) is not signed by Microsoft"
185+
}
186+
else {
187+
Copy-Item -Path $_.FullName -Dest $(depsPath) -Force -Verbose
188+
}
189+
}
190+
Remove-Item -Path $newlySignedDepsPath -Recurse -Force
191+
displayName: Validate 3rd party files were signed
192+
193+
- task: CopyFiles@2
194+
displayName: "Copy signed files to ob_outputDirectory - '$(ob_outputDirectory)'"
195+
inputs:
196+
SourceFolder: "$(signSrcPath)"
197+
Contents: '**'
198+
TargetFolder: $(ob_outputDirectory)
199+
200+
- pwsh: |
201+
Write-Host "Displaying contents of signSrcPath:"
202+
Get-ChildItem $(signSrcPath) -Recurse
203+
Write-Host "Displaying contents of ob_outputDirectory:"
204+
Get-ChildItem $(ob_outputDirectory) -Recurse
205+
displayName: Get contents of dirs with signed files
206+
207+
- job: nupkg
208+
dependsOn: jobbuild
209+
displayName: Package Microsoft.PowerShell.PSResourceGet
210+
variables:
211+
- name: ob_outputDirectory
212+
value: '$(Build.ArtifactStagingDirectory)/ONEBRANCH_ARTIFACT'
213+
- name: repoRoot
214+
value: $(Build.SourcesDirectory)\PSResourceGet
215+
- name: ob_sdl_tsa_configFile
216+
value: $(Build.SourcesDirectory)\PSResourceGet\.config\tsaoptions.json
217+
# Disable because SBOM was already built in the previous job
218+
- name: ob_sdl_sbom_enabled
219+
value: false
220+
- name: signOutPath
221+
value: $(repoRoot)/signed
222+
- name: ob_signing_setup_enabled
223+
value: true
224+
# This job is not compiling code, so disable codeQL
225+
- name: ob_sdl_codeql_compiled_enabled
226+
value: false
227+
228+
pool:
229+
type: windows
230+
steps:
231+
- checkout: self
232+
233+
- pwsh: |
234+
if (-not (Test-Path $(repoRoot)/.config/tsaoptions.json)) {
235+
Get-ChildItem $(Build.SourcesDirectory) -recurse -ErrorAction SilentlyContinue
236+
throw "tsaoptions.json does not exist under $(repoRoot)/.config"
237+
}
238+
displayName: Test if tsaoptions.json exists
239+
240+
- task: DownloadPipelineArtifact@2
241+
displayName: 'Download build files'
242+
inputs:
243+
targetPath: $(signOutPath)
244+
artifact: drop_stagebuild_jobbuild
245+
246+
- pwsh: |
247+
Set-Location "$(signOutPath)"
248+
Write-Host "Contents of signOutPath:"
249+
Get-ChildItem $(signOutPath) -Recurse
250+
displayName: Capture artifacts directory structure
251+
252+
- pwsh: |
253+
$module = 'Microsoft.PowerShell.PSResourceGet'
254+
Write-Verbose "installing $module..." -verbose
255+
$ProgressPreference = 'SilentlyContinue'
256+
Install-Module $module -AllowClobber -Force
257+
displayName: Install PSResourceGet 0.9.0 or above for build.psm1
258+
259+
- pwsh: |
260+
Set-Location "$(signOutPath)\Microsoft.PowerShell.PSResourceGet"
261+
New-Item -ItemType Directory -Path "$(signOutPath)\PublishedNupkg" -Force
262+
Register-PSResourceRepository -Name 'localRepo' -Uri "$(signOutPath)\PublishedNupkg"
263+
Publish-PSResource -Path "$(signOutPath)\Microsoft.PowerShell.PSResourceGet" -Repository 'localRepo' -Verbose
264+
displayName: Create nupkg for publishing
265+
266+
- task: onebranch.pipeline.signing@1
267+
displayName: Sign nupkg
268+
inputs:
269+
command: 'sign'
270+
signing_profile: external_distribution
271+
files_to_sign: '**\*.nupkg'
272+
search_root: "$(signOutPath)\PublishedNupkg"
273+
274+
- pwsh: |
275+
Set-Location "$(signOutPath)\PublishedNupkg"
276+
Write-Host "Contents of signOutPath:"
277+
Get-ChildItem "$(signOutPath)" -Recurse
278+
displayName: Find Nupkg
279+
280+
- task: CopyFiles@2
281+
displayName: "Copy nupkg to ob_outputDirectory - '$(ob_outputDirectory)'"
282+
inputs:
283+
Contents: $(signOutPath)\PublishedNupkg\Microsoft.PowerShell.PSResourceGet.*.nupkg
284+
TargetFolder: $(ob_outputDirectory)
285+
286+
- pwsh: |
287+
Write-Host "Contents of ob_outputDirectory:"
288+
Get-ChildItem "$(ob_outputDirectory)" -Recurse
289+
displayName: Find Signed Nupkg
290+
291+
- stage: release
292+
displayName: Release PSResourceGet
293+
dependsOn: stagebuild
294+
variables:
295+
version: $[ stageDependencies.build.main.outputs['package.version'] ]
296+
drop: $(Pipeline.Workspace)/drop_build_main
297+
jobs:
298+
- job: validation
299+
displayName: Manual validation
300+
pool:
301+
type: agentless
302+
timeoutInMinutes: 1440
303+
steps:
304+
- task: ManualValidation@0
305+
displayName: Wait 24 hours for validation
306+
inputs:
307+
instructions: Please validate the release
308+
timeoutInMinutes: 1440
309+
- job: PSGalleryPublish
310+
displayName: Publish to PSGallery
311+
dependsOn: validation
312+
pool:
313+
type: windows
314+
variables:
315+
ob_outputDirectory: '$(Build.ArtifactStagingDirectory)/ONEBRANCH_ARTIFACT'
316+
steps:
317+
- download: current
318+
displayName: Download artifact
319+
320+
- pwsh: |
321+
Get-ChildItem $(Pipeline.Workspace) -Recurse
322+
displayName: Capture environment
323+
324+
- pwsh: |
325+
Get-ChildItem "$(Pipeline.Workspace)/drop_stagebuild_nupkg" -Recurse
326+
displayName: Find signed Nupkg
327+
328+
- task: NuGetCommand@2
329+
displayName: Push PowerShellGet module artifacts to PSGallery feed
330+
inputs:
331+
command: push
332+
packagesToPush: '$(Pipeline.Workspace)\drop_stagebuild_nupkg\PSResourceGet\signed\PublishedNupkg\Microsoft.PowerShell.PSResourceGet.*.nupkg'
333+
nuGetFeedType: external
334+
publishFeedCredentials: PSGet-PSGalleryPush

global.json

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
{
2+
"sdk": {
3+
"version": "8.0.202"
4+
}
5+
}

test/PublishPSResourceTests/PublishPSResource.Tests.ps1

+2-2
Original file line numberDiff line numberDiff line change
@@ -433,8 +433,8 @@ Describe "Test Publish-PSResource" -tags 'CI' {
433433
It "Publish a module to PSGallery using incorrect API key, should throw" {
434434
$version = "1.0.0"
435435
New-ModuleManifest -Path (Join-Path -Path $script:PublishModuleBase -ChildPath "$script:PublishModuleName.psd1") -ModuleVersion $version -Description "$script:PublishModuleName module"
436-
437-
Publish-PSResource -Path $script:PublishModuleBase -Repository PSGallery -APIKey "123456789" -ErrorAction SilentlyContinue
436+
$APIKey = New-Guid
437+
Publish-PSResource -Path $script:PublishModuleBase -Repository PSGallery -APIKey $APIKey -ErrorAction SilentlyContinue
438438

439439
$Error[0].FullyQualifiedErrorId | Should -be "403Error,Microsoft.PowerShell.PSResourceGet.Cmdlets.PublishPSResource"
440440
}

0 commit comments

Comments
 (0)