name: Build images (DockerHub)

on:
  release:
    types:
      - published
  push:
    branches:
      - '[0-9]+.[0-9]+'
      - 'trunk'
    paths:
      - 'Dockerfiles/**'
      - 'build.json'
      - '!**/README.md'
      - '!Dockerfiles/*/rhel/*'
      - '!Dockerfiles/*/windows/*'
      - '.github/workflows/images_build.yml'
  schedule:
    - cron:  '50 02 * * *'
  workflow_dispatch:

defaults:
  run:
    shell: bash

permissions:
   contents: read

env:
  TRUNK_ONLY_EVENT: ${{ contains(fromJSON('["schedule"]'), github.event_name) }}
  AUTO_PUSH_IMAGES: ${{ ! contains(fromJSON('["workflow_dispatch"]'), github.event_name) && vars.AUTO_PUSH_IMAGES }}

  DOCKER_REGISTRY: ${{ vars.DOCKER_REGISTRY }}
  DOCKER_REPOSITORY: ${{ vars.DOCKER_REPOSITORY }}
  LATEST_BRANCH: ${{ github.event.repository.default_branch }}
  TRUNK_GIT_BRANCH: "refs/heads/trunk"
  IMAGES_PREFIX: "zabbix-"

  BASE_BUILD_NAME: "build-base"
  BASE_CACHE_FILE_NAME: "base_image_metadata.json"
  BUILD_CACHE_FILE_NAME: "base_build_image_metadata.json"

  MATRIX_FILE: "build.json"
  DOCKERFILES_DIRECTORY: "./Dockerfiles"

  OIDC_ISSUER: "https://token.actions.githubusercontent.com"
  IDENTITY_REGEX: "https://github.com/zabbix/zabbix-docker/.github/"

  DOCKER_REGISTRY_TEST: "ghcr.io"
  DOCKER_REPOSITORY_TEST: "zabbix"

jobs:
  init_build:
    name: Initialize build
    runs-on: ubuntu-latest
    permissions:
      contents: read
    outputs:
      os: ${{ steps.os.outputs.list }}
      database: ${{ steps.database.outputs.list }}
      components: ${{ steps.components.outputs.list }}
      is_default_branch: ${{ steps.branch_info.outputs.is_default_branch }}
      current_branch: ${{ steps.branch_info.outputs.current_branch }}
      sha_short: ${{ steps.branch_info.outputs.sha_short }}
    steps:
      - name: Block egress traffic
        uses: step-security/harden-runner@20cf305ff2072d973412fa9b1e3a4f227bda3c76 # v2.14.0
        with:
          disable-sudo: true
          egress-policy: block
          allowed-endpoints: >
            api.github.com:443
            github.com:443
            objects.githubusercontent.com:443
            release-assets.githubusercontent.com:443

      - name: Checkout repository
        uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
        with:
          ref: ${{ env.TRUNK_ONLY_EVENT == 'true' && env.TRUNK_GIT_BRANCH || '' }}
          fetch-depth: 1
          sparse-checkout: ${{ env.MATRIX_FILE }}

      - name: Check ${{ env.MATRIX_FILE }} file
        id: build_exists
        env:
          MATRIX_FILE: ${{ env.MATRIX_FILE }}
        run: |
            if [[ ! -f "$MATRIX_FILE" ]]; then
                echo "::error::File $MATRIX_FILE is missing"
                exit 1
            fi

      - name: Prepare Operating System list
        id: os
        env:
          MATRIX_FILE: ${{ env.MATRIX_FILE }}
        run: |
            os_list=$(jq -r '.["os-linux"] |  keys | map(select(. != "rhel")) | [ .[] | tostring ] | @json' "$MATRIX_FILE")

            echo "::group::Operating System List"
            echo "$os_list"
            echo "::endgroup::"

            echo "list=$os_list" >> $GITHUB_OUTPUT

      - name: Prepare Database engine list
        id: database
        env:
          MATRIX_FILE: ${{ env.MATRIX_FILE }}
        run: |
            database_list=$(jq -r '[.components | values[].base ] | sort | unique | del(.. | select ( . == "" ) ) | @json' "$MATRIX_FILE")

            echo "::group::Database List"
            echo "$database_list"
            echo "::endgroup::"

            echo "list=$database_list" >> $GITHUB_OUTPUT

      - name: Prepare Zabbix component list
        id: components
        env:
          MATRIX_FILE: ${{ env.MATRIX_FILE }}
        run: |
            component_list=$(jq -r '.components | keys | @json' "$MATRIX_FILE")

            echo "::group::Zabbix Component List"
            echo "$component_list"
            echo "::endgroup::"

            echo "list=$component_list" >> $GITHUB_OUTPUT

      - name: Get branch info
        id: branch_info
        env:
          LATEST_BRANCH: ${{ env.LATEST_BRANCH }}
          github_ref: ${{ env.TRUNK_ONLY_EVENT == 'true' && env.TRUNK_GIT_BRANCH || github.ref }}
        run: |
            result=false
            sha_short=$(git rev-parse --short HEAD)

            if [[ "$github_ref" == "refs/tags/"* ]]; then
                github_ref=${github_ref%.*}
            fi

            github_ref=${github_ref##*/}

            if [[ "$github_ref" == "$LATEST_BRANCH" ]]; then
                result=true
            fi

            echo "::group::Branch data"
            echo "is_default_branch - $result"
            echo "current_branch - $github_ref"
            echo "sha_short - $sha_short"
            echo "::endgroup::"

            echo "is_default_branch=$result" >> $GITHUB_OUTPUT
            echo "current_branch=$github_ref" >> $GITHUB_OUTPUT
            echo "sha_short=$sha_short" >> $GITHUB_OUTPUT

  build_base:
    timeout-minutes: 30
    name: Build base on ${{ matrix.os }}
    needs: init_build
    strategy:
      fail-fast: false
      matrix:
        os: ${{ fromJson(needs.init_build.outputs.os) }}

    runs-on: ubuntu-latest
    permissions:
      contents: read
      id-token: write
      packages: write
      attestations: write
    steps:
      - name: Block egress traffic
        uses: step-security/harden-runner@20cf305ff2072d973412fa9b1e3a4f227bda3c76 # v2.14.0
        with:
          disable-sudo: true
          egress-policy: audit
          allowed-endpoints: >
            api.github.com:443
            release-assets.githubusercontent.com:443
            archive.ubuntu.com:80
            atl.mirrors.knownhost.com:443
            atl.mirrors.knownhost.com:80
            auth.docker.io:443
            cdn03.quay.io:443
            cdn01.quay.io:443
            cdn02.quay.io:443
            centos-stream-distro.1gservers.com:443
            centos-stream-distro.1gservers.com:80
            dfw.mirror.rackspace.com:443
            dfw.mirror.rackspace.com:80
            dl-cdn.alpinelinux.org:443
            download.cf.centos.org:443
            download.cf.centos.org:80
            epel.mirror.constant.com:443
            ftp-nyc.osuosl.org:443
            ftp-nyc.osuosl.org:80
            ftp-osl.osuosl.org:443
            ftp-osl.osuosl.org:80
            ftp.plusline.net:443
            ftp.plusline.net:80
            ftpmirror.your.org:80
            fulcio.sigstore.dev:443
            github.com:443
            ghcr.io:443
            iad.mirror.rackspace.com:443
            iad.mirror.rackspace.com:80
            index.docker.io:443
            lesnet.mm.fcix.net:443
            mirror-mci.yuki.net.uk:443
            mirror-mci.yuki.net.uk:80
            mirror.arizona.edu:443
            mirror.arizona.edu:80
            mirror.dogado.de:443
            mirror.dogado.de:80
            mirror.facebook.net:443
            mirror.facebook.net:80
            mirror.fcix.net:443
            mirror.hoobly.com:443
            mirror.math.princeton.edu:443
            mirror.netzwerge.de:443
            mirror.pilotfiber.com:443
            mirror.pilotfiber.com:80
            mirror.rackspace.com:443
            mirror.rackspace.com:80
            mirror.scaleuptech.com:443
            mirror.scaleuptech.com:80
            mirror.servaxnet.com:443
            mirror.servaxnet.com:80
            mirror.siena.edu:80
            mirror.stream.centos.org:443
            mirror.stream.centos.org:80
            mirror.team-cymru.com:443
            mirror.team-cymru.com:80
            mirror1.hs-esslingen.de:443
            mirrors.centos.org:443
            mirrors.fedoraproject.org:443
            mirrors.fedoraproject.org:80
            mirrors.iu13.net:80
            mirrors.mit.edu:443
            mirrors.ocf.berkeley.edu:443
            mirrors.ocf.berkeley.edu:80
            mirrors.sonic.net:443
            mirrors.wcupa.edu:443
            mirrors.wcupa.edu:80
            mirrors.xtom.de:80
            na.edge.kernel.org:443
            nocix.mm.fcix.net:443
            oauth2.sigstore.dev:443
            objects.githubusercontent.com:443
            ports.ubuntu.com:80
            production.cloudflare.docker.com:443
            quay.io:443
            registry-1.docker.io:443
            rekor.sigstore.dev:443
            repo.ialab.dsu.edu:443
            repos.eggycrew.com:443
            repos.eggycrew.com:80
            security.ubuntu.com:80
            tuf-repo-cdn.sigstore.dev:443
            uvermont.mm.fcix.net:443
            yum.oracle.com:443
            ziply.mm.fcix.net:443
            pkg-containers.githubusercontent.com:443
            raw.githubusercontent.com:443
            timestamp.sigstore.dev:443

      - name: Checkout repository
        uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
        with:
          ref: ${{ env.TRUNK_ONLY_EVENT == 'true' && env.TRUNK_GIT_BRANCH || '' }}
          fetch-depth: 1

      - name: Install cosign
        if: ${{ env.AUTO_PUSH_IMAGES == 'true' }}
        uses: sigstore/cosign-installer@faadad0cce49287aee09b3a48701e75088a2c6ad

      - name: Check cosign version
        if: ${{ env.AUTO_PUSH_IMAGES == 'true' }}
        run: cosign version

      - name: Set up QEMU
        uses: docker/setup-qemu-action@29109295f81e9208d7d86ff1c6c12d2833863392 # v3.6.0
        with:
          image: tonistiigi/binfmt:qemu-v8.1.5
          platforms: all

      - name: Set up Docker Buildx
        uses: docker/setup-buildx-action@b5ca514318bd6ebac0fb2aedd5d36ec1b5c232a2 # v3.10.0
        with:
          driver-opts: image=moby/buildkit:master

      - name: Prepare Platform list
        id: platform
        env:
          MATRIX_OS: ${{ matrix.os }}
          MATRIX_FILE: ${{ env.MATRIX_FILE }}
        run: |
            platform_list=$(jq -r ".[\"os-linux\"].$MATRIX_OS | join(\",\")" "$MATRIX_FILE")
            platform_list="${platform_list%,}"

            echo "::group::Platform List"
            echo "$platform_list"
            echo "::endgroup::"

            echo "list=$platform_list" >> $GITHUB_OUTPUT

      - name: Generate tags
        id: meta
        uses: docker/metadata-action@c1e51972afc2121e065aed6d45c65596fe445f3f # v5.8.0
        with:
          images: |
              ${{ format('{0}/{1}/{2}{3}', env.DOCKER_REGISTRY_TEST, env.DOCKER_REPOSITORY_TEST, env.IMAGES_PREFIX, env.BASE_BUILD_NAME ) }},enable=${{ env.AUTO_PUSH_IMAGES != 'true' }}
              ${{ format('{0}/{1}/{2}{3}', env.DOCKER_REGISTRY, env.DOCKER_REPOSITORY, env.IMAGES_PREFIX, env.BASE_BUILD_NAME ) }},enable=${{ env.AUTO_PUSH_IMAGES == 'true' }}
          context: ${{ env.TRUNK_ONLY_EVENT == 'true' && 'git' || '' }}
          tags: |
            type=semver,enable=${{ needs.init_build.outputs.current_branch != 'trunk' }},pattern={{version}},prefix=${{ matrix.os }}-
            type=semver,enable=${{ needs.init_build.outputs.current_branch != 'trunk' }},pattern={{version}},suffix=-${{ matrix.os }}
            type=ref,enable=${{ needs.init_build.outputs.current_branch != 'trunk' && !contains(fromJSON('["workflow_dispatch"]'), github.event_name) }},event=branch,prefix=${{ matrix.os }}-,suffix=-latest
            type=ref,enable=${{ needs.init_build.outputs.current_branch != 'trunk' && !contains(fromJSON('["workflow_dispatch"]'), github.event_name) }},event=branch,suffix=-${{ matrix.os }}-latest
            type=raw,enable=${{ (needs.init_build.outputs.current_branch != 'trunk') && (needs.init_build.outputs.is_default_branch == 'true') }},value=${{matrix.os}}-latest
            type=ref,enable=${{ needs.init_build.outputs.current_branch == 'trunk' }},event=branch,prefix=${{ matrix.os }}-
            type=ref,enable=${{ needs.init_build.outputs.current_branch == 'trunk' || contains(fromJSON('["workflow_dispatch"]'), github.event_name) }},event=branch,suffix=-${{ matrix.os }}
          flavor: |
            latest=${{ (matrix.os == 'alpine') && (!contains(fromJSON('["workflow_dispatch"]'), github.event_name)) && ( needs.init_build.outputs.is_default_branch == 'true' ) }}

      - name: Prepare cache data
        id: cache_data
        env:
          IMAGE_TAG: ${{ fromJSON(steps.meta.outputs.json).tags[0] }}
          PUBLISH_IMAGES: ${{ env.AUTO_PUSH_IMAGES == 'true' }}
        run: |
            cache_from=()
            cache_to=()

            cache_from+=("type=gha,scope=${IMAGE_TAG}")
            #cache_from+=("type=registry,ref=${IMAGE_TAG}")

            cache_to+=("type=gha,mode=max,scope=${IMAGE_TAG}")

            echo "::group::Cache from data"
            echo "${cache_from[*]}"
            echo "::endgroup::"

            echo "::group::Cache to data"
            echo "${cache_to[*]}"
            echo "::endgroup::"

            cache_from=$(printf '%s\n' "${cache_from[@]}")
            cache_to=$(printf '%s\n' "${cache_to[@]}")

            echo 'cache_from<<EOF' >> "$GITHUB_OUTPUT"
            echo "$cache_from" >> "$GITHUB_OUTPUT"
            echo 'EOF' >> "$GITHUB_OUTPUT"
            echo 'cache_to<<EOF' >> "$GITHUB_OUTPUT"
            echo "$cache_to" >> "$GITHUB_OUTPUT"
            echo 'EOF' >> "$GITHUB_OUTPUT"

      - name: Login to DockerHub
        if: ${{ env.AUTO_PUSH_IMAGES == 'true' }}
        uses: docker/login-action@5e57cd118135c172c3672efd75eb46360885c0ef # v3.6.0
        with:
          username: ${{ secrets.DOCKER_USERNAME }}
          password: ${{ secrets.DOCKER_PASSWORD }}

      - name: Login to ${{ env.DOCKER_REGISTRY_TEST }}
        if: ${{ env.AUTO_PUSH_IMAGES != 'true' }}
        uses: docker/login-action@5e57cd118135c172c3672efd75eb46360885c0ef # v3.6.0
        with:
          registry: ${{ env.DOCKER_REGISTRY_TEST }}
          username: ${{ github.actor }}
          password: ${{ secrets.GITHUB_TOKEN }}

      - name: Build and publish image
        id: docker_build
        uses: docker/build-push-action@471d1dc4e07e5cdedd4c2171150001c434f0b7a4 # v6.15.0
        with:
          context: ${{ format('{0}/{1}/{2}', env.DOCKERFILES_DIRECTORY, env.BASE_BUILD_NAME, matrix.os) }}
          file: ${{ format('{0}/{1}/{2}/Dockerfile', env.DOCKERFILES_DIRECTORY, env.BASE_BUILD_NAME, matrix.os) }}
          platforms: ${{ steps.platform.outputs.list }}
          push: true
          provenance: mode=max
          sbom: true
          tags: ${{ steps.meta.outputs.tags }}
          labels: |
            org.opencontainers.image.revision=${{ fromJSON(steps.meta.outputs.json).labels['org.opencontainers.image.revision'] }}
            org.opencontainers.image.created=${{ fromJSON(steps.meta.outputs.json).labels['org.opencontainers.image.created'] }}

      - name: Scan for vulnerabilities
        if: ${{ matrix.os != 'centos' }}
        uses: crazy-max/ghaction-container-scan@v3
        with:
          image: ${{ fromJSON(steps.meta.outputs.json).tags[0] }}
          annotations: true
          dockerfile: ${{ format('{0}/{1}/{2}/Dockerfile', env.DOCKERFILES_DIRECTORY, env.BASE_BUILD_NAME, matrix.os) }}

      - name: Sign the images with GitHub OIDC Token
        if: ${{ env.AUTO_PUSH_IMAGES == 'true' }}
        env:
          DIGEST: ${{ steps.docker_build.outputs.digest }}
          TAGS: ${{ steps.meta.outputs.tags }}
        run: |
            images=""
            for tag in ${TAGS}; do
                images+="${tag}@${DIGEST} "
            done

            echo "::group::Images to sign"
            echo "$images"
            echo "::endgroup::"

            echo "::group::Signing"
            echo "cosign sign --yes $images"
            cosign sign --yes ${images}
            echo "::endgroup::"

      - name: Attest images
        if: ${{ env.AUTO_PUSH_IMAGES == 'true' }}
        id: attest
        uses: actions/attest-build-provenance@v3
        with:
          subject-name: ${{ format('{0}/{1}/{2}{3}', env.DOCKER_REGISTRY, env.DOCKER_REPOSITORY, env.IMAGES_PREFIX, env.BASE_BUILD_NAME ) }}
          subject-digest: ${{ steps.docker_build.outputs.digest }}
          push-to-registry: true

      - name: Image metadata
        env:
          CACHE_FILE_NAME: ${{ env.BASE_CACHE_FILE_NAME }}
          METADATA: ${{ steps.docker_build.outputs.metadata }}
        run: |
            echo "::group::Image metadata"
            echo "${METADATA}"
            echo "::endgroup::"
            echo "::group::Cache file name"
            echo "${CACHE_FILE_NAME}"
            echo "::endgroup::"

            echo "${METADATA}" > "$CACHE_FILE_NAME"

      - name: Cache image metadata
        uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0
        with:
          path: ${{ env.BASE_CACHE_FILE_NAME }}
          key: ${{ env.BASE_BUILD_NAME }}-${{ matrix.os }}-${{ github.run_id }}

  build_base_database:
    timeout-minutes: 1240
    needs: [ "build_base", "init_build"]
    name: Build ${{ matrix.build }} base on ${{ matrix.os }}
    strategy:
      fail-fast: false
      matrix:
        build: ${{ fromJson(needs.init_build.outputs.database) }}
        os: ${{ fromJson(needs.init_build.outputs.os) }}
    runs-on: ubuntu-latest
    permissions:
      contents: read
      id-token: write
      packages: write
      attestations: write
    steps:
      - name: Block egress traffic
        uses: step-security/harden-runner@20cf305ff2072d973412fa9b1e3a4f227bda3c76 # v2.14.0
        with:
          disable-sudo: true
          egress-policy: block
          allowed-endpoints: >
            api.github.com:443
            release-assets.githubusercontent.com:443
            auth.docker.io:443
            git.zabbix.com:443
            github.com:443
            go.googlesource.com:443
            go.mongodb.org:443
            golang.org:443
            google.golang.org:443
            golang.zabbix.com:443
            gopkg.in:443
            ghcr.io:443
            index.docker.io:443
            noto-website-2.storage.googleapis.com:443
            production.cloudflare.docker.com:443
            proxy.golang.org:443
            registry-1.docker.io:443
            storage.googleapis.com:443
            fulcio.sigstore.dev:443
            oauth2.sigstore.dev:443
            objects.githubusercontent.com:443
            tuf-repo-cdn.sigstore.dev:443
            rekor.sigstore.dev:443
            pkg-containers.githubusercontent.com:443
            raw.githubusercontent.com:443
            timestamp.sigstore.dev:443

      - name: Checkout repository
        uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
        with:
          ref: ${{ env.TRUNK_ONLY_EVENT == 'true' && env.TRUNK_GIT_BRANCH || '' }}
          fetch-depth: 1

      - name: Install cosign
        if: ${{ env.AUTO_PUSH_IMAGES == 'true' }}
        uses: sigstore/cosign-installer@faadad0cce49287aee09b3a48701e75088a2c6ad

      - name: Check cosign version
        if: ${{ env.AUTO_PUSH_IMAGES == 'true' }}
        run: cosign version

      - name: Set up QEMU
        uses: docker/setup-qemu-action@29109295f81e9208d7d86ff1c6c12d2833863392 # v3.6.0
        with:
          image: tonistiigi/binfmt:qemu-v8.1.5
          platforms: all

      - name: Set up Docker Buildx
        uses: docker/setup-buildx-action@b5ca514318bd6ebac0fb2aedd5d36ec1b5c232a2 # v3.10.0
        with:
          driver-opts: image=moby/buildkit:master

      - name: Prepare Platform list
        id: platform
        env:
          MATRIX_OS: ${{ matrix.os }}
          MATRIX_FILE: ${{ env.MATRIX_FILE }}
        run: |
            platform_list=$(jq -r ".[\"os-linux\"].$MATRIX_OS | join(\",\")" "$MATRIX_FILE")
            platform_list="${platform_list%,}"

            echo "::group::Platform List"
            echo "$platform_list"
            echo "::endgroup::"

            echo "list=$platform_list" >> $GITHUB_OUTPUT

      - name: Generate tags
        id: meta
        uses: docker/metadata-action@c1e51972afc2121e065aed6d45c65596fe445f3f # v5.8.0
        with:
          images: |
              ${{ format('{0}/{1}/{2}{3}', env.DOCKER_REGISTRY_TEST, env.DOCKER_REPOSITORY_TEST, env.IMAGES_PREFIX, matrix.build ) }},enable=${{ env.AUTO_PUSH_IMAGES != 'true' }}
              ${{ format('{0}/{1}/{2}{3}', env.DOCKER_REGISTRY, env.DOCKER_REPOSITORY, env.IMAGES_PREFIX, matrix.build ) }},enable=${{ env.AUTO_PUSH_IMAGES == 'true' }}
          context: ${{ env.TRUNK_ONLY_EVENT == 'true' && 'git' || '' }}
          tags: |
            type=semver,enable=${{ needs.init_build.outputs.current_branch != 'trunk' }},pattern={{version}},prefix=${{ matrix.os }}-
            type=semver,enable=${{ needs.init_build.outputs.current_branch != 'trunk' }},pattern={{version}},suffix=-${{ matrix.os }}
            type=ref,enable=${{ needs.init_build.outputs.current_branch != 'trunk' && (!contains(fromJSON('["workflow_dispatch"]'), github.event_name)) }},event=branch,prefix=${{ matrix.os }}-,suffix=-latest
            type=ref,enable=${{ needs.init_build.outputs.current_branch != 'trunk' && (!contains(fromJSON('["workflow_dispatch"]'), github.event_name)) }},event=branch,suffix=-${{ matrix.os }}-latest
            type=raw,enable=${{ (needs.init_build.outputs.current_branch != 'trunk') && (needs.init_build.outputs.is_default_branch == 'true') }},value=${{matrix.os}}-latest
            type=ref,enable=${{ needs.init_build.outputs.current_branch == 'trunk' }},event=branch,prefix=${{ matrix.os }}-
            type=ref,enable=${{ needs.init_build.outputs.current_branch == 'trunk' || contains(fromJSON('["workflow_dispatch"]'), github.event_name) }},event=branch,suffix=-${{ matrix.os }}
          flavor: |
            latest=${{ (matrix.os == 'alpine') && (!contains(fromJSON('["workflow_dispatch"]'), github.event_name)) && ( needs.init_build.outputs.is_default_branch == 'true' ) }}

      - name: Download metadata of ${{ env.BASE_BUILD_NAME }}:${{ matrix.os }}
        uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0
        with:
          path: ${{ env.BASE_CACHE_FILE_NAME }}
          key: ${{ env.BASE_BUILD_NAME }}-${{ matrix.os }}-${{ github.run_id }}

      - name: Process ${{ env.BASE_BUILD_NAME }}:${{ matrix.os }} image metadata
        id: base_build
        env:
          CACHE_FILE_NAME: ${{ env.BASE_CACHE_FILE_NAME }}
        run: |
            echo "::group::Base image metadata"
            cat "${CACHE_FILE_NAME}"
            echo "::endgroup::"

            IMAGE_DIGEST=$(jq -r '."containerimage.digest"' "${CACHE_FILE_NAME}")
            IMAGE_NAME=$(jq -r '."image.name"' "${CACHE_FILE_NAME}" | cut -d: -f1)

            echo "base_build_image=${IMAGE_NAME}@${IMAGE_DIGEST}" >> $GITHUB_OUTPUT

      - name: Verify ${{ env.BASE_BUILD_NAME }}:${{ matrix.os }} cosign
        if: ${{ env.AUTO_PUSH_IMAGES == 'true' }}
        env:
         BASE_IMAGE: ${{ steps.base_build.outputs.base_build_image }}
         OIDC_ISSUER: ${{ env.OIDC_ISSUER }}
         IDENTITY_REGEX: ${{ env.IDENTITY_REGEX }}
        run: |
            echo "::group::Image sign data"
            echo "OIDC issuer=$OIDC_ISSUER"
            echo "Identity=$IDENTITY_REGEX"
            echo "Image to verify=$BASE_IMAGE"
            echo "::endgroup::"

            echo "::group::Verify signature"
            cosign verify \
                    --certificate-oidc-issuer-regexp "$OIDC_ISSUER" \
                    --certificate-identity-regexp "$IDENTITY_REGEX" \
                "$BASE_IMAGE" | jq
            echo "::endgroup::"

      - name: Prepare cache data
        id: cache_data
        env:
          BASE_IMAGE_TAG: ${{ steps.base_build.outputs.base_build_image }}
          IMAGE_TAG: ${{ fromJSON(steps.meta.outputs.json).tags[0] }}
          PUBLISH_IMAGES: ${{ env.AUTO_PUSH_IMAGES == 'true' }}
        run: |
            cache_from=()
            cache_to=()

            cache_from+=("type=gha,scope=${BASE_IMAGE_TAG}")
            cache_from+=("type=registry,ref=${BASE_IMAGE_TAG}")
            cache_from+=("type=gha,scope=${IMAGE_TAG}")
            cache_from+=("type=registry,ref=${IMAGE_TAG}")

            cache_to+=("type=gha,mode=max,scope=${IMAGE_TAG}")

            echo "::group::Cache from data"
            echo "${cache_from[*]}"
            echo "::endgroup::"

            echo "::group::Cache to data"
            echo "${cache_to[*]}"
            echo "::endgroup::"

            cache_from=$(printf '%s\n' "${cache_from[@]}")
            cache_to=$(printf '%s\n' "${cache_to[@]}")

            echo 'cache_from<<EOF' >> "$GITHUB_OUTPUT"
            echo "$cache_from" >> "$GITHUB_OUTPUT"
            echo 'EOF' >> "$GITHUB_OUTPUT"
            echo 'cache_to<<EOF' >> "$GITHUB_OUTPUT"
            echo "$cache_to" >> "$GITHUB_OUTPUT"
            echo 'EOF' >> "$GITHUB_OUTPUT"

      - name: Login to DockerHub
        if: ${{ env.AUTO_PUSH_IMAGES == 'true' }}
        uses: docker/login-action@5e57cd118135c172c3672efd75eb46360885c0ef # v3.6.0
        with:
          username: ${{ secrets.DOCKER_USERNAME }}
          password: ${{ secrets.DOCKER_PASSWORD }}

      - name: Login to ${{ env.DOCKER_REGISTRY_TEST }}
        if: ${{ env.AUTO_PUSH_IMAGES != 'true' }}
        uses: docker/login-action@5e57cd118135c172c3672efd75eb46360885c0ef # v3.6.0
        with:
          registry: ${{ env.DOCKER_REGISTRY_TEST }}
          username: ${{ github.actor }}
          password: ${{ secrets.GITHUB_TOKEN }}

      - name: Build ${{ matrix.build }}/${{ matrix.os }} and push
        id: docker_build
        uses: docker/build-push-action@471d1dc4e07e5cdedd4c2171150001c434f0b7a4 # v6.15.0
        with:
          context: ${{ format('{0}/{1}/{2}/', env.DOCKERFILES_DIRECTORY, matrix.build, matrix.os) }}
          build-contexts: |
            sources=./sources/
            config_templates=./config_templates/
          file: ${{ format('{0}/{1}/{2}/Dockerfile', env.DOCKERFILES_DIRECTORY, matrix.build, matrix.os) }}
          platforms: ${{ steps.platform.outputs.list }}
          push: true
          provenance: mode=max
          sbom: true
          tags: ${{ steps.meta.outputs.tags }}
          build-args: BUILD_BASE_IMAGE=${{ steps.base_build.outputs.base_build_image }}
          labels: |
            org.opencontainers.image.revision=${{ fromJSON(steps.meta.outputs.json).labels['org.opencontainers.image.revision'] }}
            org.opencontainers.image.created=${{ fromJSON(steps.meta.outputs.json).labels['org.opencontainers.image.created'] }}

      - name: Sign the images with GitHub OIDC Token
        if: ${{ env.AUTO_PUSH_IMAGES == 'true' }}
        env:
          DIGEST: ${{ steps.docker_build.outputs.digest }}
          TAGS: ${{ steps.meta.outputs.tags }}
        run: |
            images=""
            for tag in ${TAGS}; do
                images+="${tag}@${DIGEST} "
            done

            echo "::group::Images to sign"
            echo "$images"
            echo "::endgroup::"

            echo "::group::Signing"
            echo "cosign sign --yes $images"
            cosign sign --yes ${images}
            echo "::endgroup::"

      - name: Attest images
        if: ${{ env.AUTO_PUSH_IMAGES == 'true' }}
        id: attest
        uses: actions/attest-build-provenance@v3
        with:
          subject-name: ${{ format('{0}/{1}/{2}{3}', env.DOCKER_REGISTRY, env.DOCKER_REPOSITORY, env.IMAGES_PREFIX, matrix.build ) }}
          subject-digest: ${{ steps.docker_build.outputs.digest }}
          push-to-registry: true

      - name: Image metadata
        env:
          CACHE_FILE_NAME: ${{ env.BUILD_CACHE_FILE_NAME }}
          METADATA: ${{ steps.docker_build.outputs.metadata }}
        run: |
            echo "::group::Image metadata"
            echo "${METADATA}"
            echo "::endgroup::"
            echo "::group::Cache file name"
            echo "${CACHE_FILE_NAME}"
            echo "::endgroup::"

            echo "${METADATA}" > "$CACHE_FILE_NAME"

      - name: Cache image metadata
        uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0
        with:
          path: ${{ env.BUILD_CACHE_FILE_NAME }}
          key: ${{ matrix.build }}-${{ matrix.os }}-${{ github.run_id }}

  build_images:
    timeout-minutes: 90
    needs: [ "build_base_database", "init_build"]
    name: Build ${{ matrix.build }} on ${{ matrix.os }}
    strategy:
      fail-fast: false
      matrix:
        build: ${{ fromJson(needs.init_build.outputs.components) }}
        os: ${{ fromJson(needs.init_build.outputs.os) }}

    runs-on: ubuntu-latest
    permissions:
      contents: read
      id-token: write
      packages: write
      attestations: write
    steps:
      - name: Block egress traffic
        uses: step-security/harden-runner@20cf305ff2072d973412fa9b1e3a4f227bda3c76 # v2.14.0
        with:
          disable-sudo: true
          egress-policy: block
          allowed-endpoints: >
            api.github.com:443
            release-assets.githubusercontent.com:443
            apt.postgresql.org:80
            auth.docker.io:443
            dl-cdn.alpinelinux.org:443
            github.com:443
            index.docker.io:443
            production.cloudflare.docker.com:443
            registry-1.docker.io:443
            fulcio.sigstore.dev:443
            objects.githubusercontent.com:443
            tuf-repo-cdn.sigstore.dev:443
            rekor.sigstore.dev:443
            api.github.com:443
            atl.mirrors.knownhost.com:443
            atl.mirrors.knownhost.com:80
            auth.docker.io:443
            cdn03.quay.io:443
            cdn02.quay.io:443
            cdn01.quay.io:443
            centos-stream-distro.1gservers.com:443
            centos-stream-distro.1gservers.com:80
            download.cf.centos.org:443
            download.cf.centos.org:80
            d2lzkl7pfhq30w.cloudfront.net:443
            epel.mirror.constant.com:80
            forksystems.mm.fcix.net:80
            ftp-nyc.osuosl.org:443
            ftp-nyc.osuosl.org:80
            ftp-osl.osuosl.org:443
            ftp-osl.osuosl.org:80
            ftp.plusline.net:80
            ftpmirror.your.org:80
            github.com:443
            iad.mirror.rackspace.com:443
            index.docker.io:443
            ix-denver.mm.fcix.net:443
            mirror-mci.yuki.net.uk:443
            mirror.23m.com:80
            mirror.arizona.edu:80
            mirror.dal.nexril.net:80
            mirror.de.leaseweb.net:80
            mirror.dogado.de:80
            mirror.facebook.net:80
            mirror.hoobly.com:80
            mirror.math.princeton.edu:80
            mirror.netcologne.de:443
            mirror.netzwerge.de:443
            mirror.pilotfiber.com:443
            mirror.pilotfiber.com:80
            mirror.rackspace.com:443
            mirror.rackspace.com:80
            mirror.scaleuptech.com:443
            mirror.servaxnet.com:443
            mirror.servaxnet.com:80
            mirror.sfo12.us.leaseweb.net:80
            mirror.siena.edu:80
            mirror.steadfastnet.com:80
            mirror.team-cymru.com:443
            mirror.team-cymru.com:80
            mirror.umd.edu:443
            mirror1.hs-esslingen.de:443
            mirrors.centos.org:443
            mirrors.fedoraproject.org:443
            mirrors.iu13.net:443
            mirrors.iu13.net:80
            mirrors.ocf.berkeley.edu:443
            mirrors.sonic.net:80
            mirrors.syringanetworks.net:80
            mirrors.vcea.wsu.edu:80
            mirrors.wcupa.edu:80
            mirrors.xtom.de:80
            na.edge.kernel.org:443
            nnenix.mm.fcix.net:80
            ohioix.mm.fcix.net:80
            production.cloudflare.docker.com:443
            pubmirror1.math.uh.edu:443
            pubmirror3.math.uh.edu:80
            quay.io:443
            ghcr.io:443
            registry-1.docker.io:443
            repo.ialab.dsu.edu:80
            repos.eggycrew.com:80
            uvermont.mm.fcix.net:80
            ziply.mm.fcix.net:443
            fulcio.sigstore.dev:443
            objects.githubusercontent.com:443
            tuf-repo-cdn.sigstore.dev:443
            rekor.sigstore.dev:443
            oauth2.sigstore.dev:443
            api.github.com:443
            auth.docker.io:443
            github.com:443
            index.docker.io:443
            production.cloudflare.docker.com:443
            registry-1.docker.io:443
            yum.oracle.com:443
            fulcio.sigstore.dev:443
            objects.githubusercontent.com:443
            tuf-repo-cdn.sigstore.dev:443
            rekor.sigstore.dev:443
            api.github.com:443
            archive.ubuntu.com:80
            auth.docker.io:443
            deb.debian.org:80
            github.com:443
            index.docker.io:443
            keyserver.ubuntu.com:11371
            nginx.org:443
            nginx.org:80
            ports.ubuntu.com:80
            production.cloudflare.docker.com:443
            registry-1.docker.io:443
            security.ubuntu.com:80
            fulcio.sigstore.dev:443
            objects.githubusercontent.com:443
            tuf-repo-cdn.sigstore.dev:443
            rekor.sigstore.dev:443
            pkg-containers.githubusercontent.com:443
            raw.githubusercontent.com:443
            timestamp.sigstore.dev:443

      - name: Checkout repository
        uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
        with:
          ref: ${{ env.TRUNK_ONLY_EVENT == 'true' && env.TRUNK_GIT_BRANCH || '' }}
          fetch-depth: 1

      - name: Install cosign
        if: ${{ env.AUTO_PUSH_IMAGES == 'true' }}
        uses: sigstore/cosign-installer@faadad0cce49287aee09b3a48701e75088a2c6ad

      - name: Check cosign version
        if: ${{ env.AUTO_PUSH_IMAGES == 'true' }}
        run: cosign version

      - name: Set up QEMU
        uses: docker/setup-qemu-action@29109295f81e9208d7d86ff1c6c12d2833863392 # v3.6.0
        with:
          image: tonistiigi/binfmt:qemu-v8.1.5
          platforms: all

      - name: Set up Docker Buildx
        uses: docker/setup-buildx-action@b5ca514318bd6ebac0fb2aedd5d36ec1b5c232a2 # v3.10.0
        with:
          driver-opts: image=moby/buildkit:master

      - name: Prepare Platform list
        id: platform
        env:
          MATRIX_OS: ${{ matrix.os }}
          MATRIX_BUILD: ${{ matrix.build }}
          MATRIX_FILE: ${{ env.MATRIX_FILE }}
        run: |
            # Chromium on Alpine is available only on linux/amd64, linux/arm64 platforms
            if ([ "$MATRIX_OS" == "alpine" ] || [ "$MATRIX_OS" == "centos" ]) && [ "$MATRIX_BUILD" == "web-service" ]; then
                platform_list="linux/amd64,linux/arm64"
            # Chromium on Ubuntu is not available on s390x and armhf platform
            elif [ "$MATRIX_OS" == "ubuntu" ] && [ "$MATRIX_BUILD" == "web-service" ]; then
                platform_list="linux/amd64,linux/arm64"
            # Chromium on Oracle Linux is available only on amd64 platform
            elif [ "$MATRIX_OS" == "ol" ] && [ "$MATRIX_BUILD" == "web-service" ]; then
                platform_list="linux/amd64"
            else
                platform_list=$(jq -r ".[\"os-linux\"].\"$MATRIX_OS\" | join(\",\")" "$MATRIX_FILE")
            fi

            # Build only Agent and Agent2 on 386
            if [ "$MATRIX_BUILD" != "agent"* ]; then
                platform_list="${platform_list#linux/386,}"
            fi

            platform_list="${platform_list%,}"

            echo "::group::Platform List"
            echo "$platform_list"
            echo "::endgroup::"

            echo "list=$platform_list" >> $GITHUB_OUTPUT

      - name: Detect Build Base Image
        id: build_base_image
        env:
          MATRIX_BUILD: ${{ matrix.build }}
          MATRIX_FILE: ${{ env.MATRIX_FILE }}
        run: |
            BUILD_BASE=$(jq -r ".components.\"$MATRIX_BUILD\".base" "$MATRIX_FILE")

            echo "::group::Base Build Image"
            echo "$BUILD_BASE"
            echo "::endgroup::"

            echo "build_base=${BUILD_BASE}" >> $GITHUB_OUTPUT

      - name: Generate tags
        id: meta
        uses: docker/metadata-action@c1e51972afc2121e065aed6d45c65596fe445f3f # v5.8.0
        with:
          images:  |
              ${{ format('{0}/{1}/{2}{3}', env.DOCKER_REGISTRY_TEST, env.DOCKER_REPOSITORY_TEST, env.IMAGES_PREFIX, matrix.build ) }},enable=${{ env.AUTO_PUSH_IMAGES != 'true' }}
              ${{ format('{0}/{1}/{2}{3}', env.DOCKER_REGISTRY, env.DOCKER_REPOSITORY, env.IMAGES_PREFIX, matrix.build ) }},enable=${{ env.AUTO_PUSH_IMAGES == 'true' }}
          context: ${{ env.TRUNK_ONLY_EVENT == 'true' && 'git' || '' }}
          tags: |
            type=semver,enable=${{ needs.init_build.outputs.current_branch != 'trunk' }},pattern={{version}},prefix=${{ matrix.os }}-
            type=semver,enable=${{ needs.init_build.outputs.current_branch != 'trunk' }},pattern={{version}},suffix=-${{ matrix.os }}
            type=ref,enable=${{ needs.init_build.outputs.current_branch != 'trunk' && !contains(fromJSON('["workflow_dispatch"]'), github.event_name) }},event=branch,prefix=${{ matrix.os }}-,suffix=-latest
            type=ref,enable=${{ needs.init_build.outputs.current_branch != 'trunk' && !contains(fromJSON('["workflow_dispatch"]'), github.event_name) }},event=branch,suffix=-${{ matrix.os }}-latest
            type=raw,enable=${{ (needs.init_build.outputs.current_branch != 'trunk') && (needs.init_build.outputs.is_default_branch == 'true') }},value=${{matrix.os}}-latest
            type=ref,enable=${{ needs.init_build.outputs.current_branch == 'trunk' }},event=branch,prefix=${{ matrix.os }}-
            type=ref,enable=${{ needs.init_build.outputs.current_branch == 'trunk' || contains(fromJSON('["workflow_dispatch"]'), github.event_name) }},event=branch,suffix=-${{ matrix.os }}
          flavor: |
            latest=${{ (matrix.os == 'alpine') && (!contains(fromJSON('["workflow_dispatch"]'), github.event_name)) && ( needs.init_build.outputs.is_default_branch == 'true' ) }}

      - name: Download metadata of ${{ steps.build_base_image.outputs.build_base }}:${{ matrix.os }}
        uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0
        if: ${{ matrix.build != 'snmptraps' }}
        with:
          path: ${{ env.BUILD_CACHE_FILE_NAME }}
          key: ${{ steps.build_base_image.outputs.build_base }}-${{ matrix.os }}-${{ github.run_id }}

      - name: Process ${{ steps.build_base_image.outputs.build_base }}:${{ matrix.os }} image metadata
        id: base_build
        if: ${{ matrix.build != 'snmptraps' }}
        env:
          CACHE_FILE_NAME: ${{ env.BUILD_CACHE_FILE_NAME }}
        run: |
            echo "::group::Base build image metadata"
            cat "${CACHE_FILE_NAME}"
            echo "::endgroup::"

            IMAGE_DIGEST=$(jq -r '."containerimage.digest"' "${CACHE_FILE_NAME}")
            IMAGE_NAME=$(jq -r '."image.name"' "${CACHE_FILE_NAME}" | cut -d: -f1)

            echo "base_build_image=${IMAGE_NAME}@${IMAGE_DIGEST}" >> $GITHUB_OUTPUT

      - name: Verify ${{ steps.build_base_image.outputs.build_base }}:${{ matrix.os }} cosign
        if: ${{ matrix.build != 'snmptraps' && env.AUTO_PUSH_IMAGES == 'true' }}
        env:
         BASE_IMAGE: ${{ steps.base_build.outputs.base_build_image }}
         OIDC_ISSUER: ${{ env.OIDC_ISSUER }}
         IDENTITY_REGEX: ${{ env.IDENTITY_REGEX }}
        run: |
            echo "::group::Image sign data"
            echo "OIDC issuer=${OIDC_ISSUER}"
            echo "Identity=${IDENTITY_REGEX}"
            echo "Image to verify=${BASE_IMAGE}"
            echo "::endgroup::"

            echo "::group::Verify signature"
            cosign verify \
                    --certificate-oidc-issuer-regexp "${OIDC_ISSUER}" \
                    --certificate-identity-regexp "${IDENTITY_REGEX}" \
                "${BASE_IMAGE}" | jq
            echo "::endgroup::"

      - name: Prepare cache data
        if: ${{ matrix.build != 'snmptraps' }}
        id: cache_data
        env:
          BASE_IMAGE_TAG: ${{ steps.base_build.outputs.base_build_image }}
        run: |
            cache_from=()
            cache_to=()

            cache_from+=("type=registry,ref=${BASE_IMAGE_TAG}")

            echo "::group::Cache from data"
            echo "${cache_from[*]}"
            echo "::endgroup::"

            cache_from=$(printf '%s\n' "${cache_from[@]}")

            echo 'cache_from<<EOF' >> "$GITHUB_OUTPUT"
            echo "$cache_from" >> "$GITHUB_OUTPUT"
            echo 'EOF' >> "$GITHUB_OUTPUT"

      - name: Login to DockerHub
        if: ${{ env.AUTO_PUSH_IMAGES == 'true' }}
        uses: docker/login-action@5e57cd118135c172c3672efd75eb46360885c0ef # v3.6.0
        with:
          username: ${{ secrets.DOCKER_USERNAME }}
          password: ${{ secrets.DOCKER_PASSWORD }}

      - name: Build and push image
        id: docker_build
        uses: docker/build-push-action@471d1dc4e07e5cdedd4c2171150001c434f0b7a4 # v6.15.0
        with:
          context: ${{ format('{0}/{1}/{2}', env.DOCKERFILES_DIRECTORY, matrix.build, matrix.os) }}
          file: ${{ format('{0}/{1}/{2}/Dockerfile', env.DOCKERFILES_DIRECTORY, matrix.build, matrix.os) }}
          platforms: ${{ steps.platform.outputs.list }}
          push: ${{ env.AUTO_PUSH_IMAGES == 'true' }}
          provenance: mode=max
          sbom: ${{ env.AUTO_PUSH_IMAGES == 'true' }}
          tags: ${{ steps.meta.outputs.tags }}
          build-args: BUILD_BASE_IMAGE=${{ steps.base_build.outputs.base_build_image }}
          labels: |
            org.opencontainers.image.revision=${{ fromJSON(steps.meta.outputs.json).labels['org.opencontainers.image.revision'] }}
            org.opencontainers.image.created=${{ fromJSON(steps.meta.outputs.json).labels['org.opencontainers.image.created'] }}

      - name: Sign the images with GitHub OIDC Token
        if: ${{ env.AUTO_PUSH_IMAGES == 'true' }}
        env:
          DIGEST: ${{ steps.docker_build.outputs.digest }}
          TAGS: ${{ steps.meta.outputs.tags }}
        run: |
            images=""
            for tag in ${TAGS}; do
                images+="${tag}@${DIGEST} "
            done

            echo "::group::Images to sign"
            echo "$images"
            echo "::endgroup::"

            echo "::group::Signing"
            echo "cosign sign --yes $images"
            cosign sign --yes ${images}
            echo "::endgroup::"

      - name: Attest images
        if: ${{ env.AUTO_PUSH_IMAGES == 'true' }}
        id: attest
        uses: actions/attest-build-provenance@v3
        with:
          subject-name:  ${{ format('{0}/{1}/{2}{3}', env.DOCKER_REGISTRY, env.DOCKER_REPOSITORY, env.IMAGES_PREFIX, matrix.build ) }}
          subject-digest: ${{ steps.docker_build.outputs.digest }}
          push-to-registry: true

      - name: Image metadata
        if: ${{ env.AUTO_PUSH_IMAGES == 'true' }}
        env:
          METADATA: ${{ steps.docker_build.outputs.metadata }}
        run: |
            echo "::group::Image metadata"
            echo "${METADATA}"
            echo "::endgroup::"
