2
0

sbom.yml 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197
  1. name: Generate SBOM
  2. on:
  3. workflow_run:
  4. workflows: ["Build and Publish Release"]
  5. types:
  6. - completed
  7. workflow_dispatch:
  8. inputs:
  9. release_version:
  10. description: "Version of the release to generate SBOM for"
  11. required: true
  12. pull_request:
  13. branches:
  14. - develop
  15. permissions: read-all
  16. concurrency:
  17. group: sbom-${{ github.ref }}
  18. cancel-in-progress: true
  19. env:
  20. REGISTRY: ghcr.io
  21. jobs:
  22. generate-sbom:
  23. runs-on: ubuntu-latest
  24. if: github.event_name != 'workflow_run' || github.event.workflow_run.conclusion == 'success'
  25. permissions:
  26. contents: write
  27. actions: read
  28. packages: read
  29. steps:
  30. - name: Checkout Repo (for version detection)
  31. if: github.event_name == 'workflow_run'
  32. uses: actions/checkout@v6.0.2
  33. with:
  34. fetch-depth: 0
  35. - name: Get Version From Workflow Run
  36. id: tag
  37. if: github.event_name == 'workflow_run'
  38. run: |
  39. # Get the SHA from the workflow run
  40. SHA="${{ github.event.workflow_run.head_sha }}"
  41. # Find the tag pointing to this SHA
  42. TAG=$(git tag --points-at $SHA | grep -E '^v[0-9]+\.[0-9]+\.[0-9]+$' | head -1)
  43. if [ -z "$TAG" ]; then
  44. echo "Error: No version tag found for SHA $SHA"
  45. exit 1
  46. fi
  47. echo "TRIGGERED_TAG=$TAG" >> $GITHUB_ENV
  48. echo "Found tag: $TAG"
  49. - name: Determine Version Number
  50. id: version_number
  51. run: |
  52. if [ "${{ github.event_name }}" == "workflow_run" ]; then
  53. version=$TRIGGERED_TAG
  54. elif [ -n "${{ inputs.release_version }}" ]; then
  55. version=${{ inputs.release_version }}
  56. else
  57. version=""
  58. fi
  59. if [[ ${version:0:1} == "v" ]];
  60. then
  61. echo "RELEASE_VERSION=${version:1}" >> $GITHUB_OUTPUT
  62. else
  63. echo "RELEASE_VERSION=$version" >> $GITHUB_OUTPUT
  64. fi
  65. - name: Make Branch Name
  66. id: branch
  67. if: github.event_name != 'pull_request'
  68. env:
  69. RELEASE_VERSION: ${{ steps.version_number.outputs.RELEASE_VERSION }}
  70. run: |
  71. echo "BRANCH_NAME=v${RELEASE_VERSION%.*}" >> $GITHUB_OUTPUT
  72. - name: Checkout Repo
  73. if: github.event_name != 'workflow_run'
  74. uses: actions/checkout@v6.0.2
  75. with:
  76. ref: ${{ github.event_name != 'pull_request' && steps.branch.outputs.BRANCH_NAME || '' }}
  77. - name: Set OpenCost Image Tag
  78. id: image_tag
  79. if: github.event_name != 'pull_request'
  80. env:
  81. REPO_OWNER: ${{ github.repository_owner }}
  82. RELEASE_VERSION: ${{ steps.version_number.outputs.RELEASE_VERSION }}
  83. run: |
  84. echo "IMAGE_TAG=ghcr.io/$REPO_OWNER/opencost:$RELEASE_VERSION" >> $GITHUB_OUTPUT
  85. # Generate SBOM for source code using Trivy
  86. - name: Run Trivy SBOM for Source Code (SPDX)
  87. uses: aquasecurity/trivy-action@master
  88. with:
  89. scan-type: 'fs'
  90. scan-ref: '.'
  91. format: 'spdx-json'
  92. output: 'opencost-source-sbom.spdx.json'
  93. - name: Run Trivy SBOM for Source Code (CycloneDX)
  94. uses: aquasecurity/trivy-action@master
  95. with:
  96. scan-type: 'fs'
  97. scan-ref: '.'
  98. format: 'cyclonedx'
  99. output: 'opencost-source-sbom.cyclonedx.json'
  100. # Generate SBOM for container image using Trivy
  101. - name: Run Trivy SBOM for Container Image (SPDX)
  102. if: github.event_name != 'pull_request'
  103. uses: aquasecurity/trivy-action@master
  104. with:
  105. scan-type: 'image'
  106. image-ref: ${{ steps.image_tag.outputs.IMAGE_TAG }}
  107. format: 'spdx-json'
  108. output: 'opencost-container-sbom.spdx.json'
  109. - name: Run Trivy SBOM for Container Image (CycloneDX)
  110. if: github.event_name != 'pull_request'
  111. uses: aquasecurity/trivy-action@master
  112. with:
  113. scan-type: 'image'
  114. image-ref: ${{ steps.image_tag.outputs.IMAGE_TAG }}
  115. format: 'cyclonedx'
  116. output: 'opencost-container-sbom.cyclonedx.json'
  117. # Display SBOM contents on PRs for review
  118. - name: Display SBOM Contents
  119. if: github.event_name == 'pull_request'
  120. run: |
  121. echo "## SBOM Contents (SPDX Format)" >> $GITHUB_STEP_SUMMARY
  122. echo "" >> $GITHUB_STEP_SUMMARY
  123. echo "### Package Count" >> $GITHUB_STEP_SUMMARY
  124. PACKAGE_COUNT=$(jq '.packages | length' opencost-source-sbom.spdx.json)
  125. echo "Total packages: $PACKAGE_COUNT" >> $GITHUB_STEP_SUMMARY
  126. echo "" >> $GITHUB_STEP_SUMMARY
  127. echo "### Top-level Packages" >> $GITHUB_STEP_SUMMARY
  128. echo '```' >> $GITHUB_STEP_SUMMARY
  129. jq -r '.packages[] | select(.name != null) | "\(.name) - \(.versionInfo // "unknown")"' opencost-source-sbom.spdx.json | head -50 >> $GITHUB_STEP_SUMMARY
  130. echo '```' >> $GITHUB_STEP_SUMMARY
  131. echo "" >> $GITHUB_STEP_SUMMARY
  132. echo "<details>" >> $GITHUB_STEP_SUMMARY
  133. echo "<summary>Full SPDX SBOM (click to expand)</summary>" >> $GITHUB_STEP_SUMMARY
  134. echo "" >> $GITHUB_STEP_SUMMARY
  135. echo '```json' >> $GITHUB_STEP_SUMMARY
  136. cat opencost-source-sbom.spdx.json >> $GITHUB_STEP_SUMMARY
  137. echo '```' >> $GITHUB_STEP_SUMMARY
  138. echo "</details>" >> $GITHUB_STEP_SUMMARY
  139. # Upload SBOMs as artifacts
  140. - name: Upload SBOM Artifacts
  141. uses: actions/upload-artifact@v7
  142. with:
  143. name: sbom-files
  144. path: |
  145. opencost-source-sbom.spdx.json
  146. opencost-source-sbom.cyclonedx.json
  147. opencost-container-sbom.spdx.json
  148. opencost-container-sbom.cyclonedx.json
  149. if-no-files-found: ignore
  150. # Attach SBOMs to GitHub release (only for releases, not PRs)
  151. - name: Attach SBOMs to GitHub Release
  152. if: github.event_name != 'pull_request'
  153. uses: softprops/action-gh-release@v2
  154. with:
  155. tag_name: v${{ steps.version_number.outputs.RELEASE_VERSION }}
  156. files: |
  157. opencost-source-sbom.spdx.json
  158. opencost-source-sbom.cyclonedx.json
  159. opencost-container-sbom.spdx.json
  160. opencost-container-sbom.cyclonedx.json
  161. fail_on_unmatched_files: false
  162. # Create a summary of the SBOM generation
  163. - name: Generate Summary
  164. run: |
  165. echo "## SBOM Generation Summary" >> $GITHUB_STEP_SUMMARY
  166. echo "" >> $GITHUB_STEP_SUMMARY
  167. echo "✅ Generated SBOMs for OpenCost ${{ steps.version_number.outputs.RELEASE_VERSION || 'PR build' }}" >> $GITHUB_STEP_SUMMARY
  168. echo "" >> $GITHUB_STEP_SUMMARY
  169. echo "### Generated Artifacts:" >> $GITHUB_STEP_SUMMARY
  170. echo "- Source Code SBOM (SPDX)" >> $GITHUB_STEP_SUMMARY
  171. echo "- Source Code SBOM (CycloneDX)" >> $GITHUB_STEP_SUMMARY
  172. if [ "${{ github.event_name }}" != "pull_request" ]; then
  173. echo "- Container Image SBOM (SPDX)" >> $GITHUB_STEP_SUMMARY
  174. echo "- Container Image SBOM (CycloneDX)" >> $GITHUB_STEP_SUMMARY
  175. fi
  176. echo "" >> $GITHUB_STEP_SUMMARY
  177. if [ "${{ github.event_name }}" != "pull_request" ]; then
  178. echo "📦 SBOMs have been attached to the GitHub release" >> $GITHUB_STEP_SUMMARY
  179. fi