# This GitHub action is for publishing of the primary image for AnythingLLM
# It will publish a linux/amd64 and linux/arm64 image at the same time
# This file should ONLY BY USED FOR `master` BRANCH. 
# TODO: GitHub now has an ubuntu-24.04-arm64 runner, but we still need
# to use QEMU to build the arm64 image because Chromium is not available for Linux arm64
# so builds will still fail, or fail much more often. Its inconsistent and frustrating.
name: Publish AnythingLLM Primary Docker image (amd64/arm64)

concurrency:
  group: build-${{ github.ref }}
  cancel-in-progress: true

on:
  push:
    branches: ['master'] # master branch only. Do not modify.
    paths-ignore:
      - '**.md'
      - 'cloud-deployments/**/*'
      - 'images/**/*'
      - '.vscode/**/*'
      - '**/.env.example'
      - '.github/ISSUE_TEMPLATE/**/*'
      - '.devcontainer/**/*'
      - 'embed/**/*' # Embed is submodule
      - 'browser-extension/**/*' # Chrome extension is submodule
      - 'server/utils/agents/aibitat/example/**/*' # Do not push new image for local dev testing of new aibitat images.

jobs:
  push_multi_platform_to_registries:
    name: Push Docker multi-platform image to multiple registries
    runs-on: ubuntu-latest
    permissions:
      packages: write
      contents: read
    steps:
      - name: Check out the repo
        uses: actions/checkout@v4

      - name: Check if DockerHub build needed
        shell: bash
        run: |
          # Check if the secret for USERNAME is set (don't even check for the password)
          if [[ -z "${{ secrets.DOCKER_USERNAME }}" ]]; then
            echo "DockerHub build not needed"
            echo "enabled=false" >> $GITHUB_OUTPUT
          else
            echo "DockerHub build needed"
            echo "enabled=true" >> $GITHUB_OUTPUT
          fi
        id: dockerhub

      - name: Set up QEMU
        uses: docker/setup-qemu-action@v3

      - name: Set up Docker Buildx
        uses: docker/setup-buildx-action@v3
      
      - name: Log in to Docker Hub
        uses: docker/login-action@f4ef78c080cd8ba55a85445d5b36e214a81df20a
        # Only login to the Docker Hub if the repo is mintplex/anythingllm, to allow for forks to build on GHCR
        if: steps.dockerhub.outputs.enabled == 'true' 
        with:
          username: ${{ secrets.DOCKER_USERNAME }}
          password: ${{ secrets.DOCKER_PASSWORD }}
      
      - name: Log in to the Container registry
        uses: docker/login-action@65b78e6e13532edd9afa3aa52ac7964289d1a9c1
        with:
          registry: ghcr.io
          username: ${{ github.actor }}
          password: ${{ secrets.GITHUB_TOKEN }}
      
      - name: Extract metadata (tags, labels) for Docker
        id: meta
        uses: docker/metadata-action@9ec57ed1fcdbf14dcef7dfbe97b2010124a938b7
        with:
          images: |
            ${{ steps.dockerhub.outputs.enabled == 'true' && 'mintplexlabs/anythingllm' || '' }}
            ghcr.io/${{ github.repository }}
          tags: |
            type=raw,value=latest,enable={{is_default_branch}}
            type=ref,event=branch
            type=ref,event=tag
            type=ref,event=pr

      - name: Build and push multi-platform Docker image
        uses: docker/build-push-action@v6
        with:
          context: .
          file: ./docker/Dockerfile
          push: true
          sbom: true
          provenance: mode=max
          platforms: linux/amd64,linux/arm64
          tags: ${{ steps.meta.outputs.tags }}
          labels: ${{ steps.meta.outputs.labels }}
          cache-from: type=gha
          cache-to: type=gha,mode=max
      
      # For Docker scout there are some intermediary reported CVEs which exists outside
      # of execution content or are unreachable by an attacker but exist in image.
      # We create VEX files for these so they don't show in scout summary. 
      - name: Collect known and verified CVE exceptions
        id: cve-list
        run: |
          # Collect CVEs from filenames in vex folder
          CVE_NAMES=""
          for file in ./docker/vex/*.vex.json; do
            [ -e "$file" ] || continue
            filename=$(basename "$file")
            stripped_filename=${filename%.vex.json}
            CVE_NAMES+=" $stripped_filename"
          done
          echo "CVE_EXCEPTIONS=$CVE_NAMES" >> $GITHUB_OUTPUT
        shell: bash

      # About VEX attestations https://docs.docker.com/scout/explore/exceptions/
      # Justifications https://github.com/openvex/spec/blob/main/OPENVEX-SPEC.md#status-justifications
      - name: Add VEX attestations
        env:
          CVE_EXCEPTIONS: ${{ steps.cve-list.outputs.CVE_EXCEPTIONS }}
        run: |
          echo $CVE_EXCEPTIONS
          curl -sSfL https://raw.githubusercontent.com/docker/scout-cli/main/install.sh | sh -s --
          for cve in $CVE_EXCEPTIONS; do
            for tag in "${{ join(fromJSON(steps.meta.outputs.json).tags, ' ') }}"; do
              echo "Attaching VEX exception $cve to $tag"
              docker scout attestation add \
              --file "./docker/vex/$cve.vex.json" \
              --predicate-type https://openvex.dev/ns/v0.2.0 \
              $tag
            done
          done
        shell: bash