فهرست منبع

feat: Add SBOM generation workflow using Anchore SBOM Action (#3455)

Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Alex Meijer <ameijer@users.noreply.github.com>
Warwick 5 ماه پیش
والد
کامیت
75f959675d
1فایلهای تغییر یافته به همراه180 افزوده شده و 0 حذف شده
  1. 180 0
      .github/workflows/sbom.yml

+ 180 - 0
.github/workflows/sbom.yml

@@ -0,0 +1,180 @@
+name: Generate SBOM
+
+on:
+  workflow_run:
+    workflows: ["Build and Publish Release"]
+    types:
+      - completed
+  workflow_dispatch:
+    inputs:
+      release_version:
+        description: "Version of the release to generate SBOM for"
+        required: true
+  pull_request:
+    branches:
+      - develop
+
+permissions: read-all
+
+concurrency:
+  group: sbom-${{ github.ref }}
+  cancel-in-progress: true
+
+env:
+  REGISTRY: ghcr.io
+
+jobs:
+  generate-sbom:
+    runs-on: ubuntu-latest
+    if: github.event_name != 'workflow_run' || github.event.workflow_run.conclusion == 'success'
+    permissions:
+      contents: ${{ github.event_name == 'pull_request' && 'read' || 'write' }}
+      actions: read
+      packages: read
+    steps:
+      - name: Checkout Repo (for version detection)
+        if: github.event_name == 'workflow_run'
+        uses: actions/checkout@v4
+        with:
+          fetch-depth: 0
+
+      - name: Get Version From Workflow Run
+        id: tag
+        if: github.event_name == 'workflow_run'
+        run: |
+          # Get the SHA from the workflow run
+          SHA="${{ github.event.workflow_run.head_sha }}"
+          # Find the tag pointing to this SHA
+          TAG=$(git tag --points-at $SHA | grep '^v[0-9]' | head -1)
+          if [ -z "$TAG" ]; then
+            echo "Error: No version tag found for SHA $SHA"
+            exit 1
+          fi
+          echo "TRIGGERED_TAG=$TAG" >> $GITHUB_ENV
+          echo "Found tag: $TAG"
+
+      - name: Determine Version Number
+        id: version_number
+        run: |
+          if [ "${{ github.event_name }}" == "workflow_run" ]; then
+            version=$TRIGGERED_TAG
+          elif [ -n "${{ inputs.release_version }}" ]; then
+            version=${{ inputs.release_version }}
+          else
+            version=""
+          fi
+          if [[ ${version:0:1} == "v" ]];
+          then
+            echo "RELEASE_VERSION=${version:1}" >> $GITHUB_OUTPUT
+          else
+            echo "RELEASE_VERSION=$version" >> $GITHUB_OUTPUT
+          fi
+
+      - name: Make Branch Name
+        id: branch
+        if: github.event_name != 'pull_request'
+        env:
+          RELEASE_VERSION: ${{ steps.version_number.outputs.RELEASE_VERSION }}
+        run: |
+          echo "BRANCH_NAME=v${RELEASE_VERSION%.*}" >> $GITHUB_OUTPUT
+
+      - name: Checkout Repo
+        if: github.event_name != 'workflow_run'
+        uses: actions/checkout@v4
+        with:
+          ref: ${{ github.event_name != 'pull_request' && steps.branch.outputs.BRANCH_NAME || '' }}
+
+      - name: Set OpenCost Image Tag
+        id: image_tag
+        if: github.event_name != 'pull_request'
+        env:
+          REPO_OWNER: ${{ github.repository_owner }}
+          RELEASE_VERSION: ${{ steps.version_number.outputs.RELEASE_VERSION }}
+        run: |
+          echo "IMAGE_TAG=ghcr.io/$REPO_OWNER/opencost:$RELEASE_VERSION" >> $GITHUB_OUTPUT
+
+      # Generate SBOM for source code
+      - name: Generate SBOM for Source Code
+        uses: anchore/sbom-action@v0
+        with:
+          path: .
+          artifact-name: opencost-source-sbom.spdx.json
+          output-file: opencost-source-sbom.spdx.json
+          format: spdx-json
+
+      # Generate SBOM for container image
+      - name: Generate SBOM for Container Image
+        if: github.event_name != 'pull_request'
+        uses: anchore/sbom-action@v0
+        with:
+          image: ${{ steps.image_tag.outputs.IMAGE_TAG }}
+          artifact-name: opencost-container-sbom.spdx.json
+          output-file: opencost-container-sbom.spdx.json
+          format: spdx-json
+
+      # Generate CycloneDX format as well for broader compatibility
+      - name: Generate CycloneDX SBOM for Source Code
+        uses: anchore/sbom-action@v0
+        with:
+          path: .
+          artifact-name: opencost-source-sbom.cyclonedx.json
+          output-file: opencost-source-sbom.cyclonedx.json
+          format: cyclonedx-json
+
+      # Display SBOM contents on PRs for review
+      - name: Display SBOM Contents
+        if: github.event_name == 'pull_request'
+        run: |
+          echo "## SBOM Contents (SPDX Format)" >> $GITHUB_STEP_SUMMARY
+          echo "" >> $GITHUB_STEP_SUMMARY
+          echo "### Package Count" >> $GITHUB_STEP_SUMMARY
+          PACKAGE_COUNT=$(jq '.packages | length' opencost-source-sbom.spdx.json)
+          echo "Total packages: $PACKAGE_COUNT" >> $GITHUB_STEP_SUMMARY
+          echo "" >> $GITHUB_STEP_SUMMARY
+          echo "### Top-level Packages" >> $GITHUB_STEP_SUMMARY
+          echo '```' >> $GITHUB_STEP_SUMMARY
+          jq -r '.packages[] | select(.name != null) | "\(.name) - \(.versionInfo // "unknown")"' opencost-source-sbom.spdx.json | head -50 >> $GITHUB_STEP_SUMMARY
+          echo '```' >> $GITHUB_STEP_SUMMARY
+          echo "" >> $GITHUB_STEP_SUMMARY
+          echo "<details>" >> $GITHUB_STEP_SUMMARY
+          echo "<summary>Full SPDX SBOM (click to expand)</summary>" >> $GITHUB_STEP_SUMMARY
+          echo "" >> $GITHUB_STEP_SUMMARY
+          echo '```json' >> $GITHUB_STEP_SUMMARY
+          cat opencost-source-sbom.spdx.json >> $GITHUB_STEP_SUMMARY
+          echo '```' >> $GITHUB_STEP_SUMMARY
+          echo "</details>" >> $GITHUB_STEP_SUMMARY
+
+      - name: Generate CycloneDX SBOM for Container Image
+        if: github.event_name != 'pull_request'
+        uses: anchore/sbom-action@v0
+        with:
+          image: ${{ steps.image_tag.outputs.IMAGE_TAG }}
+          artifact-name: opencost-container-sbom.cyclonedx.json
+          output-file: opencost-container-sbom.cyclonedx.json
+          format: cyclonedx-json
+
+      # Publish SBOMs to GitHub release (only for releases, not PRs)
+      - name: Attach SBOMs to GitHub Release
+        if: github.event_name != 'pull_request'
+        uses: anchore/sbom-action/publish-sbom@v0
+        with:
+          sbom-artifact-match: ".*\\.spdx\\.json$|.*\\.cyclonedx\\.json$"
+
+      # Create a summary of the SBOM generation
+      - name: Generate Summary
+        run: |
+          echo "## SBOM Generation Summary" >> $GITHUB_STEP_SUMMARY
+          echo "" >> $GITHUB_STEP_SUMMARY
+          echo "✅ Generated SBOMs for OpenCost ${{ steps.version_number.outputs.RELEASE_VERSION || 'PR build' }}" >> $GITHUB_STEP_SUMMARY
+          echo "" >> $GITHUB_STEP_SUMMARY
+          echo "### Generated Artifacts:" >> $GITHUB_STEP_SUMMARY
+          echo "- Source Code SBOM (SPDX)" >> $GITHUB_STEP_SUMMARY
+          echo "- Source Code SBOM (CycloneDX)" >> $GITHUB_STEP_SUMMARY
+          if [ "${{ github.event_name }}" != "pull_request" ]; then
+            echo "- Container Image SBOM (SPDX)" >> $GITHUB_STEP_SUMMARY
+            echo "- Container Image SBOM (CycloneDX)" >> $GITHUB_STEP_SUMMARY
+          fi
+          echo "" >> $GITHUB_STEP_SUMMARY
+          if [ "${{ github.event_name }}" != "pull_request" ]; then
+            echo "📦 SBOMs have been attached to the GitHub release" >> $GITHUB_STEP_SUMMARY
+          fi