11# This workflow will build, test, sign and pack the release branches for EPPlus.
2- # It will also generate and publish an SBOM
2+ # It will also generate and publish an SBOM per target framework.
33# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-net
44
55name : Build Release Branches
@@ -20,34 +20,26 @@ jobs:
2020 uses : actions/setup-dotnet@v4
2121 with :
2222 dotnet-version : ' 9.0.x'
23- - name : Restore dependencies
24- run : dotnet restore ./src/EPPlus.sln
2523
26- # --- SBOM ---
27- - name : Install CycloneDX
28- run : dotnet tool install --global CycloneDX
29- - name : Read version from csproj
30- id : read_version
24+ # --- Read version and TFMs from csproj ---
25+ - name : Read version and target frameworks from csproj
26+ id : read_csproj
3127 run : |
32- $version = ([xml](Get-Content ./src/EPPlus/EPPlus.csproj)).Project.PropertyGroup.Version | Where-Object { $_ } | Select-Object -First 1
28+ $xml = [xml](Get-Content ./src/EPPlus/EPPlus.csproj)
29+ $version = $xml.Project.PropertyGroup.Version | Where-Object { $_ } | Select-Object -First 1
30+ $tfms = $xml.Project.PropertyGroup.TargetFrameworks | Where-Object { $_ } | Select-Object -First 1
3331 echo "VERSION=$version" >> $env:GITHUB_ENV
32+ echo "TFMS=$tfms" >> $env:GITHUB_ENV
3433 shell : pwsh
35- - name : Generate SBOM
36- run : dotnet CycloneDX ./src/EPPlus/EPPlus.csproj -o ./sbom -F Json -st Library -sv ${{ env.VERSION }} -fn epplus-${{ env.VERSION }}.sbom.json -imp ./src/EPPlus/sbom-metadata-template.xml
37- - name : Generate SHA-256 checksum for SBOM
38- run : |
39- $sbomFile = "./sbom/epplus-${{ env.VERSION }}.sbom.json"
40- $hash = (Get-FileHash -Path $sbomFile -Algorithm SHA256).Hash.ToLower()
41- "$hash epplus-${{ env.VERSION }}.sbom.json" | Out-File -FilePath "./sbom/epplus-${{ env.VERSION }}.sbom.json.sha256" -Encoding utf8NoBOM
42- shell : pwsh
43- # --- SBOM ---
4434
35+ - name : Restore dependencies
36+ run : dotnet restore ./src/EPPlus.sln
4537 - name : Build
4638 run : dotnet build ./src/EPPlus.sln --no-restore --configuration Release
4739 - name : Test
4840 run : dotnet test ./src/EPPlus.sln --no-build --verbosity normal --configuration Release
4941 - name : Install AzureSignTool
50- run : dotnet tool install --global AzureSignTool --version 6.0.0
42+ run : dotnet tool install --global AzureSignTool --version 6.0.0
5143 - name : Install NuGetKeyVaultSignTool
5244 run : dotnet tool install --global NuGetKeyVaultSignTool
5345
@@ -57,66 +49,93 @@ jobs:
5749 uses : Azure/login@v2
5850 with :
5951 creds : ' {"clientId":"${{ secrets.EPPLUS_CODE_SIGNING_APPLICATION_ID }}","clientSecret":"${{ secrets.EPPLUS_CODE_SIGNING_SECRET }}","subscriptionId":"${{ secrets.EPPLUS_CODE_SIGNING_SUBSCRIPTION_ID }}","tenantId":"${{ secrets.EPPLUS_CODE_SIGNING_TENENT_ID }}"}'
52+
53+ # --- Sign DLLs ---
6054 - name : Sign EPPlus.dll with AzureSignTool
6155 run : |
62- azuresigntool.exe sign -kvu ${{ secrets.EPPLUS_CODE_SIGNING_KEY_VAULT_URL}} -kvi ${{ secrets.EPPLUS_CODE_SIGNING_APPLICATION_ID }} -kvt ${{ secrets.EPPLUS_CODE_SIGNING_TENENT_ID }} -kvs ${{ secrets.EPPLUS_CODE_SIGNING_SECRET }} -kvc ${{ secrets.EPPLUS_CODE_SIGNING_CERTIFICATE_NAME }} -tr http://timestamp.globalsign.com/tsa/advanced -td sha256 ".\src\EPPlus\bin\Release\net9.0\EPPlus.dll"
63- azuresigntool.exe sign -kvu ${{ secrets.EPPLUS_CODE_SIGNING_KEY_VAULT_URL}} -kvi ${{ secrets.EPPLUS_CODE_SIGNING_APPLICATION_ID }} -kvt ${{ secrets.EPPLUS_CODE_SIGNING_TENENT_ID }} -kvs ${{ secrets.EPPLUS_CODE_SIGNING_SECRET }} -kvc ${{ secrets.EPPLUS_CODE_SIGNING_CERTIFICATE_NAME }} -tr http://timestamp.globalsign.com/tsa/advanced -td sha256 ".\src\EPPlus\bin\Release\net8.0\EPPlus.dll"
64- azuresigntool.exe sign -kvu ${{ secrets.EPPLUS_CODE_SIGNING_KEY_VAULT_URL}} -kvi ${{ secrets.EPPLUS_CODE_SIGNING_APPLICATION_ID }} -kvt ${{ secrets.EPPLUS_CODE_SIGNING_TENENT_ID }} -kvs ${{ secrets.EPPLUS_CODE_SIGNING_SECRET }} -kvc ${{ secrets.EPPLUS_CODE_SIGNING_CERTIFICATE_NAME }} -tr http://timestamp.globalsign.com/tsa/advanced -td sha256 ".\src\EPPlus\bin\Release\netstandard2.1\EPPlus.dll"
65- azuresigntool.exe sign -kvu ${{ secrets.EPPLUS_CODE_SIGNING_KEY_VAULT_URL}} -kvi ${{ secrets.EPPLUS_CODE_SIGNING_APPLICATION_ID }} -kvt ${{ secrets.EPPLUS_CODE_SIGNING_TENENT_ID }} -kvs ${{ secrets.EPPLUS_CODE_SIGNING_SECRET }} -kvc ${{ secrets.EPPLUS_CODE_SIGNING_CERTIFICATE_NAME }} -tr http://timestamp.globalsign.com/tsa/advanced -td sha256 ".\src\EPPlus\bin\Release\netstandard2.0\EPPlus.dll"
66- azuresigntool.exe sign -kvu ${{ secrets.EPPLUS_CODE_SIGNING_KEY_VAULT_URL}} -kvi ${{ secrets.EPPLUS_CODE_SIGNING_APPLICATION_ID }} -kvt ${{ secrets.EPPLUS_CODE_SIGNING_TENENT_ID }} -kvs ${{ secrets.EPPLUS_CODE_SIGNING_SECRET }} -kvc ${{ secrets.EPPLUS_CODE_SIGNING_CERTIFICATE_NAME }} -tr http://timestamp.globalsign.com/tsa/advanced -td sha256 ".\src\EPPlus\bin\Release\net462\EPPlus.dll"
67- azuresigntool.exe sign -kvu ${{ secrets.EPPLUS_CODE_SIGNING_KEY_VAULT_URL}} -kvi ${{ secrets.EPPLUS_CODE_SIGNING_APPLICATION_ID }} -kvt ${{ secrets.EPPLUS_CODE_SIGNING_TENENT_ID }} -kvs ${{ secrets.EPPLUS_CODE_SIGNING_SECRET }} -kvc ${{ secrets.EPPLUS_CODE_SIGNING_CERTIFICATE_NAME }} -tr http://timestamp.globalsign.com/tsa/advanced -td sha256 ".\src\EPPlus\bin\Release\net35\EPPlus.dll"
68- - name : Sign EPPlus.Interface.dll with AzureSignTool
56+ $tfms = "${{ env.TFMS }}" -split ";"
57+ foreach ($tfm in $tfms) {
58+ $tfm = $tfm.Trim()
59+ if ([string]::IsNullOrEmpty($tfm)) { continue }
60+ $dll = ".\src\EPPlus\bin\Release\$tfm\EPPlus.dll"
61+ Write-Host "Signing $dll"
62+ azuresigntool.exe sign -kvu ${{ secrets.EPPLUS_CODE_SIGNING_KEY_VAULT_URL }} -kvi ${{ secrets.EPPLUS_CODE_SIGNING_APPLICATION_ID }} -kvt ${{ secrets.EPPLUS_CODE_SIGNING_TENENT_ID }} -kvs ${{ secrets.EPPLUS_CODE_SIGNING_SECRET }} -kvc ${{ secrets.EPPLUS_CODE_SIGNING_CERTIFICATE_NAME }} -tr http://timestamp.globalsign.com/tsa/advanced -td sha256 "$dll"
63+ }
64+ shell : pwsh
65+ - name : Sign EPPlus.Interfaces.dll with AzureSignTool
6966 run : |
70- azuresigntool.exe sign -kvu ${{ secrets.EPPLUS_CODE_SIGNING_KEY_VAULT_URL}} -kvi ${{ secrets.EPPLUS_CODE_SIGNING_APPLICATION_ID }} -kvt ${{ secrets.EPPLUS_CODE_SIGNING_TENENT_ID }} -kvs ${{ secrets.EPPLUS_CODE_SIGNING_SECRET }} -kvc ${{ secrets.EPPLUS_CODE_SIGNING_CERTIFICATE_NAME }} -tr http://timestamp.globalsign.com/tsa/advanced -td sha256 ".\src\EPPlus.Interfaces\bin\Release\net9.0\EPPlus.Interfaces.dll"
71- azuresigntool.exe sign -kvu ${{ secrets.EPPLUS_CODE_SIGNING_KEY_VAULT_URL}} -kvi ${{ secrets.EPPLUS_CODE_SIGNING_APPLICATION_ID }} -kvt ${{ secrets.EPPLUS_CODE_SIGNING_TENENT_ID }} -kvs ${{ secrets.EPPLUS_CODE_SIGNING_SECRET }} -kvc ${{ secrets.EPPLUS_CODE_SIGNING_CERTIFICATE_NAME }} -tr http://timestamp.globalsign.com/tsa/advanced -td sha256 ".\src\EPPlus.Interfaces\bin\Release\net8.0\EPPlus.Interfaces.dll"
72- azuresigntool.exe sign -kvu ${{ secrets.EPPLUS_CODE_SIGNING_KEY_VAULT_URL}} -kvi ${{ secrets.EPPLUS_CODE_SIGNING_APPLICATION_ID }} -kvt ${{ secrets.EPPLUS_CODE_SIGNING_TENENT_ID }} -kvs ${{ secrets.EPPLUS_CODE_SIGNING_SECRET }} -kvc ${{ secrets.EPPLUS_CODE_SIGNING_CERTIFICATE_NAME }} -tr http://timestamp.globalsign.com/tsa/advanced -td sha256 ".\src\EPPlus.Interfaces\bin\Release\netstandard2.1\EPPlus.Interfaces.dll"
73- azuresigntool.exe sign -kvu ${{ secrets.EPPLUS_CODE_SIGNING_KEY_VAULT_URL}} -kvi ${{ secrets.EPPLUS_CODE_SIGNING_APPLICATION_ID }} -kvt ${{ secrets.EPPLUS_CODE_SIGNING_TENENT_ID }} -kvs ${{ secrets.EPPLUS_CODE_SIGNING_SECRET }} -kvc ${{ secrets.EPPLUS_CODE_SIGNING_CERTIFICATE_NAME }} -tr http://timestamp.globalsign.com/tsa/advanced -td sha256 ".\src\EPPlus.Interfaces\bin\Release\netstandard2.0\EPPlus.Interfaces.dll"
74- azuresigntool.exe sign -kvu ${{ secrets.EPPLUS_CODE_SIGNING_KEY_VAULT_URL}} -kvi ${{ secrets.EPPLUS_CODE_SIGNING_APPLICATION_ID }} -kvt ${{ secrets.EPPLUS_CODE_SIGNING_TENENT_ID }} -kvs ${{ secrets.EPPLUS_CODE_SIGNING_SECRET }} -kvc ${{ secrets.EPPLUS_CODE_SIGNING_CERTIFICATE_NAME }} -tr http://timestamp.globalsign.com/tsa/advanced -td sha256 ".\src\EPPlus.Interfaces\bin\Release\net462\EPPlus.Interfaces.dll"
75- azuresigntool.exe sign -kvu ${{ secrets.EPPLUS_CODE_SIGNING_KEY_VAULT_URL}} -kvi ${{ secrets.EPPLUS_CODE_SIGNING_APPLICATION_ID }} -kvt ${{ secrets.EPPLUS_CODE_SIGNING_TENENT_ID }} -kvs ${{ secrets.EPPLUS_CODE_SIGNING_SECRET }} -kvc ${{ secrets.EPPLUS_CODE_SIGNING_CERTIFICATE_NAME }} -tr http://timestamp.globalsign.com/tsa/advanced -td sha256 ".\src\EPPlus.Interfaces\bin\Release\net35\EPPlus.Interfaces.dll"
67+ $tfms = "${{ env.TFMS }}" -split ";"
68+ foreach ($tfm in $tfms) {
69+ $tfm = $tfm.Trim()
70+ if ([string]::IsNullOrEmpty($tfm)) { continue }
71+ $dll = ".\src\EPPlus.Interfaces\bin\Release\$tfm\EPPlus.Interfaces.dll"
72+ Write-Host "Signing $dll"
73+ azuresigntool.exe sign -kvu ${{ secrets.EPPLUS_CODE_SIGNING_KEY_VAULT_URL }} -kvi ${{ secrets.EPPLUS_CODE_SIGNING_APPLICATION_ID }} -kvt ${{ secrets.EPPLUS_CODE_SIGNING_TENENT_ID }} -kvs ${{ secrets.EPPLUS_CODE_SIGNING_SECRET }} -kvc ${{ secrets.EPPLUS_CODE_SIGNING_CERTIFICATE_NAME }} -tr http://timestamp.globalsign.com/tsa/advanced -td sha256 "$dll"
74+ }
75+ shell : pwsh
7676 - name : Sign EPPlus.System.Drawing.dll with AzureSignTool
7777 run : |
78- azuresigntool.exe sign -kvu ${{ secrets.EPPLUS_CODE_SIGNING_KEY_VAULT_URL}} -kvi ${{ secrets.EPPLUS_CODE_SIGNING_APPLICATION_ID }} -kvt ${{ secrets.EPPLUS_CODE_SIGNING_TENENT_ID }} -kvs ${{ secrets.EPPLUS_CODE_SIGNING_SECRET }} -kvc ${{ secrets.EPPLUS_CODE_SIGNING_CERTIFICATE_NAME }} -tr http://timestamp.globalsign.com/tsa/advanced -td sha256 ".\src\EPPlus.System.Drawing\bin\Release\net9.0\EPPlus.System.Drawing.dll"
79- azuresigntool.exe sign -kvu ${{ secrets.EPPLUS_CODE_SIGNING_KEY_VAULT_URL}} -kvi ${{ secrets.EPPLUS_CODE_SIGNING_APPLICATION_ID }} -kvt ${{ secrets.EPPLUS_CODE_SIGNING_TENENT_ID }} -kvs ${{ secrets.EPPLUS_CODE_SIGNING_SECRET }} -kvc ${{ secrets.EPPLUS_CODE_SIGNING_CERTIFICATE_NAME }} -tr http://timestamp.globalsign.com/tsa/advanced -td sha256 ".\src\EPPlus.System.Drawing\bin\Release\net8.0\EPPlus.System.Drawing.dll"
80- azuresigntool.exe sign -kvu ${{ secrets.EPPLUS_CODE_SIGNING_KEY_VAULT_URL}} -kvi ${{ secrets.EPPLUS_CODE_SIGNING_APPLICATION_ID }} -kvt ${{ secrets.EPPLUS_CODE_SIGNING_TENENT_ID }} -kvs ${{ secrets.EPPLUS_CODE_SIGNING_SECRET }} -kvc ${{ secrets.EPPLUS_CODE_SIGNING_CERTIFICATE_NAME }} -tr http://timestamp.globalsign.com/tsa/advanced -td sha256 ".\src\EPPlus.System.Drawing\bin\Release\netstandard2.1\EPPlus.System.Drawing.dll"
81- azuresigntool.exe sign -kvu ${{ secrets.EPPLUS_CODE_SIGNING_KEY_VAULT_URL}} -kvi ${{ secrets.EPPLUS_CODE_SIGNING_APPLICATION_ID }} -kvt ${{ secrets.EPPLUS_CODE_SIGNING_TENENT_ID }} -kvs ${{ secrets.EPPLUS_CODE_SIGNING_SECRET }} -kvc ${{ secrets.EPPLUS_CODE_SIGNING_CERTIFICATE_NAME }} -tr http://timestamp.globalsign.com/tsa/advanced -td sha256 ".\src\EPPlus.System.Drawing\bin\Release\netstandard2.0\EPPlus.System.Drawing.dll"
82- azuresigntool.exe sign -kvu ${{ secrets.EPPLUS_CODE_SIGNING_KEY_VAULT_URL}} -kvi ${{ secrets.EPPLUS_CODE_SIGNING_APPLICATION_ID }} -kvt ${{ secrets.EPPLUS_CODE_SIGNING_TENENT_ID }} -kvs ${{ secrets.EPPLUS_CODE_SIGNING_SECRET }} -kvc ${{ secrets.EPPLUS_CODE_SIGNING_CERTIFICATE_NAME }} -tr http://timestamp.globalsign.com/tsa/advanced -td sha256 ".\src\EPPlus.System.Drawing\bin\Release\net462\EPPlus.System.Drawing.dll"
83- azuresigntool.exe sign -kvu ${{ secrets.EPPLUS_CODE_SIGNING_KEY_VAULT_URL}} -kvi ${{ secrets.EPPLUS_CODE_SIGNING_APPLICATION_ID }} -kvt ${{ secrets.EPPLUS_CODE_SIGNING_TENENT_ID }} -kvs ${{ secrets.EPPLUS_CODE_SIGNING_SECRET }} -kvc ${{ secrets.EPPLUS_CODE_SIGNING_CERTIFICATE_NAME }} -tr http://timestamp.globalsign.com/tsa/advanced -td sha256 ".\src\EPPlus.System.Drawing\bin\Release\net35\EPPlus.System.Drawing.dll"
78+ $tfms = "${{ env.TFMS }}" -split ";"
79+ foreach ($tfm in $tfms) {
80+ $tfm = $tfm.Trim()
81+ if ([string]::IsNullOrEmpty($tfm)) { continue }
82+ $dll = ".\src\EPPlus.System.Drawing\bin\Release\$tfm\EPPlus.System.Drawing.dll"
83+ Write-Host "Signing $dll"
84+ azuresigntool.exe sign -kvu ${{ secrets.EPPLUS_CODE_SIGNING_KEY_VAULT_URL }} -kvi ${{ secrets.EPPLUS_CODE_SIGNING_APPLICATION_ID }} -kvt ${{ secrets.EPPLUS_CODE_SIGNING_TENENT_ID }} -kvs ${{ secrets.EPPLUS_CODE_SIGNING_SECRET }} -kvc ${{ secrets.EPPLUS_CODE_SIGNING_CERTIFICATE_NAME }} -tr http://timestamp.globalsign.com/tsa/advanced -td sha256 "$dll"
85+ }
86+ shell : pwsh
87+ # --- Sign DLLs ---
88+
8489 - name : Pack NuGet package
8590 run : dotnet pack ./src/EPPlus.sln --configuration Release --output ./output
8691 - name : Sign NuGet package
8792 run : |
88- NuGetKeyVaultSignTool.exe sign -kvu ${{ secrets.EPPLUS_CODE_SIGNING_KEY_VAULT_URL}} -kvc ${{ secrets.EPPLUS_CODE_SIGNING_CERTIFICATE_NAME }} -kvi ${{ secrets.EPPLUS_CODE_SIGNING_APPLICATION_ID }} -kvs ${{ secrets.EPPLUS_CODE_SIGNING_SECRET }} -kvt ${{ secrets.EPPLUS_CODE_SIGNING_TENENT_ID }} -tr http://timestamp.globalsign.com/tsa/advanced -fd sha256 -td sha256 -own EPPlusSoftware ".\output\*.nupkg"
93+ NuGetKeyVaultSignTool.exe sign -kvu ${{ secrets.EPPLUS_CODE_SIGNING_KEY_VAULT_URL }} -kvc ${{ secrets.EPPLUS_CODE_SIGNING_CERTIFICATE_NAME }} -kvi ${{ secrets.EPPLUS_CODE_SIGNING_APPLICATION_ID }} -kvs ${{ secrets.EPPLUS_CODE_SIGNING_SECRET }} -kvt ${{ secrets.EPPLUS_CODE_SIGNING_TENENT_ID }} -tr http://timestamp.globalsign.com/tsa/advanced -fd sha256 -td sha256 -own EPPlusSoftware ".\output\*.nupkg"
8994 - name : Upload NuGet package as artifact
9095 uses : actions/upload-artifact@v4
9196 with :
92- name : signed-nuget-package
93- path : ./output/*.nupkg
94- # --- SBOM ---
95- - name : Upload SBOM to Azure Blob Storage
97+ name : signed-nuget-package
98+ path : ./output/*.nupkg
99+
100+ # --- SBOM (after build to avoid CycloneDX overwriting project.assets.json) ---
101+ - name : Install CycloneDX
102+ run : dotnet tool install --global CycloneDX
103+ - name : Generate combined SBOM
104+ run : dotnet CycloneDX ./src/EPPlus/EPPlus.csproj -o ./sbom -F Json -st Library -sv ${{ env.VERSION }} -fn epplus-${{ env.VERSION }}.sbom.json -imp ./src/EPPlus/sbom-metadata-template.xml
105+ - name : Generate per-TFM SBOMs
96106 run : |
97- az storage blob upload `
98- --account-name eppluswebprod `
99- --container-name sbom `
100- --name epplus-${{ env.VERSION }}.sbom.json `
101- --file ./sbom/epplus-${{ env.VERSION }}.sbom.json `
102- --auth-mode login `
103- --overwrite
107+ $tfms = "${{ env.TFMS }}" -split ";"
108+ foreach ($tfm in $tfms) {
109+ $tfm = $tfm.Trim()
110+ if ([string]::IsNullOrEmpty($tfm)) { continue }
111+ Write-Host "Generating SBOM for $tfm"
112+ dotnet CycloneDX ./src/EPPlus/EPPlus.csproj -o ./sbom -F Json -st Library -sv ${{ env.VERSION }} -fn "epplus-${{ env.VERSION }}.$tfm.sbom.json" -imp ./src/EPPlus/sbom-metadata-template.xml --framework $tfm
113+ }
104114 shell : pwsh
105- - name : Upload SBOM checksum to Azure Blob Storage
115+ - name : Generate SHA-256 checksums for all SBOMs
106116 run : |
107- az storage blob upload `
108- --account-name eppluswebprod `
109- --container-name sbom `
110- --name epplus-${{ env.VERSION }}.sbom.json.sha256 `
111- --file ./sbom/epplus-${{ env.VERSION }}.sbom.json.sha256 `
112- --auth-mode login `
113- --overwrite
117+ Get-ChildItem -Path "./sbom" -Filter "*.sbom.json" | ForEach-Object {
118+ $hash = (Get-FileHash -Path $_.FullName -Algorithm SHA256).Hash.ToLower()
119+ "$hash $($_.Name)" | Out-File -FilePath "$($_.FullName).sha256" -Encoding utf8NoBOM
120+ Write-Host "Checksum generated for $($_.Name): $hash"
121+ }
114122 shell : pwsh
115- - name : Upload SBOM as artifact
123+ - name : Upload all SBOMs to Azure Blob Storage
124+ run : |
125+ Get-ChildItem -Path "./sbom" | ForEach-Object {
126+ Write-Host "Uploading $($_.Name)"
127+ az storage blob upload `
128+ --account-name eppluswebprod `
129+ --container-name sbom `
130+ --name $_.Name `
131+ --file $_.FullName `
132+ --auth-mode login `
133+ --overwrite
134+ }
135+ shell : pwsh
136+ - name : Upload all SBOMs as artifact
116137 uses : actions/upload-artifact@v4
117138 with :
118- name : sbom
119- path : |
120- ./sbom/epplus-${{ env.VERSION }}.sbom.json
121- ./sbom/epplus-${{ env.VERSION }}.sbom.json.sha256
122- # --- SBOM ---
139+ name : sbom
140+ path : ./sbom/
141+ # --- SBOM ---
0 commit comments