Skip to content

Commit

Permalink
[ci] Switch to using the 1ES mandated pipeline template. (#844)
Browse files Browse the repository at this point in the history
As part of Microsoft's continued push for supply chain security, our CI that builds shipping software must extend an "official" template that can be used to ensure various safety checks have run.

Unfortunately, this requires extensive changes to our CI to fit their model.  This PR requires both necessary changes and cleanup done to make our process mesh better with the template.

The only functional difference should be:
- Previously the outputs of both the `Windows` and `MacOS` builds were copied to the same artifact directory (`"nuget"`) which was signed and released.  This meant that the last one written "won" and that's what we shipped.  The new template didn't like multiple agents writing to the same output directory, so now we only write to `output-windows` and `output-macos`, and we always sign and ship the `output-windows` output.
  • Loading branch information
jpobst authored Mar 4, 2024
1 parent b741a7b commit c9f6fdb
Show file tree
Hide file tree
Showing 5 changed files with 134 additions and 200 deletions.
79 changes: 63 additions & 16 deletions azure-pipelines.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,27 +9,74 @@ variables:
BUILD_NUMBER: $(Build.BuildNumber)
BUILD_COMMIT: $(Build.SourceVersion)

# Build variables
mainBranchName: main # Name of Git "main" branch
configuration: Release # Build configuration: 'Debug', 'Release'

# Reporting variables
areaPath: DevDiv\VS Client - Runtime SDKs\Android # AzDo area path to log any issues

# Windows specific variables
windowsAgentPoolName: Maui-1ESPT # Windows VM pool name
windowsImage: 1ESPT-Windows2022 # Windows VM image name
windowsClassicInstaller: https://aka.ms/xamarin-android-commercial-d17-4-windows # Windows Classic XA installer URL

# macOS specific variables
macosAgentPoolName: Azure Pipelines # macOS VM pool name
macosImage: internal-macos12 # macOS VM image name
macosClassicInstaller: https://aka.ms/xamarin-android-commercial-d17-4-macos # macOS Classic XA installer URL

resources:
repositories:
- repository: 1esPipelines
type: git
name: 1ESPipelineTemplates/1ESPipelineTemplates
ref: refs/tags/release
- repository: internal-templates
type: github
name: xamarin/yaml-templates
endpoint: xamarin
ref: refs/heads/main

jobs:
- template: build/ci/build.yml

- ${{ if eq(variables['System.TeamProject'], 'devdiv') }}:
- template: sign-artifacts/jobs/v2.yml@internal-templates
parameters:
dependsOn: [ 'build' ]
condition: startsWith(variables['Build.SourceBranch'], 'refs/tags/')

- template: compliance/sbom/job.v1.yml@internal-templates
parameters:
dependsOn: signing
condition: startsWith(variables['Build.SourceBranch'], 'refs/tags/')
artifactNames: [ nuget-signed ]
packageName: androidx
packageFilter: '*.nupkg'
extends:
template: v1/1ES.Official.PipelineTemplate.yml@1esPipelines
parameters:
pool:
name: AzurePipelines-EO
image: 1ESPT-Windows2022
os: windows

stages:
- stage: Build

jobs:
- template: build/ci/build.yml@self
parameters:
name: windows
buildPool:
name: $(windowsAgentPoolName)
image: $(windowsImage)
os: windows
classicInstallerUrl: $(windowsClassicInstaller)
mainBranchName: $(mainBranchName)
configuration: $(configuration)
runAPIScan: true

- template: build/ci/build.yml@self
parameters:
name: macos
buildPool:
name: $(macosAgentPoolName)
vmImage: $(macosImage)
os: macOS
classicInstallerUrl: $(macosClassicInstaller)
mainBranchName: $(mainBranchName)
configuration: $(configuration)

- template: sign-artifacts/jobs/v2.yml@internal-templates
parameters:
dependsOn: [ 'build_windows' ]
artifactName: output-windows
usePipelineArtifactTasks: true
use1ESTemplate: true
condition: startsWith(variables['Build.SourceBranch'], 'refs/tags/')
14 changes: 7 additions & 7 deletions build/ci/api-scan.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ steps:
TargetFolder: ${{ parameters.apiScanDirectory }}
OverWrite: true
flattenFolders: true
condition: and(succeeded(), eq(variables['runAPIScan'], 'true'), eq('refs/heads/${{ parameters.mainBranchName }}', variables['Build.SourceBranch']))
condition: and(succeeded(), eq('refs/heads/${{ parameters.mainBranchName }}', variables['Build.SourceBranch']))

- task: CmdLine@2
displayName: 'Remove System assemblies from APIScan'
Expand All @@ -23,14 +23,14 @@ steps:
del ${{ parameters.apiScanDirectory }}\System.*
del ${{ parameters.apiScanDirectory }}\mscorlib.dll
del ${{ parameters.apiScanDirectory }}\netstandard.dll
condition: and(succeeded(), eq(variables['runAPIScan'], 'true'), eq('refs/heads/${{ parameters.mainBranchName }}', variables['Build.SourceBranch']))
condition: and(succeeded(), eq('refs/heads/${{ parameters.mainBranchName }}', variables['Build.SourceBranch']))

- task: CmdLine@2
displayName: 'List Files for APIScan'
inputs:
script: |
tree ${{ parameters.apiScanDirectory }} /f
condition: and(succeeded(), eq(variables['runAPIScan'], 'true'), eq('refs/heads/${{ parameters.mainBranchName }}', variables['Build.SourceBranch']))
condition: and(succeeded(), eq('refs/heads/${{ parameters.mainBranchName }}', variables['Build.SourceBranch']))

### Run latest version of APIScan listed at https://www.1eswiki.com/wiki/APIScan_Build_Task
- task: APIScan@2
Expand All @@ -41,7 +41,7 @@ steps:
softwareVersionNum: '$(Build.BuildId)'
isLargeApp: true
toolVersion: Latest
condition: and(succeeded(), eq(variables['runAPIScan'], 'true'), eq('refs/heads/${{ parameters.mainBranchName }}', variables['Build.SourceBranch']))
condition: and(succeeded(), eq('refs/heads/${{ parameters.mainBranchName }}', variables['Build.SourceBranch']))
env:
AzureServicesAuthConnectionString: runAs=App;AppId=$(ApiScanClientId);TenantId=$(ApiScanTenant);AppKey=$(ApiScanSecret)

Expand All @@ -51,7 +51,7 @@ steps:
GdnExportAllTools: false
GdnExportGdnToolApiScan: true
GdnExportOutputSuppressionFile: source.gdnsuppress
condition: and(eq(variables['runAPIScan'], 'true'), eq('refs/heads/${{ parameters.mainBranchName }}', variables['Build.SourceBranch']))
condition: eq('refs/heads/${{ parameters.mainBranchName }}', variables['Build.SourceBranch'])

- task: PublishSecurityAnalysisLogs@3
displayName: Publish Guardian Artifacts
Expand All @@ -61,11 +61,11 @@ steps:
AllTools: false
APIScan: true
ToolLogsNotFoundAction: Warning
condition: and(eq(variables['runAPIScan'], 'true'), eq('refs/heads/${{ parameters.mainBranchName }}', variables['Build.SourceBranch']))
condition: eq('refs/heads/${{ parameters.mainBranchName }}', variables['Build.SourceBranch'])

- task: PostAnalysis@2
displayName: Fail Build on Guardian Issues
inputs:
GdnBreakAllTools: false
GdnBreakGdnToolApiScan: true
condition: and(eq(variables['runAPIScan'], 'true'), eq('refs/heads/${{ parameters.mainBranchName }}', variables['Build.SourceBranch']))
condition: eq('refs/heads/${{ parameters.mainBranchName }}', variables['Build.SourceBranch'])
166 changes: 63 additions & 103 deletions build/ci/build.yml
Original file line number Diff line number Diff line change
@@ -1,122 +1,82 @@
parameters:
# Environment Parameters
name: 'build' # the name of the build job for dependency purposes
timeoutInMinutes: 300 # the timeout in minutes
mainBranchName: 'main' # the "main" branch that should be used - can be something other than "main"
macosAgentPoolName: 'Azure Pipelines' # the name of the macOS VM pool
# https://github.com/actions/runner-images
macosImage: internal-macos12 # macOS VM image name, must be "internal" locked down image
windowsAgentPoolName: android-win-2022 # the name of the Windows VM pool
windowsImage: 'windows-latest' # the name of the Windows VM image
name: # Job display name
buildPool: # VM pool information
classicInstallerUrl: # URL to retrieve the Classic XA installer

# Build Parameters
mainBranchName: 'main' # Name of Git "main" branch
configuration: 'Release' # Build configuration: 'Debug', 'Release'
verbosity: 'normal' # Build verbosity: 'minimal', 'normal', 'diagnostic'
timeoutInMinutes: 300 # Max job runtime in minutes
runAPIScan: false # Run APIScan analysis

# Tool Parameters
dotnetVersion: '7.0.405' # the version of .NET to use
dotnetWorkloadRollbackFile: 'workloads.json'
dotnetWorkloadSource: 'https://aka.ms/dotnet6/nuget/index.json'
dotnetNuGetOrgSource: 'https://api.nuget.org/v3/index.json'
classicXAPkg: https://aka.ms/xamarin-android-commercial-d17-4-macos
classicXAVsix: https://aka.ms/xamarin-android-commercial-d17-4-windows
skipUnitTests: false # do not run unit test step
dotnetVersion: '7.0.406' # .NET version to install on agent
dotnetWorkloadRollbackFile: 'workloads.json' # Rollback file specifying workload versions to install
dotnetNuGetOrgSource: 'https://api.nuget.org/v3/index.json' # NuGet.org URL to find workloads
dotnetWorkloadSource: 'https://aka.ms/dotnet6/nuget/index.json' # .NET engineering URL to find workloads
skipUnitTests: false # Skip running unit tests

tools: # a list of additional .NET global tools needed
tools: # Additional .NET global tools to install
- 'xamarin.androidbinderator.tool': '0.5.7'
- 'Cake.Tool': '4.0.0'
- 'boots': '1.1.0.712-preview2'
- 'private-api-tools': '1.0.2'

# Build Parameters
verbosity: 'normal' # the build verbosity: 'minimal', 'normal', 'diagnostic'
configuration: 'Release' # the build configuration: 'Debug', 'Release'

# Reporting/Analysis Parameters
areaPath: 'DevDiv\VS Client - Runtime SDKs\Android' # the areaPath to log any issues
publishJob: '' # the job to use as the source of the 'nuget' artifact: '', 'windows', 'macos', 'linux'
publishOutputSuffix: '' # the artifact suffix to use when publishing the output folder
signListPath: 'SignList.xml' # the path to the SignList.xml to copy into the nuget artifact for signing
artifactsPath: 'output' # the path to the NuGet packages that need to be signed, verified and published
artifactsPath: 'output' # Path to the NuGet packages that need to be signed, verified and published
signListPath: 'SignList.xml' # Path to 'SignList.xml' used for signing NuGet packages

jobs:
- job: ${{ parameters.name }}
strategy:
matrix:
macos:
poolName: ${{ parameters.macosAgentPoolName }}
imageName: ${{ parameters.macosImage }}
classicInstallerUrl: ${{ parameters.classicXAPkg }}
runCodeQL: false
windows:
poolName: ${{ parameters.windowsAgentPoolName }}
imageName: ${{ parameters.windowsImage }}
classicInstallerUrl: ${{ parameters.classicXAVsix }}
runCodeQL: true
runAPIScan: true
displayName: Build
timeoutInMinutes: ${{ parameters.timeoutInMinutes }}
variables:
Codeql.Enabled: $(runCodeQL)
pool:
name: $(poolName)
vmImage: $(imageName)

steps:
- template: setup-environment.yml
parameters:
dotnetVersion: ${{ parameters.dotnetVersion }}
dotnetWorkloadRollbackFile: ${{ parameters.dotnetWorkloadRollbackFile }}
dotnetWorkloadSource: ${{ parameters.dotnetWorkloadSource }}
dotnetNuGetOrgSource: ${{ parameters.dotnetNuGetOrgSource }}
dotnetTools: ${{ parameters.tools }}
classicInstallerUrl: $(classicInstallerUrl)
- job: build_${{ parameters.name }}
displayName: ${{ parameters.name }}
timeoutInMinutes: ${{ parameters.timeoutInMinutes }}
pool: ${{ parameters.buildPool }}

templateContext:
sdl:
spotBugs:
enabled: false
binskim:
scanOutputDirectoryOnly: true
outputs:
- output: pipelineArtifact
targetPath: ${{ parameters.artifactsPath }}
artifactName: output-${{ parameters.name }}

steps:
- template: setup-environment.yml
parameters:
dotnetVersion: ${{ parameters.dotnetVersion }}
dotnetWorkloadRollbackFile: ${{ parameters.dotnetWorkloadRollbackFile }}
dotnetWorkloadSource: ${{ parameters.dotnetWorkloadSource }}
dotnetNuGetOrgSource: ${{ parameters.dotnetNuGetOrgSource }}
dotnetTools: ${{ parameters.tools }}
classicInstallerUrl: ${{ parameters.classicInstallerUrl }}

- template: build-and-test.yml
parameters:
artifactsPath: ${{ parameters.artifactsPath }}
verbosity: ${{ parameters.verbosity }}
configuration: ${{ parameters.configuration }}
skipUnitTests: ${{ parameters.skipUnitTests }}
- template: build-and-test.yml
parameters:
artifactsPath: ${{ parameters.artifactsPath }}
verbosity: ${{ parameters.verbosity }}
configuration: ${{ parameters.configuration }}
skipUnitTests: ${{ parameters.skipUnitTests }}

- ${{ if eq(parameters.runAPIScan, true) }}:
- template: api-scan.yml
parameters:
mainBranchName: ${{ parameters.mainBranchName }}

# after the build is complete
- pwsh: |
$srcExists = (Test-Path "${{ parameters.signListPath }}")
$dstExists = (Test-Path "${{ parameters.artifactsPath }}\SignList.xml")
if ($srcExists -and !$dstExists) {
Copy-Item "${{ parameters.signListPath }}" "${{ parameters.artifactsPath }}\SignList.xml"
Write-Host "Copied ${{ parameters.signListPath }} to ${{ parameters.artifactsPath }}\SignList.xml"
} elseif (!$srcExists) {
Write-Host "${{ parameters.signListPath }} did not exist, nothing copied."
} elseif ($dstExists) {
Write-Host "${{ parameters.artifactsPath }}\SignList.xml already existed, nothing copied."
}
displayName: 'Copy SignList.xml to the nuget artifact'
- task: PublishBuildArtifacts@1
displayName: 'Publish artifacts'
condition: or(eq('${{ parameters.publishJob }}', ''), eq('${{ parameters.publishJob }}', variables['System.JobName']))
inputs:
PathToPublish: ${{ parameters.artifactsPath }}
ArtifactName: nuget
- task: PublishBuildArtifacts@1
displayName: 'Publish platform artifacts'
condition: always()
inputs:
PathToPublish: output
ArtifactName: output-$(System.JobName)${{ parameters.publishOutputSuffix }}
# run any required checks
- ${{ if eq(variables['System.TeamProject'], 'devdiv') }}:
- task: ComponentGovernanceComponentDetection@0
displayName: 'Run component detection'
condition: and(always(), eq('refs/heads/${{ parameters.mainBranchName }}', variables['Build.SourceBranch']))
inputs:
scanType: 'Register'
verbosity: 'Verbose'
alertWarningLevel: 'High'

- template: code-analysis.yml
parameters:
name: ${{ parameters.name }}
mainBranchName: ${{ parameters.mainBranchName }}
areaPath: ${{ parameters.areaPath }}
configuration: ${{ parameters.configuration }}
# Copy SignList.xml to output
- pwsh: |
$srcExists = (Test-Path "${{ parameters.signListPath }}")
$dstExists = (Test-Path "${{ parameters.artifactsPath }}\SignList.xml")
if ($srcExists -and !$dstExists) {
Copy-Item "${{ parameters.signListPath }}" "${{ parameters.artifactsPath }}\SignList.xml"
Write-Host "Copied ${{ parameters.signListPath }} to ${{ parameters.artifactsPath }}\SignList.xml"
} elseif (!$srcExists) {
Write-Host "${{ parameters.signListPath }} did not exist, nothing copied."
} elseif ($dstExists) {
Write-Host "${{ parameters.artifactsPath }}\SignList.xml already existed, nothing copied."
}
displayName: Copy SignList.xml to output
Loading

0 comments on commit c9f6fdb

Please sign in to comment.