name: build on: push: branches: - main paths: - "src/**" - "Dockerfile" - "go.mod" - "go.sum" release: types: [published] workflow_dispatch: permissions: attestations: write contents: read id-token: write packages: write jobs: build: name: build (${{ matrix.platform }}) runs-on: ${{ matrix.runner }} if: github.event_name != 'pull_request' strategy: fail-fast: false matrix: include: - runner: ubuntu-24.04 platform: linux/amd64 arch: x86_64 - runner: ubuntu-24.04-arm platform: linux/arm64 arch: aarch64 - runner: ubuntu-24.04-arm platform: linux/arm/v7 arch: armv7 steps: - name: Checkout uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 #v6.0.1 - name: Docker meta id: docker_meta uses: docker/metadata-action@c299e40c65443455700f0fdfc63efafe5b349051 #v5.10.0 with: images: | ${{ github.repository }} ghcr.io/${{ github.repository }} tags: | type=edge type=ref,event=branch,enable={{is_not_default_branch}} type=semver,pattern={{version}} type=semver,pattern={{major}} type=semver,pattern={{major}}.{{minor}} env: DOCKER_METADATA_ANNOTATIONS_LEVELS: manifest,index - name: Set up QEMU (${{ matrix.platform }}) if: matrix.platform == 'linux/arm/v7' uses: docker/setup-qemu-action@c7c53464625b32c7a7e944ae62b3e17d2b600130 #v3.7.0 with: platforms: ${{ matrix.platform }} - name: Set up Docker Buildx uses: docker/setup-buildx-action@e468171a9de216ec08956ac3ada2f0791b6bd435 #v3.11.1 - name: Login to DockerHub uses: docker/login-action@5e57cd118135c172c3672efd75eb46360885c0ef #v3.6.0 with: username: ${{ vars.DOCKERHUB_USERNAME }} password: ${{ secrets.DOCKERHUB_TOKEN }} - name: Login to GitHub Container Registry uses: docker/login-action@5e57cd118135c172c3672efd75eb46360885c0ef #v3.6.0 with: registry: ghcr.io username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} - name: Build and push (${{ matrix.platform }}) id: docker_build uses: docker/build-push-action@263435318d21b8e681c14492fe198d362a7d2c83 #v6.18.0 with: context: . platforms: ${{ matrix.platform }} outputs: type=image,push-by-digest=true,name-canonical=true,push=true annotations: ${{ steps.docker_meta.outputs.annotations }} labels: ${{ steps.docker_meta.outputs.labels }} tags: | ${{ github.repository }} ghcr.io/${{ github.repository }} cache-from: type=gha,scope=${{ matrix.platform }} cache-to: type=gha,scope=${{ matrix.platform }},mode=max sbom: true build-args: | apiVersion=${{ fromJSON(steps.docker_meta.outputs.json).labels['org.opencontainers.image.version'] }} - name: Export digest run: | mkdir -p ${{ runner.temp }}/digests digest="${{ steps.docker_build.outputs.digest }}" touch "${{ runner.temp }}/digests/${digest#sha256:}" - name: Upload digest id: upload_digests uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 #v5.0.0 with: name: digests-${{ matrix.arch }} path: ${{ runner.temp }}/digests/* if-no-files-found: error retention-days: 1 - name: Attest digest (per-arch) uses: actions/attest-build-provenance@977bb373ede98d70efdf65b84cb5f73e068dcc2a #v3.0.0 with: subject-name: digests-${{ matrix.arch }} subject-digest: sha256:${{ steps.upload_digests.outputs.artifact-digest }} manifest: name: build (multi-arch) runs-on: ubuntu-latest needs: - build outputs: docker_build_digest: ${{ steps.docker_build.outputs.digest }} docker_meta_version: ${{ steps.docker_meta.outputs.version }} steps: - name: Checkout uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 #v6.0.1 - name: Docker meta id: docker_meta uses: docker/metadata-action@c299e40c65443455700f0fdfc63efafe5b349051 #v5.10.0 with: images: | ${{ github.repository }} ghcr.io/${{ github.repository }} tags: | type=edge type=ref,event=branch,enable={{is_not_default_branch}} type=semver,pattern={{version}} type=semver,pattern={{major}} type=semver,pattern={{major}}.{{minor}} env: DOCKER_METADATA_ANNOTATIONS_LEVELS: manifest,index - name: Download digests uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 #v7.0.0 with: path: ${{ runner.temp }}/digests pattern: digests-* merge-multiple: true - name: Install Cosign uses: sigstore/cosign-installer@faadad0cce49287aee09b3a48701e75088a2c6ad #v4.0.0 - name: Set up Docker Buildx uses: docker/setup-buildx-action@e468171a9de216ec08956ac3ada2f0791b6bd435 #v3.11.1 - name: Login to DockerHub uses: docker/login-action@5e57cd118135c172c3672efd75eb46360885c0ef #v3.6.0 with: username: ${{ vars.DOCKERHUB_USERNAME }} password: ${{ secrets.DOCKERHUB_TOKEN }} - name: Login to GitHub Container Registry uses: docker/login-action@5e57cd118135c172c3672efd75eb46360885c0ef #v3.6.0 with: registry: ghcr.io username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} - name: Create manifest list and push id: docker_build working-directory: ${{ runner.temp }}/digests run: | # Create the manifest list and push to both registries docker buildx imagetools create $(jq -cr '.tags | map("-t " + .) | join(" ")' <<< "$DOCKER_METADATA_OUTPUT_JSON") \ --annotation "index:org.opencontainers.image.created=${{ fromJSON(steps.docker_meta.outputs.json).labels['org.opencontainers.image.created'] }}" \ --annotation "index:org.opencontainers.image.description=${{ fromJSON(steps.docker_meta.outputs.json).labels['org.opencontainers.image.description'] }}" \ --annotation "index:org.opencontainers.image.licenses=${{ fromJSON(steps.docker_meta.outputs.json).labels['org.opencontainers.image.licenses'] }}" \ --annotation "index:org.opencontainers.image.revision=${{ fromJSON(steps.docker_meta.outputs.json).labels['org.opencontainers.image.revision'] }}" \ --annotation "index:org.opencontainers.image.source=${{ fromJSON(steps.docker_meta.outputs.json).labels['org.opencontainers.image.source'] }}" \ --annotation "index:org.opencontainers.image.version=${{ fromJSON(steps.docker_meta.outputs.json).labels['org.opencontainers.image.version'] }}" \ $(printf '${{ github.repository }}@sha256:%s ' *) \ $(printf 'ghcr.io/${{ github.repository }}@sha256:%s ' *) # Get the digest of the created manifest list DIGEST=$(docker buildx imagetools inspect ghcr.io/${{ github.repository }}:${{ steps.docker_meta.outputs.version }} --format '{{ json .Manifest.Digest }}' | jq -r .) echo "digest=$DIGEST" >> $GITHUB_OUTPUT - name: Attest docker build (DockerHub) uses: actions/attest-build-provenance@977bb373ede98d70efdf65b84cb5f73e068dcc2a #v3.0.0 with: subject-name: index.docker.io/${{ github.repository }} subject-digest: ${{ steps.docker_build.outputs.digest }} push-to-registry: true - name: Attest docker build (GitHub Container Registry) uses: actions/attest-build-provenance@977bb373ede98d70efdf65b84cb5f73e068dcc2a #v3.0.0 with: subject-name: ghcr.io/${{ github.repository }} subject-digest: ${{ steps.docker_build.outputs.digest }} push-to-registry: true - name: Cosign sign images (GitHub OIDC) run: | cosign sign --yes \ ${{ github.repository }}@${{ steps.docker_build.outputs.digest }} cosign sign --yes \ ghcr.io/${{ github.repository }}@${{ steps.docker_build.outputs.digest }} - name: Inspect images (GitHub Container Registry) run: | docker buildx imagetools inspect ghcr.io/${{ github.repository }}:${{ steps.docker_meta.outputs.version }} echo "::group::Manifest" docker buildx imagetools inspect ghcr.io/${{ github.repository }}:${{ steps.docker_meta.outputs.version }} --format '{{ json .Manifest }}' echo "::endgroup::" echo "::group::Image (linux/amd64)" docker buildx imagetools inspect ghcr.io/${{ github.repository }}:${{ steps.docker_meta.outputs.version }} --format '{{ json (index .Image "linux/amd64") }}' echo "::endgroup::" echo "::group::Provenance (linux/amd64)" docker buildx imagetools inspect ghcr.io/${{ github.repository }}:${{ steps.docker_meta.outputs.version }} --format '{{ json (index .Provenance "linux/amd64") }}' echo "::endgroup::" echo "::group::SBOM (linux/amd64)" docker buildx imagetools inspect ghcr.io/${{ github.repository }}:${{ steps.docker_meta.outputs.version }} --format '{{ json (index .SBOM "linux/amd64") }}' echo "::endgroup::" - name: Verify cosign signatures run: | echo "::group::Verify signature (DockerHub)" cosign verify --rekor-url https://rekor.sigstore.dev \ --certificate-identity "https://github.com/${{ github.workflow_ref }}" \ --certificate-oidc-issuer "https://token.actions.githubusercontent.com" \ ${{ github.repository }}@${{ steps.docker_build.outputs.digest }} echo "::endgroup::" echo "::group::Verify signature (GitHub Container Registry)" cosign verify --rekor-url https://rekor.sigstore.dev \ --certificate-identity "https://github.com/${{ github.workflow_ref }}" \ --certificate-oidc-issuer "https://token.actions.githubusercontent.com" \ ghcr.io/${{ github.repository }}@${{ steps.docker_build.outputs.digest }} echo "::endgroup::" dockerhub: if: github.event_name == 'release' runs-on: ubuntu-latest needs: - build - manifest steps: - name: Checkout uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 #v6.0.1 - name: Docker Hub Description uses: peter-evans/dockerhub-description@1b9a80c056b620d92cedb9d9b5a223409c68ddfa #v5.0.0 with: username: ${{ vars.DOCKERHUB_USERNAME }} password: ${{ secrets.DOCKERHUB_TOKEN }} short-description: ${{ github.event.repository.description }}