name: Generate SBOM

# This workflow uses cyclonedx/cdxgen and publishes an sbom.json artifact.
# It runs on manual trigger or when package files change on master branch,
# and creates a PR with the updated SBOM.
# Internal documentation: go/sbom-scope

on:
  workflow_dispatch: {}
  push:
    branches: ['master']
    paths:
      - 'Gemfile'
      - 'Gemfile.lock'
      - 'mongo.gemspec'

permissions:
  contents: write
  pull-requests: write

jobs:
  sbom:
    name: Generate SBOM and Create PR
    runs-on: ubuntu-latest
    concurrency:
      group: sbom-${{ github.ref }}
      cancel-in-progress: false

    steps:
      - name: Checkout repository
        uses: actions/checkout@v4
        with:
          persist-credentials: false

      - name: Set up Ruby
        uses: ruby/setup-ruby@v1
        with:
          ruby-version: "3.2"

      - name: Install dependencies
        run: bundle install --standalone

      - name: Generate SBOM
        run: |
          # Generate SBOM with license fetching and required-only flag
          FETCH_LICENSE=true npx -y -p "@cyclonedx/cdxgen@11.0.0" \
            cdxgen \
            --type ruby \
            --spec-version 1.5 \
            --required-only \
            --output sbom.json

          # Post-process SBOM: remove incompatible fields and fix licenses
          jq '
            # Remove incompatible fields for silkbomb compatibility
            del(.metadata.lifecycles) |
            walk(if type == "object" then del(.evidence) else . end) |

            # Fix missing licenses
            .components |= map(
              if .name == "yard-solargraph" and
                 (.licenses == null or .licenses == []) then
                . + {licenses: [{license: {
                  id: "MIT",
                  url: "https://opensource.org/licenses/MIT"
                }}]}
              else
                .
              end
            )
          ' sbom.json > sbom.tmp.json && mv sbom.tmp.json sbom.json

      - name: Download CycloneDX CLI
        run: |
          curl -L -s -o /tmp/cyclonedx \
            "https://github.com/CycloneDX/cyclonedx-cli/releases/download/v0.29.1/cyclonedx-linux-x64"
          chmod +x /tmp/cyclonedx

      - name: Validate SBOM
        run: |
          /tmp/cyclonedx validate --input-file sbom.json \
            --fail-on-errors

      - name: Cleanup vendor directory
        if: always()
        run: |
          # Remove vendor directory if it was created during bundle install
          rm -rf vendor/

      - name: Upload SBOM artifact
        uses: actions/upload-artifact@v4
        with:
          name: sbom
          path: sbom.json
          if-no-files-found: error

      - name: Create Pull Request
        # peter-evans/create-pull-request v7.0.6
        uses: peter-evans/create-pull-request@b4733b9419fd47bbfa1807b15627e17cd70b5b22
        with:
          token: ${{ secrets.GITHUB_TOKEN }}
          commit-message: 'chore: Update SBOM after dependency changes'
          branch: auto-update-sbom-${{ github.run_id }}
          delete-branch: true
          title: 'chore: Update SBOM'
          add-paths: |
            sbom.json
          body: |
            ## Automated SBOM Update

            This PR was automatically generated because dependency
            manifest files changed.

            ### Changes
            - Updated `sbom.json` to reflect current dependencies

            ### Verification
            The SBOM was generated using @cyclonedx/cdxgen v11.0.0 with
            the ruby type.

            ### Triggered by
            - Commit: ${{ github.sha }}
            - Workflow run: ${{ github.run_id }}

            ---
            _This PR was created automatically by the [SBOM workflow](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }})_
          labels: |
            sbom
            automated
            dependencies
