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 "
" >> $GITHUB_STEP_SUMMARY echo "Full SPDX SBOM (click to expand)" >> $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 "
" >> $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