name: SBOM

on:
  workflow_dispatch:
  push:
    branches:
    - feature/*
    - fix/*
    - improvement/*
    - release/*
    - technical/*


permissions:
  contents: write

jobs:
  sbom:
    # Skip if the job was triggered by the SBOM commit or a merge commit in the latest push.
    if: "!contains(github.event.head_commit.message, 'Merge pull request') && !contains(github.event.head_commit.message, 'SBOM updated')"
    runs-on: ubuntu-latest

    steps:
      # Checkout the repository
      - name: Checkout repository
        uses: actions/checkout@v4
        with:
          # Parent commit to compare
          fetch-depth: 2
          persist-credentials: false


      # Cache Gradle dependencies to speed up future builds
      - name: Cache Gradle dependencies
        uses: actions/cache@v4
        with:
          path: |
            ~/.gradle/caches
            ~/.gradle/wrapper
            ~/.gradle/wrapper/dists/
          key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }}
          restore-keys: |
            ${{ runner.os }}-gradle-

      # Set up Java 17 (required by Gradle and CycloneDX plugin)
      - name: Set up JDK 17
        uses: actions/setup-java@v4
        with:
          java-version: '17'
          distribution: 'temurin'

      # Generate the Software Bill of Materials (SBOM) using CycloneDX Gradle plugin
      - name: Generate SBOM (CycloneDX)
        run: ./gradlew --no-daemon cyclonedxBom

      # Move the generated SBOM to the root and rename it
      - name: Move and rename SBOM to root
        run: mv build/reports/bom.json ./sbom.json

      # Install jq (JSON processor) for JSON manipulations
      - name: Install jq
        run: sudo apt-get update && sudo apt-get install -y jq

      # Creates script to normalize SBOM files to compare
      - name: Normalization script
        run: |
          cat <<'EOF' > normalize-sbom.sh
          #!/bin/bash
          jq -S '
            del(.serialNumber, .timestamp, .metadata.timestamp)
            | .components |= (if type=="array" then sort_by(.["bom-ref"] // "") else . end)
            | .dependencies |= (if type=="array" then sort_by(.ref // "") else . end)
          ' "$1" > "$2"
          EOF
          chmod +x normalize-sbom.sh

      # Compares with the HEAD to check if there are changes
      - name: Compare with previous SBOM
        id: compare
        run: |
          # Try HEAD first to compare with previous commit's sbom (HEAD~1)
          git show HEAD~1:sbom.json > sbom_prev.json 2>/dev/null || echo '{}' > sbom_prev.json

          ./normalize-sbom.sh sbom_prev.json sbom_prev_normalized.json
          ./normalize-sbom.sh sbom.json sbom_current_normalized.json

          if diff -q sbom_prev_normalized.json sbom_current_normalized.json; then
            echo "no_changes=true" >> $GITHUB_OUTPUT
            echo "No changes in SBOM"
          else
            echo "no_changes=false" >> $GITHUB_OUTPUT
            echo "Differences in SBOM"
            diff sbom_prev_normalized.json sbom_current_normalized.json || true
          fi

      # Commit the SBOM file only if it differs from master to avoid unnecessary commits
      - name: Commit and push updated SBOM
        if: steps.compare.outputs.no_changes == 'false'
        uses: GuillaumeFalourd/git-commit-push@v1.3
        with:
          commit_message: "docs: SBOM updated"
          files: sbom.json
          email: devops@owncloud.com
          name: ownClouders
          access_token: ${{ secrets.GH_PAT }}
