name: Build and Upload OS image
on:
  workflow_dispatch:
    inputs:
      imageVersion:
        description: "Semantic version including patch e.g. v<major>.<minor>.<patch> (only used for releases)"
        required: false
      isRelease:
        description: 'Is this a release? (sets "ref" to special value "-")'
        type: boolean
        required: false
        default: false
      stream:
        description: "Image stream / type. (Use 'stable' for releases, 'nightly' for regular non-release images and 'debug' for debug builds)"
        type: choice
        required: true
        options:
          - "stable"
          - "nightly"
          - "debug"
          # TODO: implement console access enabled image
          # - "console"

jobs:
  build-dependencies:
    name: "Build binaries for embedding in the OS"
    runs-on: ubuntu-22.04
    permissions:
      contents: read
      packages: read
    outputs:
      bootstrapper-sha256: ${{ steps.collect-hashes.outputs.bootstrapper-sha256 }}
      disk-mapper-sha256: ${{ steps.collect-hashes.outputs.disk-mapper-sha256 }}
    steps:
      - name: Checkout
        uses: actions/checkout@755da8c3cf115ac066823e79a1e1788f8940201b # v3.2.0
        with:
          ref: ${{ !github.event.pull_request.head.repo.fork && github.head_ref || '' }}

      - name: Build bootstrapper
        if: inputs.stream != 'debug'
        uses: ./.github/actions/build_bootstrapper
        with:
          outputPath: ${{ github.workspace }}/build/bootstrapper

      - name: Build debugd
        if: inputs.stream == 'debug'
        uses: ./.github/actions/build_debugd
        with:
          outputPath: ${{ github.workspace }}/build/bootstrapper

      - name: Build disk-mapper
        uses: ./.github/actions/build_disk_mapper
        with:
          outputPath: ${{ github.workspace }}/build/disk-mapper

      - name: Upload dependencies
        uses: actions/upload-artifact@83fd05a356d7e2593de66fc9913b3002723633cb # tag=v3.1.1
        with:
          name: dependencies
          path: |
            ${{ github.workspace }}/build/bootstrapper
            ${{ github.workspace }}/build/disk-mapper

      - name: Collect hashes
        id: collect-hashes
        run: |
          {
            echo "bootstrapper-sha256=$(sha256sum bootstrapper | head -c 64)"
            echo "disk-mapper-sha256=$(sha256sum disk-mapper | head -c 64)"
          } >> "$GITHUB_OUTPUT"
        working-directory: ${{ github.workspace }}/build

  build-settings:
    name: "Determine build settings"
    runs-on: ubuntu-22.04
    outputs:
      ref: ${{ steps.ref.outputs.ref }}
      imageType: ${{ steps.image-type.outputs.imageType }}
      pkiSet: ${{ steps.pki-set.outputs.pkiSet }}
      imageVersion: ${{ steps.image-version.outputs.imageVersion }}
      imageName: ${{ steps.image-version.outputs.imageName }}
      imageNameShort: ${{ steps.image-version.outputs.imageNameShort }}
      imageApiBasePath: ${{ steps.image-version.outputs.imageApiBasePath }}

    steps:
      - name: Checkout
        uses: actions/checkout@755da8c3cf115ac066823e79a1e1788f8940201b # v3.2.0
        with:
          ref: ${{ !github.event.pull_request.head.repo.fork && github.head_ref || '' }}

      - name: Determine version
        id: version
        uses: ./.github/actions/pseudo_version

      - name: Determine ref
        id: ref
        run: |
          if [[ "${{ inputs.isRelease }}" = "true" ]]; then
            echo "ref=-" >> "$GITHUB_OUTPUT"
          else
            echo "ref=${{ steps.version.outputs.branchName }}" >> "$GITHUB_OUTPUT"
          fi

      - name: Validate stream
        run: |
          if [[ "${{ inputs.isRelease }}" == "true" ]] && [[ "${{ inputs.stream }}" == "nightly" ]]; then
            echo "Nightly builds are not allowed for releases"
            exit 1
          elif [[ "${{ inputs.isRelease }}" != "true" ]] && [[ "${{ inputs.stream }}" == "stable" ]]; then
            echo "Stable builds are only allowed for releases"
            exit 1
          fi

      - name: Determine type of image build
        shell: bash
        id: image-type
        run: |
          if  [[ "${{ inputs.stream }}" == "debug" ]]; then
            echo "imageType=debug" >> "$GITHUB_OUTPUT"
          else
            echo "imageType=default" >> "$GITHUB_OUTPUT"
          fi

      - name: Determine PKI set
        id: pki-set
        shell: bash
        run: |
          if [[ "${{ inputs.isRelease }}" == "true" ]] && [[ "${{ inputs.stream }}" == "stable" ]]; then
             echo "pkiSet=pki_prod" >> "$GITHUB_OUTPUT"
           else
             echo "pkiSet=pki_testing" >> "$GITHUB_OUTPUT"
           fi

      - name: Determine image version
        id: image-version
        shell: bash
        env:
          REF: ${{ steps.ref.outputs.ref }}
          STREAM: ${{ inputs.stream }}
          IMAGE_VERSION: ${{ inputs.imageVersion || steps.version.outputs.pseudoVersion }}
        run: |
          {
            echo "imageVersion=${IMAGE_VERSION}"
            echo "imageName=ref/${REF}/stream/${STREAM}/${IMAGE_VERSION}"
            echo "imageApiBasePath=constellation/v1/ref/${REF}/stream/${STREAM}/image/${IMAGE_VERSION}"
          } >> "$GITHUB_OUTPUT"

          if [[ "${REF}" = "-" ]] && [[ "${STREAM}" = "stable" ]]; then
            echo "imageNameShort=${IMAGE_VERSION}" >> "$GITHUB_OUTPUT"
          elif [[ "${REF}" = "-" ]]; then
            echo "imageNameShort=stream/${STREAM}/${IMAGE_VERSION}" >> "$GITHUB_OUTPUT"
          else
            echo "imageNameShort=ref/${REF}/stream/${STREAM}/${IMAGE_VERSION}" >> "$GITHUB_OUTPUT"
          fi

  make-os-image:
    name: "Build OS using mkosi"
    needs: [build-settings, build-dependencies]
    runs-on: ubuntu-22.04
    # TODO: flatten outputs once possible
    # https://github.com/community/community/discussions/17245
    outputs:
      image-raw-aws-sha256: ${{ steps.collect-hashes.outputs.image-raw-aws-sha256 }}
      image-raw-azure-sha256: ${{ steps.collect-hashes.outputs.image-raw-azure-sha256 }}
      image-raw-gcp-sha256: ${{ steps.collect-hashes.outputs.image-raw-gcp-sha256 }}
      image-raw-qemu-sha256: ${{ steps.collect-hashes.outputs.image-raw-qemu-sha256 }}
      image-efi-aws-sha256: ${{ steps.collect-hashes.outputs.image-efi-aws-sha256 }}
      image-efi-azure-sha256: ${{ steps.collect-hashes.outputs.image-efi-azure-sha256 }}
      image-efi-gcp-sha256: ${{ steps.collect-hashes.outputs.image-efi-gcp-sha256 }}
      image-efi-qemu-sha256: ${{ steps.collect-hashes.outputs.image-efi-qemu-sha256 }}
      image-initrd-aws-sha256: ${{ steps.collect-hashes.outputs.image-initrd-aws-sha256 }}
      image-initrd-azure-sha256: ${{ steps.collect-hashes.outputs.image-initrd-azure-sha256 }}
      image-initrd-gcp-sha256: ${{ steps.collect-hashes.outputs.image-initrd-gcp-sha256 }}
      image-initrd-qemu-sha256: ${{ steps.collect-hashes.outputs.image-initrd-qemu-sha256 }}
      image-root-raw-aws-sha256: ${{ steps.collect-hashes.outputs.image-root-raw-aws-sha256 }}
      image-root-raw-azure-sha256: ${{ steps.collect-hashes.outputs.image-root-raw-azure-sha256 }}
      image-root-raw-gcp-sha256: ${{ steps.collect-hashes.outputs.image-root-raw-gcp-sha256 }}
      image-root-raw-qemu-sha256: ${{ steps.collect-hashes.outputs.image-root-raw-qemu-sha256 }}
      image-root-verity-aws-sha256: ${{ steps.collect-hashes.outputs.image-root-verity-aws-sha256 }}
      image-root-verity-azure-sha256: ${{ steps.collect-hashes.outputs.image-root-verity-azure-sha256 }}
      image-root-verity-gcp-sha256: ${{ steps.collect-hashes.outputs.image-root-verity-gcp-sha256 }}
      image-root-verity-qemu-sha256: ${{ steps.collect-hashes.outputs.image-root-verity-qemu-sha256 }}
      image-vmlinuz-aws-sha256: ${{ steps.collect-hashes.outputs.image-vmlinuz-aws-sha256 }}
      image-vmlinuz-azure-sha256: ${{ steps.collect-hashes.outputs.image-vmlinuz-azure-sha256 }}
      image-vmlinuz-gcp-sha256: ${{ steps.collect-hashes.outputs.image-vmlinuz-gcp-sha256 }}
      image-vmlinuz-qemu-sha256: ${{ steps.collect-hashes.outputs.image-vmlinuz-qemu-sha256 }}
      image-raw-changelog-aws-sha256: ${{ steps.collect-hashes.outputs.image-raw-changelog-aws-sha256 }}
      image-raw-changelog-azure-sha256: ${{ steps.collect-hashes.outputs.image-raw-changelog-azure-sha256 }}
      image-raw-changelog-gcp-sha256: ${{ steps.collect-hashes.outputs.image-raw-changelog-gcp-sha256 }}
      image-raw-changelog-qemu-sha256: ${{ steps.collect-hashes.outputs.image-raw-changelog-qemu-sha256 }}
      image-raw-manifest-aws-sha256: ${{ steps.collect-hashes.outputs.image-raw-manifest-aws-sha256 }}
      image-raw-manifest-azure-sha256: ${{ steps.collect-hashes.outputs.image-raw-manifest-azure-sha256 }}
      image-raw-manifest-gcp-sha256: ${{ steps.collect-hashes.outputs.image-raw-manifest-gcp-sha256 }}
      image-raw-manifest-qemu-sha256: ${{ steps.collect-hashes.outputs.image-raw-manifest-qemu-sha256 }}
    strategy:
      fail-fast: false
      matrix:
        csp: [aws, azure, gcp, qemu]
    steps:
      - name: Checkout
        uses: actions/checkout@755da8c3cf115ac066823e79a1e1788f8940201b # v3.2.0
        with:
          ref: ${{ !github.event.pull_request.head.repo.fork && github.head_ref || '' }}

      - name: Download build dependencies
        uses: actions/download-artifact@9782bd6a9848b53b110e712e20e42d89988822b7 # tag=v3.0.1
        with:
          name: dependencies
          path: ${{ github.workspace }}/build

      - name: Mark bootstrapper and disk-mapper as executable
        run: |
          chmod +x ${{ github.workspace }}/build/bootstrapper
          chmod +x ${{ github.workspace }}/build/disk-mapper

      - name: Setup mkosi
        uses: ./.github/actions/setup_mkosi
        with:
          version: 058046019e7ed2e8e93af87b8c14a808dcc6bbc3

      - name: Prepare PKI for secure boot signing
        id: prepare-pki
        shell: bash
        run: |
          echo "${DB_KEY}" > "${PKI_SET}/db.key"
          ln -s "${PKI_SET}" pki
        working-directory: ${{ github.workspace }}/image
        env:
          PKI_SET: ${{ needs.build-settings.outputs.pkiSet }}
          DB_KEY: ${{ ((needs.build-settings.outputs.pkiSet == 'pki_prod') && secrets.SECURE_BOOT_RELEASE_DB_KEY) || secrets.SECURE_BOOT_TESTING_DB_KEY }}

      - name: Build
        shell: bash
        run: |
          echo "::group::Build"
          sudo make IMAGE_VERSION="${IMAGE_VERSION}" "${CSP}"
          echo "::endgroup::"
        working-directory: ${{ github.workspace }}/image
        env:
          BOOTSTRAPPER_BINARY: ${{ github.workspace }}/build/bootstrapper
          DISK_MAPPER_BINARY: ${{ github.workspace }}/build/disk-mapper
          IMAGE_VERSION: ${{ needs.build-settings.outputs.imageVersion }}
          CSP: ${{ matrix.csp }}

      - name: Collect hashes
        id: collect-hashes
        run: |
          {
            echo "image-raw-${{ matrix.csp }}-sha256=$(sha256sum image.raw | head -c 64)"
            echo "image-efi-${{ matrix.csp }}-sha256=$(sha256sum image.efi | head -c 64)"
            echo "image-initrd-${{ matrix.csp }}-sha256=$(sha256sum image.initrd | head -c 64)"
            echo "image-root-raw-${{ matrix.csp }}-sha256=$(sha256sum image.root.raw | head -c 64)"
            echo "image-root-verity-${{ matrix.csp }}-sha256=$(sha256sum image.root.verity | head -c 64)"
            echo "image-vmlinuz-${{ matrix.csp }}-sha256=$(sha256sum image.vmlinuz | head -c 64)"
            echo "image-raw-changelog-${{ matrix.csp }}-sha256=$(sha256sum image.raw.changelog | head -c 64)"
            echo "image-raw-manifest-${{ matrix.csp }}-sha256=$(sha256sum image.raw.manifest | head -c 64)"
          } >> "$GITHUB_OUTPUT"
        working-directory: ${{ github.workspace }}/image/mkosi.output.${{ matrix.csp }}/fedora~37
        continue-on-error: true

      - name: Upload raw OS image as artifact
        uses: actions/upload-artifact@83fd05a356d7e2593de66fc9913b3002723633cb # tag=v3.1.1
        with:
          name: image-${{ matrix.csp }}
          path: ${{ github.workspace }}/image/mkosi.output.${{ matrix.csp }}/fedora~37/image.raw
        if: always()
        continue-on-error: true

      - name: Upload individual OS parts as artifacts
        uses: actions/upload-artifact@83fd05a356d7e2593de66fc9913b3002723633cb # tag=v3.1.1
        with:
          name: parts-${{ matrix.csp }}
          path: |
            ${{ github.workspace }}/image/mkosi.output.${{ matrix.csp }}/fedora~37/image.cmdline
            ${{ github.workspace }}/image/mkosi.output.${{ matrix.csp }}/fedora~37/image.efi
            ${{ github.workspace }}/image/mkosi.output.${{ matrix.csp }}/fedora~37/image.initrd
            ${{ github.workspace }}/image/mkosi.output.${{ matrix.csp }}/fedora~37/image.root.raw
            ${{ github.workspace }}/image/mkosi.output.${{ matrix.csp }}/fedora~37/image.root.roothash
            ${{ github.workspace }}/image/mkosi.output.${{ matrix.csp }}/fedora~37/image.root.verity
            ${{ github.workspace }}/image/mkosi.output.${{ matrix.csp }}/fedora~37/image.vmlinuz
        if: always()
        continue-on-error: true

      - name: Upload manifest as artifact
        uses: actions/upload-artifact@83fd05a356d7e2593de66fc9913b3002723633cb # tag=v3.1.1
        with:
          name: manifest-${{ matrix.csp }}
          path: |
            ${{ github.workspace }}/image/mkosi.output.${{ matrix.csp }}/fedora~37/image.raw.changelog
            ${{ github.workspace }}/image/mkosi.output.${{ matrix.csp }}/fedora~37/image.raw.manifest
        if: always()
        continue-on-error: true

  upload-os-image:
    name: "Upload OS image to CSP"
    needs: [build-settings, make-os-image]
    runs-on: ubuntu-22.04
    permissions:
      id-token: write
      contents: read
    strategy:
      fail-fast: false
      matrix:
        csp: [aws, azure, gcp, qemu]
        upload-variant: [""]
        include:
          - csp: azure
            upload-variant: TrustedLaunch
    steps:
      - name: Checkout
        uses: actions/checkout@755da8c3cf115ac066823e79a1e1788f8940201b # v3.2.0
        with:
          ref: ${{ !github.event.pull_request.head.repo.fork && github.head_ref || '' }}

      - name: Download OS image artifact
        uses: actions/download-artifact@9782bd6a9848b53b110e712e20e42d89988822b7 # tag=v3.0.1
        with:
          name: image-${{ matrix.csp }}
          path: ${{ github.workspace }}/image/mkosi.output.${{ matrix.csp }}/fedora~37

      - name: Configure input variables
        id: vars
        uses: ./.github/actions/os_build_variables
        with:
          csp: ${{ matrix.csp }}
          uploadVariant: ${{ matrix.upload-variant }}
          basePath: ${{ github.workspace }}/image
          ref: ${{ needs.build-settings.outputs.ref }}
          stream: ${{ inputs.stream }}
          imageVersion: ${{ needs.build-settings.outputs.imageVersion }}
          imageType: ${{ needs.build-settings.outputs.imageType }}
          debug: ${{ needs.build-settings.outputs.imageType == 'debug' }}

      - name: Install tools
        shell: bash
        run: |
          echo "::group::Install tools"
          sudo apt-get update
          sudo apt-get install -y \
            pigz \
            qemu-utils \
            python3-crc32c
          echo "::endgroup::"

      - name: Login to AWS
        uses: aws-actions/configure-aws-credentials@67fbcbb121271f7775d2e7715933280b06314838 # tag=v1.7.0
        # on AWS, login is required to upload the image as AMI
        # on Azure, login is done to download the VMGS from S3
        # on QEMU, login is done to upload the image to S3
        if: matrix.csp == 'aws' || matrix.csp == 'azure' || matrix.csp == 'qemu'
        with:
          role-to-assume: arn:aws:iam::795746500882:role/GitHubConstellationImagePipeline
          aws-region: eu-central-1

      - name: Login to Azure
        if: matrix.csp == 'azure'
        uses: ./.github/actions/login_azure
        with:
          azure_credentials: ${{ secrets.AZURE_CREDENTIALS }}

      - name: Login to GCP
        uses: ./.github/actions/login_gcp
        if: matrix.csp == 'gcp'
        with:
          gcp_service_account_json: ${{ secrets.GCP_IMAGE_UPLOAD_SERVICE_ACCOUNT }}

      - name: Prepare PKI for image upload
        id: prepare-pki
        shell: bash
        run: |
          ln -s ${{ needs.build-settings.outputs.pkiSet }} pki
        working-directory: ${{ github.workspace }}/image

      - name: Download VMGS blob
        run: |
          aws s3 cp \
            --region "${AZURE_VMGS_REGION}" \
            "s3://constellation-secure-boot/${PKI_SET}/${AZURE_SECURITY_TYPE}.vmgs" \
            "${PKI_SET}/${AZURE_SECURITY_TYPE}.vmgs" \
            --no-progress
        working-directory: ${{ github.workspace }}/image
        if: matrix.csp == 'azure' && !endsWith(env.AZURE_SECURITY_TYPE, 'Supported')
        env:
          PKI_SET: ${{ needs.build-settings.outputs.pkiSet }}
          AZURE_VMGS_REGION: ${{ steps.vars.outputs.azureVmgsRegion }}
          AZURE_SECURITY_TYPE: ${{ steps.vars.outputs.azureSecurityType }}

      - name: Upload AWS image
        shell: bash
        run: |
          echo "::group::Upload AWS image"
          secure-boot/aws/create_uefivars.sh "${AWS_EFIVARS_PATH}"
          upload/upload_aws.sh
          echo -e "Uploaded AWS image: \n\n\`\`\`\n$(jq < "${AWS_JSON_OUTPUT}")\n\`\`\`\n" >> "$GITHUB_STEP_SUMMARY"
          echo "::endgroup::"
        working-directory: ${{ github.workspace }}/image
        if: matrix.csp == 'aws'
        env:
          PKI: ${{ github.workspace }}/image/pki
          AWS_JSON_OUTPUT: ${{ steps.vars.outputs.awsJsonOutput }}
          AWS_BUCKET: ${{ steps.vars.outputs.awsBucket }}
          AWS_EFIVARS_PATH: ${{ steps.vars.outputs.awsEfivarsPath }}
          AWS_IMAGE_FILENAME: ${{ steps.vars.outputs.awsImageFilename }}
          AWS_IMAGE_NAME: ${{ steps.vars.outputs.awsImageName }}
          AWS_IMAGE_PATH: ${{ steps.vars.outputs.awsImagePath }}
          AWS_REGION: ${{ steps.vars.outputs.awsRegion }}
          AWS_REPLICATION_REGIONS: ${{ steps.vars.outputs.awsReplicationRegions }}
          AWS_PUBLISH: ${{ steps.vars.outputs.awsPublish }}

      - name: Upload GCP image
        shell: bash
        run: |
          echo "::group::Upload GCP image"
          upload/pack.sh gcp "${GCP_RAW_IMAGE_PATH}" "${GCP_IMAGE_PATH}"
          upload/upload_gcp.sh
          echo -e "Uploaded GCP image: \n\n\`\`\`\n$(jq < "${GCP_JSON_OUTPUT}")\n\`\`\`\n" >> "$GITHUB_STEP_SUMMARY"
          echo "::endgroup::"
        working-directory: ${{ github.workspace }}/image
        if: matrix.csp == 'gcp'
        env:
          PKI: ${{ github.workspace }}/image/pki
          GCP_JSON_OUTPUT: ${{ steps.vars.outputs.gcpJsonOutput }}
          GCP_BUCKET: ${{ steps.vars.outputs.gcpBucket }}
          GCP_IMAGE_FAMILY: ${{ steps.vars.outputs.gcpImageFamily }}
          GCP_IMAGE_FILENAME: ${{ steps.vars.outputs.gcpImageFilename }}
          GCP_IMAGE_NAME: ${{ steps.vars.outputs.gcpImageName }}
          GCP_IMAGE_PATH: ${{ steps.vars.outputs.gcpImagePath }}
          GCP_PROJECT: ${{ steps.vars.outputs.gcpProject }}
          GCP_RAW_IMAGE_PATH: ${{ steps.vars.outputs.gcpRawImagePath }}
          GCP_REGION: ${{ steps.vars.outputs.gcpRegion }}

      - name: Upload Azure image
        shell: bash
        run: |
          echo "::group::Upload Azure image"
          upload/pack.sh azure "${AZURE_RAW_IMAGE_PATH}" "${AZURE_IMAGE_PATH}"
          upload/upload_azure.sh -g --disk-name "${AZURE_DISK_NAME}" "${AZURE_VMGS_PATH}"
          echo -e "Uploaded Azure ${AZURE_SECURITY_TYPE} image: \n\n\`\`\`\n$(jq < "${AZURE_JSON_OUTPUT}")\n\`\`\`\n" >> "$GITHUB_STEP_SUMMARY"
          echo "::endgroup::"
        working-directory: ${{ github.workspace }}/image
        if: matrix.csp == 'azure'
        env:
          PKI: ${{ github.workspace }}/image/pki
          AZURE_JSON_OUTPUT: ${{ steps.vars.outputs.azureJsonOutput }}
          AZURE_DISK_NAME: ${{ steps.vars.outputs.azureDiskName }}
          AZURE_GALLERY_NAME: ${{ steps.vars.outputs.azureGalleryName }}
          AZURE_IMAGE_DEFINITION: ${{ steps.vars.outputs.azureImageDefinition }}
          AZURE_IMAGE_OFFER: ${{ steps.vars.outputs.azureImageOffer }}
          AZURE_IMAGE_PATH: ${{ steps.vars.outputs.azureImagePath }}
          AZURE_IMAGE_VERSION: ${{ steps.vars.outputs.azureImageVersion }}
          AZURE_PUBLISHER: ${{ steps.vars.outputs.azurePublisher }}
          AZURE_RAW_IMAGE_PATH: ${{ steps.vars.outputs.azureRawImagePath }}
          AZURE_REGION: ${{ steps.vars.outputs.azureRegion }}
          AZURE_REPLICATION_REGIONS: ${{ steps.vars.outputs.azureReplicationRegions }}
          AZURE_VMGS_REGION: ${{ steps.vars.outputs.azureVmgsRegion }}
          AZURE_RESOURCE_GROUP_NAME: ${{ steps.vars.outputs.azureResourceGroupName }}
          AZURE_SECURITY_TYPE: ${{ steps.vars.outputs.azureSecurityType }}
          AZURE_SKU: ${{ steps.vars.outputs.azureSku }}
          AZURE_VMGS_PATH: ${{ steps.vars.outputs.azureVmgsPath }}

      - name: Upload QEMU image
        shell: bash
        if: matrix.csp == 'qemu'
        run: |
          echo "::group::Upload QEMU image"
          upload/upload_qemu.sh
          echo -e "Uploaded QEMU image: \n\n\`\`\`\n$(jq < "${QEMU_JSON_OUTPUT}")\n\`\`\`\n" >> "$GITHUB_STEP_SUMMARY"
          echo "::endgroup::"
        working-directory: ${{ github.workspace }}/image
        env:
          QEMU_JSON_OUTPUT: ${{ steps.vars.outputs.qemuJsonOutput }}
          QEMU_BUCKET: ${{ steps.vars.outputs.qemuBucket }}
          QEMU_BASE_URL: ${{ steps.vars.outputs.qemuBaseUrl }}
          QEMU_IMAGE_PATH: ${{ steps.vars.outputs.qemuImagePath }}
          REF: ${{needs.build-settings.outputs.ref }}
          STREAM: ${{inputs.stream }}
          IMAGE_VERSION: ${{needs.build-settings.outputs.imageVersion }}

      - name: Upload image lookup table as artifact
        uses: actions/upload-artifact@83fd05a356d7e2593de66fc9913b3002723633cb # tag=v3.1.1
        with:
          name: lookup-table
          path: ${{ github.workspace }}/image/mkosi.output.*/*/image-upload*.json

  calculate-pcrs:
    name: "Calculate PCRs"
    needs: [build-settings, make-os-image]
    permissions:
      id-token: write
      contents: read
    runs-on: ubuntu-22.04
    strategy:
      fail-fast: false
      matrix:
        csp: [aws, azure, gcp, qemu]
    steps:
      - name: Checkout repository
        uses: actions/checkout@755da8c3cf115ac066823e79a1e1788f8940201b # v3.2.0
        with:
          ref: ${{ !github.event.pull_request.head.repo.fork && github.head_ref || '' }}

      - name: Login to AWS
        uses: aws-actions/configure-aws-credentials@67fbcbb121271f7775d2e7715933280b06314838 # tag=v1.7.0
        with:
          role-to-assume: arn:aws:iam::795746500882:role/GitHubConstellationImagePipeline
          aws-region: eu-central-1

      - name: Download OS image artifact
        uses: actions/download-artifact@9782bd6a9848b53b110e712e20e42d89988822b7 # tag=v3.0.1
        with:
          name: image-${{ matrix.csp }}

      - name: Install dependencies
        run: |
          echo "::group::Install dependencies"
          python -m pip install --user lief==0.12.2
          sudo apt-get update
          sudo apt-get install -y systemd-container # for systemd-dissect
          echo "::endgroup::"

      - name: Calculate expected PCRs
        run: |
          echo "::group::Calculate expected PCRs"
          {
            ./precalculate_pcr_4.sh ${{ github.workspace }}/image.raw ${{ github.workspace }}/pcr-4-${{ matrix.csp }}.json
            ./precalculate_pcr_9.sh ${{ github.workspace }}/image.raw ${{ github.workspace }}/pcr-9-${{ matrix.csp }}.json
            ./precalculate_pcr_12.sh ${{ github.workspace }}/image.raw ${{ github.workspace }}/pcr-12-${{ matrix.csp }}.json ${{ matrix.csp }}
          } >> "$GITHUB_STEP_SUMMARY"
          cp pcr-stable.json ${{ github.workspace }}/
          jq -sSc '.[0] * .[1] * .[2] * .[3]' ${{ github.workspace }}/pcr-* > ${{ github.workspace }}/pcrs-${{ matrix.csp }}.json
          echo "::endgroup::"
        working-directory: ${{ github.workspace }}/image/measured-boot

      - name: Upload expected PCRs as artifact
        uses: actions/upload-artifact@83fd05a356d7e2593de66fc9913b3002723633cb # tag=v3.1.1
        with:
          name: pcrs
          path: pcrs-${{ matrix.csp }}.json

      - name: Upload expected PCRs to S3
        shell: bash
        run: |
          aws s3 cp \
            "pcrs-${{ matrix.csp }}.json" \
            "s3://cdn-constellation-backend/${{needs.build-settings.outputs.imageApiBasePath}}/csp/${{ matrix.csp }}/measurements.image.json" \
            --no-progress

  generate-sbom:
    name: "Generate SBOM"
    needs: [build-settings, build-dependencies, make-os-image]
    permissions:
      id-token: write
      contents: read
    runs-on: ubuntu-22.04
    steps:
      - name: Login to AWS
        uses: aws-actions/configure-aws-credentials@67fbcbb121271f7775d2e7715933280b06314838 # tag=v1.7.0
        with:
          role-to-assume: arn:aws:iam::795746500882:role/GitHubConstellationImagePipeline
          aws-region: eu-central-1

      - name: Install squashfs tools
        run: |
          echo "::group::Install squashfs tools"
          sudo apt-get update
          sudo apt-get install -y squashfs-tools
          echo "::endgroup::"

      - name: Download rootfs
        uses: actions/download-artifact@9782bd6a9848b53b110e712e20e42d89988822b7 # tag=v3.0.1
        with:
          # downloading / using only the QEMU rootfs is fine
          # since the images only differ in the ESP partition
          name: parts-qemu

      - name: Download manifest
        uses: actions/download-artifact@9782bd6a9848b53b110e712e20e42d89988822b7 # tag=v3.0.1
        with:
          # downloading / using only the QEMU manifest is fine
          # since the images only differ in the ESP partition
          name: manifest-qemu

      - name: Unpack squashfs
        run: |
          echo "::group::Unpack squashfs"
          unsquashfs -user-xattrs -d image.root.tree image.root.raw
          echo "::endgroup::"

      - uses: anchore/sbom-action@06e109483e6aa305a2b2395eabae554e51530e1d # tag=v0.13.1
        with:
          path: image.root.tree
          artifact-name: sbom.spdx.json
          output-file: sbom.spdx.json
          format: spdx-json

      - uses: anchore/sbom-action@06e109483e6aa305a2b2395eabae554e51530e1d # tag=v0.13.1
        with:
          path: image.root.tree
          artifact-name: sbom.cyclonedx.json
          output-file: sbom.cyclonedx.json
          format: cyclonedx-json

      - uses: anchore/sbom-action@06e109483e6aa305a2b2395eabae554e51530e1d # tag=v0.13.1
        with:
          path: image.root.tree
          artifact-name: sbom.syft.json
          output-file: sbom.syft.json
          format: syft-json

      - name: Combine hashes
        run: |
          cat > SHA256SUMS <<EOF
          ${{ needs.build-dependencies.outputs.bootstrapper-sha256 }} bootstrapper
          ${{ needs.build-dependencies.outputs.disk-mapper-sha256 }} disk-mapper
          ${{ needs.make-os-image.outputs.image-raw-aws-sha256 }} aws/image.raw
          ${{ needs.make-os-image.outputs.image-raw-changelog-aws-sha256 }} aws/image.raw.changelog
          ${{ needs.make-os-image.outputs.image-raw-manifest-aws-sha256 }} aws/image.raw.manifest
          ${{ needs.make-os-image.outputs.image-efi-aws-sha256 }} aws/image.efi
          ${{ needs.make-os-image.outputs.image-initrd-aws-sha256 }} aws/image.initrd
          ${{ needs.make-os-image.outputs.image-root-raw-aws-sha256 }} aws/image.root.raw
          ${{ needs.make-os-image.outputs.image-root-verity-aws-sha256 }} aws/image.root.verity
          ${{ needs.make-os-image.outputs.image-vmlinuz-aws-sha256 }} aws/image.vmlinuz
          ${{ needs.make-os-image.outputs.image-raw-azure-sha256 }} azure/image.raw
          ${{ needs.make-os-image.outputs.image-raw-changelog-azure-sha256 }} azure/image.raw.changelog
          ${{ needs.make-os-image.outputs.image-raw-manifest-azure-sha256 }} azure/image.raw.manifest
          ${{ needs.make-os-image.outputs.image-efi-azure-sha256 }} azure/image.efi
          ${{ needs.make-os-image.outputs.image-initrd-azure-sha256 }} azure/image.initrd
          ${{ needs.make-os-image.outputs.image-root-raw-azure-sha256 }} azure/image.root.raw
          ${{ needs.make-os-image.outputs.image-root-verity-azure-sha256 }} azure/image.root.verity
          ${{ needs.make-os-image.outputs.image-vmlinuz-azure-sha256 }} azure/image.vmlinuz
          ${{ needs.make-os-image.outputs.image-raw-gcp-sha256 }} gcp/image.raw
          ${{ needs.make-os-image.outputs.image-raw-changelog-gcp-sha256 }} gcp/image.raw.changelog
          ${{ needs.make-os-image.outputs.image-raw-manifest-gcp-sha256 }} gcp/image.raw.manifest
          ${{ needs.make-os-image.outputs.image-efi-gcp-sha256 }} gcp/image.efi
          ${{ needs.make-os-image.outputs.image-initrd-gcp-sha256 }} gcp/image.initrd
          ${{ needs.make-os-image.outputs.image-root-raw-gcp-sha256 }} gcp/image.root.raw
          ${{ needs.make-os-image.outputs.image-root-verity-gcp-sha256 }} gcp/image.root.verity
          ${{ needs.make-os-image.outputs.image-vmlinuz-gcp-sha256 }} gcp/image.vmlinuz
          ${{ needs.make-os-image.outputs.image-raw-qemu-sha256 }} qemu/image.raw
          ${{ needs.make-os-image.outputs.image-raw-changelog-qemu-sha256 }} qemu/image.raw.changelog
          ${{ needs.make-os-image.outputs.image-raw-manifest-qemu-sha256 }} qemu/image.raw.manifest
          ${{ needs.make-os-image.outputs.image-efi-qemu-sha256 }} qemu/image.efi
          ${{ needs.make-os-image.outputs.image-initrd-qemu-sha256 }} qemu/image.initrd
          ${{ needs.make-os-image.outputs.image-root-raw-qemu-sha256 }} qemu/image.root.raw
          ${{ needs.make-os-image.outputs.image-root-verity-qemu-sha256 }} qemu/image.root.verity
          ${{ needs.make-os-image.outputs.image-vmlinuz-qemu-sha256 }} qemu/image.vmlinuz
          EOF
          cat SHA256SUMS
          echo -e "SHA256SUMS:\n\`\`\`\n$(cat SHA256SUMS)\n\`\`\`" >> "$GITHUB_STEP_SUMMARY"

      - name: Upload SBOMs to S3
        shell: bash
        run: |
          sboms='sbom.spdx.json sbom.cyclonedx.json sbom.syft.json'
          manifests='image.raw.manifest image.raw.changelog'
          hashes='SHA256SUMS'
          for file in ${sboms} ${manifests} ${hashes}; do
            aws s3 cp \
              "${file}" \
              "s3://cdn-constellation-backend/${{needs.build-settings.outputs.imageApiBasePath}}/${file}" \
              --no-progress
          done

  upload-image-lookup-table:
    name: "Upload image lookup table"
    runs-on: ubuntu-22.04
    needs: [build-settings, upload-os-image]
    permissions:
      id-token: write
      contents: read
    steps:
      - name: Download image lookup table
        uses: actions/download-artifact@9782bd6a9848b53b110e712e20e42d89988822b7 # v3
        with:
          name: lookup-table

      - name: Login to AWS
        uses: aws-actions/configure-aws-credentials@67fbcbb121271f7775d2e7715933280b06314838 # tag=v1.7.0
        with:
          role-to-assume: arn:aws:iam::795746500882:role/GitHubConstellationImagePipeline
          aws-region: eu-central-1

      - name: Combine lookup tables for CSPs
        shell: bash
        run: |
          echo '{}' > intermediate.json

          jq '.ref = "${{  needs.build-settings.outputs.ref }}"' intermediate.json > lookup-table.json
          cp lookup-table.json intermediate.json

          jq '.stream = "${{ inputs.stream }}"' intermediate.json > lookup-table.json
          cp lookup-table.json intermediate.json

          jq '.version = "${{ needs.build-settings.outputs.imageVersion }}"' intermediate.json > lookup-table.json
          cp lookup-table.json intermediate.json

          for lut in mkosi.output.*/*/image-upload*.json; do
            jq -scS '.[0] * .[1]' intermediate.json "${lut}" > lookup-table.json
            cp lookup-table.json intermediate.json
          done

          rm -f intermediate.json

      - name: Upload lookup table to S3
        shell: bash
        run: |
          aws s3 cp \
            "lookup-table.json" \
            "s3://cdn-constellation-backend/${{ needs.build-settings.outputs.imageApiBasePath }}/info.json" \
            --no-progress
          (
            echo -e "Image version ([Lookup table](https://cdn.confidential.cloud/${{ needs.build-settings.outputs.imageApiBasePath }}/info.json)):"
            echo
            echo -e "\`\`\`"
            echo "${{ needs.build-settings.outputs.imageNameShort }}"
            echo -e "\`\`\`"
          ) >> "$GITHUB_STEP_SUMMARY"

      - name: Checkout
        uses: actions/checkout@755da8c3cf115ac066823e79a1e1788f8940201b # v3.2.0
        with:
          ref: ${{ !github.event.pull_request.head.repo.fork && github.head_ref || '' }}

      - name: Setup Go environment
        uses: actions/setup-go@6edd4406fa81c3da01a34fa6f6343087c207a568 # v3.5.0
        with:
          go-version: "1.19.4"
          cache: true

      - name: Update list of available OS image versions
        if: needs.build-settings.outputs.ref != '-'
        run: |
          go run main.go \
            --version "${{ needs.build-settings.outputs.imageVersion }}" \
            --stream "${{ inputs.stream }}" \
            --ref "${{ needs.build-settings.outputs.ref }}" \
            --latest
        working-directory: hack/add-version
