name: Build Bazzite
on:
  schedule:
    - cron: "40 4 * * 2,5" # 4:40 utc tues thurs
  pull_request:
    branches:
      - testing
      - unstable
    paths-ignore:
      - "**.md"
      - "**.txt"
      - "installer/**"
      - "repo_content/**"
      - "spec_files/**"
      - "post_install_files/**"
      - "press_kit/**"
      - ".github/workflows/build_iso.yml"
  push:
    branches:
      - testing
      - unstable
    paths-ignore:
      - "**.md"
      - "**.txt"
      - "repo_content/**"
      - "spec_files/**"
      - "post_install_files/**"
      - "press_kit/**"
      - ".github/workflows/build_iso.yml"
  merge_group:
  workflow_dispatch:
    inputs:
      # Run with this periodically to analyze the image again
      # As package drift will make the plan eventually non-ideal
      # (existing users will have to redownload most of the image)
      fresh-rechunk:
        description: 'Clear rechunk history'
        type: boolean
        default: false
env:
  IMAGE_REGISTRY: ghcr.io/${{ github.repository_owner }}

concurrency:
  group: ${{ github.workflow }}-${{ github.ref || github.run_id }}
  cancel-in-progress: true

jobs:
  push-ghcr:
    name: Make
    runs-on: ubuntu-24.04
    continue-on-error: false
    permissions:
      contents: read
      packages: write
      id-token: write
    strategy:
      fail-fast: false
      matrix:
        base_image_flavor: [main]
        base_name: [bazzite, bazzite-deck, bazzite-nvidia]
        base_image_name: [kinoite, silverblue]
        target_image_flavor: [main, asus]
        fedora_version: [40]
        include:
          - fedora_version: 40
            bazzite_version: "v3.6"
            is_latest_version: true
            is_stable_version: true
            kernel_flavor: fsync # must match a kernel_flavor from akmods repo
    steps:
      - name: Define env.IMAGE_FLAVOR
        run: |
          if [[ "${{ matrix.base_name }}" == "bazzite-nvidia" ]]; then
              if [[ "${{ matrix.target_image_flavor }}" == "main" ]]; then
                  echo "IMAGE_FLAVOR=nvidia" >> $GITHUB_ENV
              else
                  echo "IMAGE_FLAVOR=${{ format('{0}-{1}', matrix.target_image_flavor, 'nvidia') }}" >> $GITHUB_ENV
              fi
          else
              echo "IMAGE_FLAVOR=${{ matrix.target_image_flavor }}" >> $GITHUB_ENV
          fi

      - name: Define env.IMAGE_NAME
        run: |
          DESKTOP=""
          if [[ "${{ matrix.base_image_name }}" == "silverblue" ]]; then
              DESKTOP="-gnome"
          fi

          if [[ "${{ matrix.base_name }}" == "bazzite-deck" ]]; then
              if [[ "${{ matrix.target_image_flavor }}" == "asus" ]]; then
                  echo "IMAGE_NAME=${{ format('{0}{1}', 'bazzite-ally', '${DESKTOP}') }}" >> $GITHUB_ENV
              else
                  echo "IMAGE_NAME=${{ format('{0}{1}', 'bazzite-deck', '${DESKTOP}') }}" >> $GITHUB_ENV
              fi
          else
              if [[ "${{ env.IMAGE_FLAVOR }}" == "main" ]]; then
                  echo "IMAGE_NAME=${{ format('{0}{1}', 'bazzite', '${DESKTOP}') }}" >> $GITHUB_ENV
              else
                  echo "IMAGE_NAME=${{ format('{0}{1}-{2}', 'bazzite', '${DESKTOP}', env.IMAGE_FLAVOR) }}" >> $GITHUB_ENV
              fi
          fi

      - name: Define env.SHA_HEAD_SHORT
        run: |
          echo "SHA_HEAD_SHORT=${GITHUB_SHA::7}" >> $GITHUB_ENV

      - name: Verify main image
        uses: EyeCantCU/cosign-action/verify@v0.3.0
        with:
          containers: ${{ matrix.base_image_name }}-${{ matrix.base_image_flavor }}:${{ matrix.fedora_version }}
          pubkey: https://raw.githubusercontent.com/ublue-os/${{ matrix.base_image_flavor }}/main/cosign.pub
          registry: ${{ env.IMAGE_REGISTRY }}

      - name: Verify akmods image
        uses: EyeCantCU/cosign-action/verify@v0.3.0
        with:
          containers: akmods:${{ matrix.kernel_flavor}}-${{ matrix.fedora_version }}
          pubkey: https://raw.githubusercontent.com/ublue-os/akmods/main/cosign.pub
          registry: ${{ env.IMAGE_REGISTRY }}

      - name: Verify akmods-nvidia image
        uses: EyeCantCU/cosign-action/verify@v0.3.0
        with:
          containers: akmods-nvidia:${{ matrix.kernel_flavor}}-${{ matrix.fedora_version }}
          pubkey: https://raw.githubusercontent.com/ublue-os/akmods/main/cosign.pub
          registry: ${{ env.IMAGE_REGISTRY }}

      # Checkout push-to-registry action GitHub repository
      - name: Checkout Push to Registry action
        uses: actions/checkout@v4

      - name: Check just syntax
        uses: ublue-os/just-action@v1

      - name: Maximize build space
        uses: ublue-os/remove-unwanted-software@v7

      - name: Pull main, akmods, rechunk images
        uses: Wandalen/wretry.action@v3.5.0
        with:
          attempt_limit: 3
          attempt_delay: 15000
          command: |
            # pull the base images used for FROM in Containerfile so
            # we can retry on that unfortunately common failure case
            sudo podman pull ${{ env.IMAGE_REGISTRY }}/${{ matrix.base_image_name }}-${{ matrix.base_image_flavor }}:${{ matrix.fedora_version }}
            sudo podman pull ${{ env.IMAGE_REGISTRY }}/akmods:${{ matrix.kernel_flavor}}-${{ matrix.fedora_version }}
            sudo podman pull ${{ env.IMAGE_REGISTRY }}/akmods-nvidia:${{ matrix.kernel_flavor}}-${{ matrix.fedora_version }}

            # Add rechunk as well to remove this source of failure
            sudo podman pull ghcr.io/hhd-dev/rechunk:v0.5.0

      - name: Get source versions
        id: labels
        uses: Wandalen/wretry.action@v3.5.0
        with:
          attempt_limit: 3
          attempt_delay: 15000
          command: |
            set -eo pipefail
            skopeo inspect docker://${{ env.IMAGE_REGISTRY }}/${{ matrix.base_image_name }}-${{ matrix.base_image_flavor }}:${{ matrix.fedora_version }} > source.json
            ver=$(jq -r '.Labels["org.opencontainers.image.version"]' source.json)
            if [ -z "$ver" ] || [ "null" = "$ver" ]; then
              echo "inspected image version must not be empty or null"
              exit 1
            fi
            echo "SOURCE_IMAGE_VERSION=$ver" >> $GITHUB_ENV

            skopeo inspect docker://${{ env.IMAGE_REGISTRY }}/akmods:${{ matrix.kernel_flavor }}-${{ matrix.fedora_version }} > akmods.json
            linux=$(jq -r '.Labels["ostree.linux"]' akmods.json)
            if [ -z "$linux" ] || [ "null" = "$linux" ]; then
              echo "inspected image linux version must not be empty or null"
              exit 1
            fi
            echo "KERNEL_VERSION=$linux" >> $GITHUB_ENV
      
      # Generate a primary version key that appears
      # in KDE, rpm-ostree status, and github.
      - name: Generate Version
        id: generate-version
        shell: bash
        run: |
          # Generate the primary version key that will be stored on os-release,
          # shown on the bootloader, and used for the image tag.
          UPSTREAM_TAG="${{ env.SOURCE_IMAGE_VERSION }}"
          FEDORA_VERSION="${{ matrix.fedora_version }}"
          BAZZITE_VERSION="${{ matrix.bazzite_version }}"
          SHA_SHORT="${GITHUB_SHA::7}"

          if [ -n "${{ github.event.pull_request.number }}" ]; then
            VERSION="pr-${FEDORA_VERSION}-${{ github.event.pull_request.number }}"
            PRETTY_VERSION="PR (${{ github.event.pull_request.number }}, ${UPSTREAM_TAG})"
          elif [[ ${{ github.ref_name }} == "unstable" ]]; then
            VERSION="unstable-${SHA_SHORT}"
            PRETTY_VERSION="Unstable (#${SHA_SHORT}, F${UPSTREAM_TAG})"
          elif [[ ${{ github.ref_name }} == "testing" ]]; then
            VERSION="testing-${BAZZITE_VERSION}-${UPSTREAM_TAG}"
            PRETTY_VERSION="Testing (${BAZZITE_VERSION}, F${UPSTREAM_TAG})"
          else
            VERSION="${BAZZITE_VERSION}-${UPSTREAM_TAG}"
            PRETTY_VERSION="Stable (${BAZZITE_VERSION}, F${UPSTREAM_TAG})"
          fi
          echo "tag=${VERSION}" >> $GITHUB_OUTPUT
          echo "pretty=${PRETTY_VERSION}" >> $GITHUB_OUTPUT

          echo "Generated the following:"
          cat $GITHUB_OUTPUT

      # Build image using buildah and save it to raw-img
      - name: Build Image
        id: build_image
        run: |
          sudo buildah build \
            --target ${{ matrix.base_name }} \
            --build-arg IMAGE_NAME=${{ env.IMAGE_NAME }} \
            --build-arg IMAGE_FLAVOR=${{ env.IMAGE_FLAVOR }} \
            --build-arg IMAGE_VENDOR=${{ github.repository_owner }} \
            --build-arg BASE_IMAGE_NAME=${{ matrix.base_image_name }} \
            --build-arg BASE_IMAGE_FLAVOR=${{ matrix.base_image_flavor }} \
            --build-arg FEDORA_VERSION=${{ matrix.fedora_version }} \
            --build-arg KERNEL_FLAVOR=${{ matrix.kernel_flavor }} \
            --build-arg IMAGE_BRANCH=${{ github.ref_name }} \
            --build-arg SHA_HEAD_SHORT=${{ env.SHA_HEAD_SHORT }} \
            --build-arg VERSION_TAG=${{ steps.generate-version.outputs.tag }} \
            --build-arg VERSION_PRETTY="${{ steps.generate-version.outputs.pretty }}" \
            --tag raw-img .
      
      - name: Remove auxiliary images
        # We are tight on space, need at least 2x for OSTree
        run: |            
          sudo podman image rm ${{ env.IMAGE_REGISTRY }}/${{ matrix.base_image_name }}-${{ matrix.base_image_flavor }}:${{ matrix.fedora_version }}
          sudo podman image rm ${{ env.IMAGE_REGISTRY }}/akmods:${{ matrix.kernel_flavor}}-${{ matrix.fedora_version }}
          sudo podman image rm ${{ env.IMAGE_REGISTRY }}/akmods-nvidia:${{ matrix.kernel_flavor}}-${{ matrix.fedora_version }}
      
      # Reprocess raw-img using rechunker which will delete it
      - name: Run Rechunker
        id: rechunk
        uses: hhd-dev/rechunk@v0.6.2
        with:
          rechunk: 'ghcr.io/hhd-dev/rechunk:v0.5.0'
          ref: 'raw-img'
          prev-ref: ${{ github.event.inputs.fresh-rechunk == 'true' && '' || 'ghcr.io/ublue-os/bazzite:unstable' }}
          version: '${{ steps.generate-version.outputs.tag }}'
          labels: |
            io.artifacthub.package.logo-url=https://raw.githubusercontent.com/ublue-os/bazzite/main/repo_content/logo.png
            io.artifacthub.package.readme-url=https://bazzite.gg/
            org.opencontainers.image.created=<timestamp>
            org.opencontainers.image.description=Bazzite is an OCI image that serves as an alternative operating system for the Steam Deck, and a ready-to-game SteamOS-like for desktop computers, living room home theater PCs, and numerous other handheld PCs.
            org.opencontainers.image.licenses=Apache-2.0
            org.opencontainers.image.revision=${{ github.sha }}
            org.opencontainers.image.source=https://github.com/ublue-os/bazzite
            org.opencontainers.image.title=bazzite
            org.opencontainers.image.url=https://github.com/ublue-os/bazzite

            org.universal-blue.pkg.kernel=<relver:kernel>
            org.universal-blue.pkg.gamescope=<relver:gamescope>
            ${{ matrix.base_image_name == "kinoite" && "org.universal-blue.pkg.kwayland=<version:kwayland>" || "" }}
            ${{ matrix.base_name == "bazzite-deck" && "org.universal-blue.pkg.hhd=<version:hhd>" || "" }}
            ${{ matrix.base_name == "bazzite-deck" && "org.universal-blue.pkg.hhd=<version:adjustor>" || "" }}
            ${{ matrix.base_name == "bazzite-deck" && "org.universal-blue.pkg.hhd=<version:hhd-ui>" || "" }}

            org.universal-blue.info=<imginfo>
      
      # Generate tags after rechunker runs and checks the primary tag is not duplicated
      # If it is, rechunk will suffix it by .1, .2, etc and put it in steps.rechunk.outputs.version
      - name: Generate tags
        id: generate-tags
        shell: bash
        run: |
          # Common vars for generating tags
          VERSION_TAG="${{ steps.rechunk.outputs.version }}"
          UPSTREAM_TAG="${{ env.SOURCE_IMAGE_VERSION }}"
          FEDORA_VERSION="${{ matrix.fedora_version }}"
          BAZZITE_VERSION="${{ matrix.bazzite_version }}"
          SHA_SHORT="${GITHUB_SHA::7}"
          BUILD_TAGS=( "${VERSION_TAG}" )

          # Use latest var to check if we should tag as latest
          unset LATEST
          if [[ "${{ matrix.is_latest_version }}" == "true" ]] && \
              [[ "${{ matrix.is_stable_version }}" == "true" ]]; then
                LATEST="1"
          fi

          if [[ "${{ github.event_name }}" == "pull_request" ]]; then
            # Track latest ver per PR
            if [ -n "$LATEST" ]; then
                BUILD_TAGS+=("pr-${{ github.event.pull_request.number }}")
            fi
          elif [[ ${{ github.ref_name }} == "unstable" ]]; then
            # Per fedora version
            BUILD_TAGS+=("${FEDORA_VERSION}-unstable")
            BUILD_TAGS+=("unstable-${FEDORA_VERSION}") # flip ver to be last

            # Per upstream ver
            BUILD_TAGS+=("unstable-${UPSTREAM_TAG}")

            if [ -n "$LATEST" ]; then
                BUILD_TAGS+=("unstable")
            fi
          elif [[ ${{ github.ref_name }} == "testing" ]]; then
            # Per fedora version
            BUILD_TAGS+=("${FEDORA_VERSION}-testing")
            BUILD_TAGS+=("testing-${FEDORA_VERSION}") # flip ver to be last

            # Per upstream ver
            BUILD_TAGS+=("testing-${UPSTREAM_TAG}")

            if [ -n "$LATEST" ]; then
                BUILD_TAGS+=("testing-${BAZZITE_VERSION}")
                BUILD_TAGS+=("testing")
            fi
          else
            BUILD_TAGS+=("${FEDORA_VERSION}")
            BUILD_TAGS+=("${UPSTREAM_TAG}")
            BUILD_TAGS+=("${BAZZITE_VERSION}")
            BUILD_TAGS+=("stable-${UPSTREAM_TAG}")

            # Per fedora version
            BUILD_TAGS+=("${FEDORA_VERSION}-stable")
            BUILD_TAGS+=("stable-${FEDORA_VERSION}") # flip ver to be last
            
            if [ -n "$LATEST" ]; then
              BUILD_TAGS+=("latest" "stable")
            fi
          fi
          
          echo "Generated the following build tags: "
          for TAG in "${BUILD_TAGS[@]}"; do
              echo "${TAG}"
          done
          echo "alias_tags=${BUILD_TAGS[*]}" >> $GITHUB_OUTPUT

      # Pull oci-dir image, remove oci dir to make space, and then tag appropriately
      - name: Load in podman and tag
        run: |
          IMAGE=$(podman pull ${{ steps.rechunk.outputs.ref }})
          sudo rm -rf ${{ steps.rechunk.outputs.output }}
          for tag in ${{ steps.generate-tags.outputs.alias_tags }}; do
            podman tag $IMAGE ${{ env.IMAGE_NAME }}:$tag
          done
          # keep for secureboot check
          podman tag $IMAGE rechunked-img

      - name: Check Secureboot
        shell: bash
        run: |
          set -x
          if [[ ! $(command -v sbverify) || ! $(command -v curl) || ! $(command -v openssl) ]]; then
            sudo apt update
            sudo apt install sbsigntool curl openssl
          fi
          TMP=$(podman create rechunked-img bash)
          podman cp $TMP:/usr/lib/modules/${{ env.KERNEL_VERSION }}/vmlinuz .
          podman rm $TMP
          sbverify --list vmlinuz
          curl --retry 3 -Lo kernel-sign.der https://github.com/ublue-os/kernel-cache/raw/main/certs/public_key.der
          curl --retry 3 -Lo akmods.der https://github.com/ublue-os/kernel-cache/raw/main/certs/public_key_2.der
          openssl x509 -in kernel-sign.der -out kernel-sign.crt
          openssl x509 -in akmods.der -out akmods.crt
          sbverify --cert kernel-sign.crt vmlinuz || exit 1
          sbverify --cert akmods.crt vmlinuz || exit 1

      # Workaround bug where capital letters in your GitHub username make it impossible to push to GHCR.
      # https://github.com/macbre/push-to-ghcr/issues/12
      - name: Lowercase Registry
        id: registry_case
        uses: ASzc/change-string-case-action@v6
        with:
          string: ${{ env.IMAGE_REGISTRY }}

      # Push the image to GHCR (Image Registry)
      - name: Push To GHCR
        uses: Wandalen/wretry.action@v3.5.0
        id: push
        if: github.event_name != 'pull_request'
        env:
          REGISTRY_USER: ${{ github.actor }}
          REGISTRY_PASSWORD: ${{ github.token }}
        with:
          action: redhat-actions/push-to-registry@v2
          attempt_limit: 3
          attempt_delay: 15000
          with: |
            image: ${{ env.IMAGE_NAME }}
            tags: ${{ steps.generate-tags.outputs.alias_tags }}
            registry: ${{ steps.registry_case.outputs.lowercase }}
            username: ${{ env.REGISTRY_USER }}
            password: ${{ env.REGISTRY_PASSWORD }}
            extra-args: |
              --disable-content-trust

      - name: Sign container image
        uses: EyeCantCU/cosign-action/sign@v0.3.0
        if: github.event_name != 'pull_request'
        with:
          containers: ${{ env.IMAGE_NAME }}
          registry-token: ${{ secrets.GITHUB_TOKEN }}
          signing-secret: ${{ secrets.SIGNING_SECRET }}
          tags: ${{ steps.push.outputs.outputs && fromJSON(steps.push.outputs.outputs).digest }}

      - name: Echo outputs
        if: github.event_name != 'pull_request'
        run: |
          echo "${{ toJSON(steps.push.outputs) }}"

  build_iso:
    name: build iso
    needs: [push-ghcr]
    if: github.ref_name == 'testing'
    # Eventually would be nice for building images in PRs
    #if: ${{ endsWith(github.event.pull_request.title, '[ISO]') }}
    uses: ./.github/workflows/build_iso.yml
    secrets: inherit