diff --git a/.config/sbom-tool/dotnet-tools.json b/.config/sbom-tool/dotnet-tools.json new file mode 100644 index 0000000..eabcff8 --- /dev/null +++ b/.config/sbom-tool/dotnet-tools.json @@ -0,0 +1,13 @@ +{ + "version": 1, + "isRoot": true, + "tools": { + "microsoft.sbom.dotnettool": { + "version": "4.1.5", + "commands": [ + "sbom-tool" + ], + "rollForward": true + } + } +} diff --git a/.github/workflows/sbom.yml b/.github/workflows/sbom.yml new file mode 100644 index 0000000..a69a297 --- /dev/null +++ b/.github/workflows/sbom.yml @@ -0,0 +1,107 @@ +name: Generate SBOM + +# Runs when the 'generate sbom' label is added to a PR, or when a release is published. +# Uses the sbom-tool CLI (version pinned in .config/sbom-tool/dotnet-tools.json) +# so the NuGet package is never modified and no SBOM is embedded in it. +on: + pull_request: + types: [labeled] + release: + types: [published] + +permissions: + contents: read + +concurrency: + group: sbom-${{ github.event_name == 'release' && github.ref_name || github.event.pull_request.number }} + cancel-in-progress: true + +env: + BUILD_CONFIGURATION: Release + DOTNET_VERSION: '9.x' + PACKAGE_VERSION: ${{ github.event_name == 'release' && github.ref_name || format('0.0.0-pr.{0}', github.event.pull_request.number) }} + +jobs: + sbom: + if: github.event_name == 'release' || github.event.label.name == 'generate sbom' + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7 + + - name: Setup .NET + uses: actions/setup-dotnet@26b0ec14cb23fa6904739307f278c14f94c95bf1 # v5.4.0 + with: + dotnet-version: ${{ env.DOTNET_VERSION }} + + - name: Pack NuGet package + run: > + dotnet pack Infragistics.QueryBuilder.Executor.csproj + --configuration ${{ env.BUILD_CONFIGURATION }} + -p:Version=${{ env.PACKAGE_VERSION }} + -o ./artifacts + + # Dedicated nested manifest keeps sbom-tool out of the root 'dotnet tool restore' used by build-and-publish.yml + - name: Restore sbom-tool (pinned) + run: dotnet tool restore --tool-manifest .config/sbom-tool/dotnet-tools.json + + # -b: the shipped artifact (nupkg) gets listed with its hash in the SBOM's files section + # -bc: dependency detection scans the repo root (csproj) + - name: Generate SBOM + working-directory: .config/sbom-tool + run: > + dotnet tool run sbom-tool -- generate + -b ${{ github.workspace }}/artifacts + -bc ${{ github.workspace }} + -pn Infragistics.QueryBuilder.Executor + -pv ${{ env.PACKAGE_VERSION }} + -ps "Infragistics Inc." + -nsb http://spdx.org/spdxdocs/Infragistics.QueryBuilder.Executor + -V Information + + - name: Verify SBOM + run: | + set -euo pipefail + test -s artifacts/_manifest/spdx_2.2/manifest.spdx.json + test -s artifacts/_manifest/spdx_2.2/manifest.spdx.json.sha256 + echo "SBOM generated successfully." + + - name: Upload NuGet package + uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0 + with: + name: nupkg + path: artifacts/*.nupkg + retention-days: 1 + if-no-files-found: error + + - name: Upload SBOM files + uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0 + with: + name: sbom-spdx_2.2 + path: artifacts/_manifest/spdx_2.2 + retention-days: 1 + if-no-files-found: error + + attach-to-release: + if: github.event_name == 'release' + needs: sbom + runs-on: ubuntu-latest + permissions: + contents: write # required to upload release assets + + steps: + - name: Download SBOM artifact + uses: actions/download-artifact@70fc10c6e5e1ce46ad2ea6f2b72d43f7d47b13c3 # v8.0.0 + with: + name: sbom-spdx_2.2 + path: spdx_2.2 + + - name: Attach SBOM to release + env: + GH_TOKEN: ${{ github.token }} + TAG: ${{ github.ref_name }} + run: | + set -euo pipefail + asset="Infragistics.QueryBuilder.Executor.${TAG}.spdx_2.2.zip" + (cd spdx_2.2 && zip -r "../${asset}" .) + gh release upload "$TAG" "$asset" --clobber -R "${{ github.repository }}"