Skip to content

Commit efc56c4

Browse files
Add SBOM manifest generation (#48)
Adds SBOM manifest generation to the CI pipeline. The SBOM will only be generated for release builds or when the user overrides the SimulateReleaseBuild variable on ADO.
1 parent 0ed0a0e commit efc56c4

File tree

7 files changed

+246
-54
lines changed

7 files changed

+246
-54
lines changed

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,5 +21,8 @@ local.settings.json
2121
# Temporary output folder for Durable SDK binaries
2222
**/src/out
2323

24+
# Package folder for .tar file
25+
/package/**
26+
2427
# VS publish settings
2528
*.pubxml

azure-pipelines.yml

Lines changed: 67 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,78 @@ pool:
1818
demands:
1919
- ImageOverride -equals $(imageName)
2020

21+
variables:
22+
artifactName: 'azure-functions-durable-powershell-$(Build.SourceVersion)'
23+
# Every build will increment
24+
buildNumber: $[counter('build', 001) ]
25+
modulePath: './test/E2E/durableApp/Modules/AzureFunctions.PowerShell.Durable.SDK'
26+
2127
steps:
22-
- pwsh: ./test/E2E/Start-E2ETest.ps1 -UseCoreToolsBuildFromIntegrationTests
28+
- pwsh: |
29+
$simulateReleaseBuild = $null
30+
Write-Host "SimulateReleaseBuild set to $env:SimulateReleaseBuild"
31+
if (-not([bool]::TryParse($env:SimulateReleaseBuild, [ref] $simulateReleaseBuild)))
32+
{
33+
throw "SimulateReleaseBuild can only be set to true or false."
34+
}
35+
36+
$isReleaseBuild = $false
37+
if ($env:BuildSourceBranchName -like "release_*" -or $simulateReleaseBuild)
38+
{
39+
$isReleaseBuild = $true
40+
}
41+
Write-Host "Setting IsReleaseBuild to $isReleaseBuild because SimulateReleaseBuild is $env:SimulateReleaseBuild"
42+
Write-Host "##vso[task.setvariable variable=IsReleaseBuild]$isReleaseBuild"
43+
Write-Host "IsReleaseBuild: $isReleaseBuild"
44+
displayName: Set IsReleaseBuild pipeline variable
45+
env:
46+
SimulateReleaseBuild: $(SimulateReleaseBuild)
47+
48+
- pwsh: |
49+
Import-Module ".\pipelineUtilities.psm1" -Force
50+
Install-Dotnet
51+
displayName: 'Install .NET 3.1'
52+
53+
- pwsh: |
54+
Write-Host "IsReleaseBuild set to $env:IsReleaseBuild"
55+
$isReleaseBuild = $false
56+
if (-not([bool]::TryParse($env:IsReleaseBuild, [ref] $isReleaseBuild)))
57+
{
58+
throw "SimulateReleaseBuild can only be set to true or false."
59+
}
60+
61+
# We only generate an SBOM for release or simulated release builds
62+
Write-Host "Running ./build.ps1 -Configuration Release -AddSBOM:$isReleaseBuild..."
63+
./build.ps1 -Configuration Release -AddSBOM:$isReleaseBuild
64+
displayName: 'Build Durable SDK'
65+
env:
66+
# We include IsReleaseBuild as an environment variable since Linux agents don't seem to support including
67+
# pipeline variables in scripts with the $(variable) syntax
68+
IsReleaseBuild: $(IsReleaseBuild)
69+
SBOMUtilSASUrl: $(SBOMUtilSASUrl)
70+
71+
- pwsh: |
72+
./test/E2E/Start-E2ETest.ps1 -NoBuild -UseCoreToolsBuildFromIntegrationTests
2373
env:
2474
AzureWebJobsStorage: $(AzureWebJobsStorage)
2575
displayName: 'Run E2E tests'
2676

77+
- task: ArchiveFiles@2
78+
inputs:
79+
rootFolderOrFile: '$(Build.SourcesDirectory)/test/E2E/durableApp/Modules/AzureFunctions.PowerShell.Durable.SDK'
80+
includeRootFolder: false
81+
archiveType: 'tar'
82+
archiveFile: '$(Build.ArtifactStagingDirectory)/$(artifactName).tar.gz'
83+
replaceExistingArchive: true
84+
displayName: 'Tar build tartifacts'
85+
86+
- task: PublishBuildArtifacts@1
87+
inputs:
88+
PathtoPublish: $(Build.ArtifactStagingDirectory)
89+
ArtifactName: $(artifactName).tar.gz
90+
condition: and(succeeded(), eq(variables['IsReleaseBuild'], 'true'))
91+
displayName: 'Publish build artifacts'
92+
2793
- task: PublishTestResults@2
2894
inputs:
2995
testResultsFormat: 'VSTest'

build.ps1

Lines changed: 31 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -3,56 +3,29 @@
33
param(
44
[ValidateSet('Debug', 'Release')]
55
[string]
6-
$Configuration = 'Debug'
6+
$Configuration = 'Debug',
7+
[switch]
8+
$AddSBOM
79
)
810

11+
Import-Module "$PSScriptRoot\pipelineUtilities.psm1" -Force
12+
913
$packageName = "AzureFunctions.PowerShell.Durable.SDK"
1014
$shimPath = "$PSScriptRoot/src/DurableSDK"
1115
$durableEnginePath = "$PSScriptRoot/src/DurableEngine"
1216
$durableAppPath = "$PSScriptRoot/test/E2E/durableApp/Modules/$packageName"
1317
$powerShellModulePath = "$PSScriptRoot/src/$packageName.psm1"
1418
$manifestPath = "$PSScriptRoot/src/$packageName.psd1"
1519

16-
$outputPath = "$PSScriptRoot/src/out/"
17-
if ($Configuration -eq "Debug")
18-
{
19-
# Publish directly to the test durable app for testing
20-
$outputPath = $durableAppPath
21-
}
20+
# Publish directly to the test durable app for testing
21+
$outputPath = $durableAppPath
22+
2223
$sharedDependenciesPath = "$outputPath/Dependencies/"
2324

2425
$netCoreTFM = 'net6.0'
2526
$publishPathSuffix = "bin/$Configuration/$netCoreTFM/publish"
2627

27-
function Write-Log
28-
{
29-
param (
30-
[Parameter(Mandatory=$true)]
31-
[ValidateNotNullOrEmpty()]
32-
[System.String]
33-
$Message,
34-
35-
[Switch]
36-
$Warning,
37-
38-
[Switch]
39-
$Throw,
40-
41-
[System.String]
42-
$Color
43-
)
44-
45-
$Message = (Get-Date -Format G) + " -- $Message"
46-
47-
if ($Throw)
48-
{
49-
throw $Message
50-
}
51-
52-
$foregroundColor = if ($Warning.IsPresent) { 'Yellow' } elseif ($Color) { $Color } else { 'Green' }
53-
Write-Host -ForegroundColor $foregroundColor $Message
54-
}
55-
28+
#region BUILD ARTIFACTS ===========================================================================
5629
Write-Log "Build started..."
5730
Write-Log "Configuration: '$Configuration'`nOutput folder '$outputPath'`nShared dependencies folder: '$sharedDependenciesPath'" "Gray"
5831

@@ -89,18 +62,37 @@ foreach ($project in $projects.GetEnumerator()) {
8962
$commonFiles = [System.Collections.Generic.HashSet[string]]::new()
9063

9164
Write-Log "Copying assemblies from the Durable Engine project into $sharedDependenciesPath" "Gray"
92-
Get-ChildItem -Path "$durableEnginePath/$publishPathSuffix" |
65+
Get-ChildItem -Path (Join-Path "$durableEnginePath" "$publishPathSuffix") |
9366
Where-Object { $_.Extension -in '.dll','.pdb' } |
9467
ForEach-Object { [void]$commonFiles.Add($_.Name); Copy-Item -LiteralPath $_.FullName -Destination $sharedDependenciesPath }
9568

9669
# Copy all *unique* assemblies from Durable SDK into output directory
9770
Write-Log "Copying unique assemblies from the Durable SDK project into $outputPath" "Gray"
98-
Get-ChildItem -Path "$shimPath/$publishPathSuffix" |
71+
Get-ChildItem -Path (Join-Path "$shimPath" "$publishPathSuffix") |
9972
Where-Object { $_.Extension -in '.dll','.pdb' -and -not $commonFiles.Contains($_.Name) } |
10073
ForEach-Object { Copy-Item -LiteralPath $_.FullName -Destination $outputPath }
10174

10275
# Move Durable SDK manifest into the output directory
10376
Write-Log "Copying PowerShell module and manifest from the Durable SDK source code into $outputPath" "Gray"
10477
Copy-Item -Path $powerShellModulePath -Destination $outputPath
10578
Copy-Item -Path $manifestPath -Destination $outputPath
106-
Write-Log "Build succeeded!"
79+
Write-Log "Build succeeded!"
80+
#endregion
81+
82+
#region ADD SBOM ==================================================================================
83+
if ($AddSBOM) {
84+
# Install manifest tool
85+
$manifestToolPath = Install-SBOMUtil
86+
Write-Log "Manifest tool path: $manifestToolPath"
87+
88+
# Generate manifest
89+
$telemetryFilePath = Join-Path $PSScriptRoot ((New-Guid).Guid + ".json")
90+
$packageName = "AzureFunctions.PowerShell.Durable.SDK"
91+
92+
Write-Log "Running: dotnet $manifestToolPath generate -BuildDropPath $outputPath -BuildComponentPath $outputPath -Verbosity Information -t $telemetryFilePath -PackageName $packageName"
93+
dotnet $manifestToolPath generate -BuildDropPath $outputPath -BuildComponentPath $outputPath -Verbosity Information -t $telemetryFilePath -PackageName $packageName
94+
95+
# Discard telemetry generated
96+
Remove-Item -Path $telemetryFilePath -ErrorAction Ignore
97+
}
98+
#endregion

pipelineUtilities.psm1

Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
#
2+
# Copyright (c) Microsoft. All rights reserved.
3+
# Licensed under the MIT license. See LICENSE file in the project root for full license information.
4+
#
5+
6+
using namespace System.Runtime.InteropServices
7+
8+
$DotnetSDKVersionRequirements = @{
9+
10+
# .NET SDK 3.1 is required by the Microsoft.ManifestTool.dll tool
11+
'3.1' = @{
12+
MinimalPatch = '415'
13+
DefaultPatch = '415'
14+
}
15+
}
16+
17+
function Write-Log
18+
{
19+
param (
20+
[Parameter(Mandatory=$true)]
21+
[ValidateNotNullOrEmpty()]
22+
[System.String]
23+
$Message,
24+
25+
[Switch]
26+
$Warning,
27+
28+
[Switch]
29+
$Throw,
30+
31+
[System.String]
32+
$Color
33+
)
34+
35+
$Message = (Get-Date -Format G) + " -- $Message"
36+
37+
if ($Throw)
38+
{
39+
throw $Message
40+
}
41+
42+
$foregroundColor = if ($Warning.IsPresent) { 'Yellow' } elseif ($Color) { $Color } else { 'Green' }
43+
Write-Host -ForegroundColor $foregroundColor $Message
44+
}
45+
46+
function Install-SBOMUtil
47+
{
48+
if ([string]::IsNullOrEmpty($env:SBOMUtilSASUrl))
49+
{
50+
throw "The `$SBOMUtilSASUrl environment variable cannot be null or empty when specifying the `$AddSBOM switch"
51+
}
52+
53+
$MANIFESTOOLNAME = "ManifestTool"
54+
Write-Log "Installing $MANIFESTOOLNAME..."
55+
56+
$MANIFESTOOL_DIRECTORY = Join-Path $PSScriptRoot $MANIFESTOOLNAME
57+
Remove-Item -Recurse -Force $MANIFESTOOL_DIRECTORY -ErrorAction Ignore
58+
59+
Invoke-RestMethod -Uri $env:SBOMUtilSASUrl -OutFile "$MANIFESTOOL_DIRECTORY.zip"
60+
Expand-Archive "$MANIFESTOOL_DIRECTORY.zip" -DestinationPath $MANIFESTOOL_DIRECTORY
61+
62+
$dllName = "Microsoft.ManifestTool.dll"
63+
$manifestToolPath = Join-Path "$MANIFESTOOL_DIRECTORY" "$dllName"
64+
65+
if (-not (Test-Path $manifestToolPath))
66+
{
67+
throw "$MANIFESTOOL_DIRECTORY does not contain '$dllName'"
68+
}
69+
70+
Write-Log 'Done.'
71+
72+
return $manifestToolPath
73+
}
74+
75+
76+
function AddLocalDotnetDirPath {
77+
$LocalDotnetDirPath = if ($IsWindows) { "$env:ProgramFiles/dotnet" } else { "/usr/share/dotnet" }
78+
if (($env:PATH -split [IO.Path]::PathSeparator) -notcontains $LocalDotnetDirPath) {
79+
$env:PATH = $LocalDotnetDirPath + [IO.Path]::PathSeparator + $env:PATH
80+
}
81+
}
82+
83+
function Find-Dotnet
84+
{
85+
AddLocalDotnetDirPath
86+
$listSdksOutput = dotnet --list-sdks
87+
$installedDotnetSdks = $listSdksOutput | ForEach-Object { $_.Split(" ")[0] }
88+
Write-Host "Detected dotnet SDKs: $($installedDotnetSdks -join ', ')"
89+
foreach ($majorMinorVersion in $DotnetSDKVersionRequirements.Keys) {
90+
$minimalVersion = "$majorMinorVersion.$($DotnetSDKVersionRequirements[$majorMinorVersion].MinimalPatch)"
91+
$firstAcceptable = $installedDotnetSdks |
92+
Where-Object { $_.StartsWith("$majorMinorVersion.") } |
93+
Where-Object { [System.Management.Automation.SemanticVersion]::new($_) -ge [System.Management.Automation.SemanticVersion]::new($minimalVersion) } |
94+
Select-Object -First 1
95+
if (-not $firstAcceptable) {
96+
throw "Cannot find the dotnet SDK for .NET Core $majorMinorVersion. Version $minimalVersion or higher is required. Please specify '-Bootstrap' to install build dependencies."
97+
}
98+
}
99+
}
100+
101+
function Install-Dotnet {
102+
[CmdletBinding()]
103+
param(
104+
[string]$Channel = 'release'
105+
)
106+
try {
107+
Find-Dotnet
108+
return # Simply return if we find dotnet SDk with the correct version
109+
} catch { }
110+
$obtainUrl = "https://raw.githubusercontent.com/dotnet/cli/master/scripts/obtain"
111+
try {
112+
$installScript = if ($IsWindows) { "dotnet-install.ps1" } else { "dotnet-install.sh" }
113+
Invoke-WebRequest -Uri $obtainUrl/$installScript -OutFile $installScript
114+
foreach ($majorMinorVersion in $DotnetSDKVersionRequirements.Keys) {
115+
$version = "$majorMinorVersion.$($DotnetSDKVersionRequirements[$majorMinorVersion].DefaultPatch)"
116+
Write-Host "Installing dotnet SDK version $version"
117+
if ($IsWindows) {
118+
& .\$installScript -InstallDir "$env:ProgramFiles/dotnet" -Channel $Channel -Version $Version
119+
} else {
120+
bash ./$installScript --install-dir "/usr/share/dotnet" -c $Channel -v $Version
121+
}
122+
}
123+
AddLocalDotnetDirPath
124+
}
125+
finally {
126+
Remove-Item $installScript -Force -ErrorAction SilentlyContinue
127+
}
128+
}

src/DurableSDK/DurableSDK.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,6 @@
88

99
<ItemGroup>
1010
<ProjectReference Include="..\DurableEngine\DurableEngine.csproj" />
11-
<PackageReference Include="Microsoft.PowerShell.SDK" Version="7.2.1" PrivateAssets="all"/>
11+
<PackageReference Include="Microsoft.PowerShell.SDK" Version="7.2.1" PrivateAssets="all" />
1212
</ItemGroup>
1313
</Project>

test/E2E/Start-E2ETest.ps1

Lines changed: 15 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44
#
55
param
66
(
7+
[Switch]
8+
$NoBuild,
79
[Switch]
810
$UseCoreToolsBuildFromIntegrationTests,
911
[Switch]
@@ -98,11 +100,11 @@ Write-Host "Deleting $FUNC_CLI_DIRECTORY if it exists..."
98100
Remove-Item -Force "$FUNC_CLI_DIRECTORY.zip" -ErrorAction Ignore
99101
Remove-Item -Recurse -Force $FUNC_CLI_DIRECTORY -ErrorAction Ignore
100102

101-
if (-not $SkipCoreToolsDownload.IsPresent)
103+
if (-not $SkipCoreToolsDownload)
102104
{
103105
Write-Host "Downloading Core Tools because SkipCoreToolsDownload switch parameter is not present..."
104106
$coreToolsDownloadURL = $null
105-
if ($UseCoreToolsBuildFromIntegrationTests.IsPresent)
107+
if ($UseCoreToolsBuildFromIntegrationTests)
106108
{
107109
$coreToolsDownloadURL = "https://functionsintegclibuilds.blob.core.windows.net/builds/$FUNC_RUNTIME_VERSION/latest/Azure.Functions.Cli.$os-$arch.zip"
108110
$env:CORE_TOOLS_URL = "https://functionsintegclibuilds.blob.core.windows.net/builds/$FUNC_RUNTIME_VERSION/latest"
@@ -148,16 +150,17 @@ if (-not $SkipCoreToolsDownload.IsPresent)
148150
$env:FUNC_PATH = $funcPath
149151
Write-Host "Set FUNC_PATH environment variable to $env:FUNC_PATH"
150152

151-
# For both integration build test runs and regular test runs, we copy binaries to durableApp/Modules
152-
Write-Host "Building the DurableSDK module and copying binaries to the durableApp/Modules directory..."
153-
$configuration = if ($env:CONFIGURATION) { $env:CONFIGURATION } else { 'Debug' }
154-
155-
Push-Location "$PSScriptRoot/../.."
156-
try {
157-
& ./build.ps1 -Configuration 'Debug'
158-
}
159-
finally {
160-
Pop-Location
153+
if (-not $NoBuild) {
154+
# For both integration build test runs and regular test runs, we copy binaries to durableApp/Modules
155+
Write-Host "Building the DurableSDK module and copying binaries to the durableApp/Modules directory..."
156+
157+
Push-Location "$PSScriptRoot/../.."
158+
try {
159+
& ./build.ps1 -Configuration 'Debug'
160+
}
161+
finally {
162+
Pop-Location
163+
}
161164
}
162165

163166
Write-Host "Starting Core Tools..."

test/E2E/durableApp/SimpleOrchestrator/run.ps1

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,4 +6,4 @@ $ErrorActionPreference = 'Stop'
66

77
$output = Invoke-DurableActivity -FunctionName "Hello" -Input "Tokyo"
88

9-
return $output
9+
$output

0 commit comments

Comments
 (0)