From 5e967287d653fa610d3c8e202cdaeeb76216e144 Mon Sep 17 00:00:00 2001 From: Tobias Lindberg Date: Thu, 18 Sep 2025 11:55:34 +0200 Subject: [PATCH 1/3] feat(ci): use multiple runners for build --- .github/workflows/build.yml | 236 +++++++++++++++++++++++++++++++----- 1 file changed, 203 insertions(+), 33 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 2e6eb75..6796922 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -3,9 +3,7 @@ name: build on: push: branches: - - "main" - - "feature-*" - - "bug-*" + - main paths: - "src/**" - "Dockerfile" @@ -16,16 +14,30 @@ on: workflow_dispatch: permissions: + attestations: write contents: read id-token: write packages: write jobs: build: - runs-on: ubuntu-latest - outputs: - docker_build_digest: ${{ steps.docker_build.outputs.digest }} - docker_meta_version: ${{ steps.docker_meta.outputs.version }} + name: build (${{ matrix.platform }}) + runs-on: ${{ matrix.runner }} + + 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@08c6903cd8c0fde910a37f88322edcfb5dd907a8 #v5.0.0 @@ -35,73 +47,220 @@ jobs: uses: docker/metadata-action@c1e51972afc2121e065aed6d45c65596fe445f3f #v5.8.0 with: images: | - tibiadata/tibiadata-api-go - ghcr.io/tibiadata/tibiadata-api-go + ${{ github.repository }} + ghcr.io/${{ github.repository }} tags: | type=edge - type=ref,event=branch,enable=${{ (github.ref != 'refs/heads/main') }} + 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: Install Cosign - if: github.event_name != 'pull_request' - uses: sigstore/cosign-installer@d7543c93d881b35a8faa02e8e3605f69b7a1ce62 #v3.10.0 - - - name: Set up QEMU + - name: Set up QEMU (${{ matrix.platform }}) + if: matrix.platform == 'linux/arm/v7' uses: docker/setup-qemu-action@29109295f81e9208d7d86ff1c6c12d2833863392 #v3.6.0 + with: + platforms: ${{ matrix.platform }} - name: Set up Docker Buildx uses: docker/setup-buildx-action@e468171a9de216ec08956ac3ada2f0791b6bd435 #v3.11.1 - name: Login to DockerHub - if: github.event_name != 'pull_request' uses: docker/login-action@184bdaa0721073962dff0199f1fb9940f07167d1 #v3.5.0 with: username: ${{ secrets.DOCKERHUB_USERNAME }} password: ${{ secrets.DOCKERHUB_TOKEN }} - name: Login to GitHub Container Registry - if: github.event_name != 'pull_request' uses: docker/login-action@184bdaa0721073962dff0199f1fb9940f07167d1 #v3.5.0 with: registry: ghcr.io username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} - - name: Build and push + - name: Build and push (${{ matrix.platform }}) id: docker_build uses: docker/build-push-action@263435318d21b8e681c14492fe198d362a7d2c83 #v6.18.0 with: context: . - platforms: linux/amd64,linux/arm/v7,linux/arm64 - push: ${{ github.event_name != 'pull_request' }} + 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: ${{ steps.docker_meta.outputs.tags }} - cache-from: type=gha - cache-to: type=gha,mode=max + 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: | TibiaDataBuildBuilder=github TibiaDataBuildRelease=${{ fromJSON(steps.docker_meta.outputs.json).labels['org.opencontainers.image.version'] }} TibiaDataBuildCommit=${{ fromJSON(steps.docker_meta.outputs.json).labels['org.opencontainers.image.revision'] }} - - name: Sign the images (with GitHub OIDC Token) - if: github.event_name != 'pull_request' + - 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@ea165f8d65b6e75b540449e92b4886f43607fa02 #v4.6.2 + 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@08c6903cd8c0fde910a37f88322edcfb5dd907a8 #v5.0.0 + + - name: Docker meta + id: docker_meta + uses: docker/metadata-action@c1e51972afc2121e065aed6d45c65596fe445f3f #v5.8.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@634f93cb2916e3fdff6788551b99b062d0335ce0 #v5.0.0 + with: + path: ${{ runner.temp }}/digests + pattern: digests-* + merge-multiple: true + + - name: Install Cosign + uses: sigstore/cosign-installer@d7543c93d881b35a8faa02e8e3605f69b7a1ce62 #v3.10.0 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@e468171a9de216ec08956ac3ada2f0791b6bd435 #v3.11.1 + + - name: Login to DockerHub + uses: docker/login-action@184bdaa0721073962dff0199f1fb9940f07167d1 #v3.5.0 + with: + username: ${{ secrets.DOCKERHUB_USERNAME }} + password: ${{ secrets.DOCKERHUB_TOKEN }} + + - name: Login to GitHub Container Registry + uses: docker/login-action@184bdaa0721073962dff0199f1fb9940f07167d1 #v3.5.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: | - cosign sign --yes --recursive \ - tibiadata/tibiadata-api-go@${{ steps.docker_build.outputs.digest }} + 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::" - cosign sign --yes --recursive \ - ghcr.io/tibiadata/tibiadata-api-go@${{ steps.docker_build.outputs.digest }} + 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::" argocd: if: github.event_name == 'release' || (github.event_name == 'push' && github.ref == 'refs/heads/main') runs-on: ubuntu-latest - needs: build + needs: + - build + - manifest + steps: - name: Determine the deployment subdomain id: determine_deployment @@ -118,14 +277,21 @@ jobs: token: ${{ secrets.REPO_ACCESS_TOKEN }} repository: tibiadata/tibiadata-argocd-app-of-apps event-type: bump-tibiadata-api-go-image-sha - client-payload: '{"docker_digest": "${{ needs.build.outputs.docker_build_digest }}", "subdomain": "${{ steps.determine_deployment.outputs.subdomain }}"}' + client-payload: |- + { + "docker_digest": "${{ needs.manifest.outputs.docker_build_digest }}", + "subdomain": "${{ steps.determine_deployment.outputs.subdomain }}" + } helm-chart: if: github.event_name == 'release' permissions: contents: write runs-on: ubuntu-latest - needs: build + needs: + - build + - manifest + steps: - name: Trigger workflow in tibiadata-helm-charts repo uses: peter-evans/repository-dispatch@ff45666b9427631e3450c54a1bcbee4d9ff4d7c0 #v3.0.0 @@ -133,4 +299,8 @@ jobs: token: ${{ secrets.REPO_ACCESS_TOKEN }} repository: tibiadata/tibiadata-helm-charts event-type: bump-helm-chart-release - client-payload: '{"chart_name": "${{ github.event.repository.name }}", "release_version": "${{ needs.build.outputs.docker_meta_version }}"}' + client-payload: |- + { + "chart_name": "${{ github.event.repository.name }}", + "release_version": "${{ needs.manifest.outputs.docker_meta_version }}" + } From 5de24ee081c84267c004d37e1980ae1d76ffce8f Mon Sep 17 00:00:00 2001 From: Tobias Lindberg Date: Thu, 18 Sep 2025 19:54:37 +0200 Subject: [PATCH 2/3] fix(ci): add if when not pull request --- .github/workflows/build.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 6796922..8cc1a40 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -23,6 +23,7 @@ jobs: build: name: build (${{ matrix.platform }}) runs-on: ${{ matrix.runner }} + if: github.event_name != 'pull_request' strategy: fail-fast: false From 88ec6c627be8d8d1f5fcde8b5813f79855f20621 Mon Sep 17 00:00:00 2001 From: Tobias Lindberg Date: Thu, 18 Sep 2025 21:34:01 +0200 Subject: [PATCH 3/3] fix(ci): fix format --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 8cc1a40..d6ea09b 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -194,7 +194,7 @@ jobs: $(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 .) + 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)