From 368b1768ebca2462ee93c6c96ccb89ebd93463be Mon Sep 17 00:00:00 2001 From: Miles Kane Date: Sun, 29 Mar 2026 22:06:57 -0500 Subject: [PATCH 1/7] feat: add multi-architecture support for Docker builds and scans --- .github/workflows/ci-cd.yaml | 7 ++- .github/workflows/docker-build-and-scan.yaml | 60 +++++++++++++------- .github/workflows/release.yaml | 3 +- 3 files changed, 45 insertions(+), 25 deletions(-) diff --git a/.github/workflows/ci-cd.yaml b/.github/workflows/ci-cd.yaml index 63270fa..35c8ce1 100644 --- a/.github/workflows/ci-cd.yaml +++ b/.github/workflows/ci-cd.yaml @@ -32,20 +32,21 @@ jobs: docker-build-and-image-scan: if: github.event_name == 'push' needs: test - uses: milsman2/python-app-template/.github/workflows/docker-build-and-scan.yaml@main + uses: ./.github/workflows/docker-build-and-scan.yaml with: DOCKER_PATH_CONTEXT: . DOCKER_BUILD_DOCKERFILE: ./Dockerfile - DOCKER_TAGS: ${{ vars.DOCKER_USERNAME }}/${{ vars.DOCKER_REPOSITORY }}:${{ github.sha }} + DOCKER_TAGS: ${{ vars.DOCKER_USERNAME }}_${{ vars.DOCKER_REPOSITORY }}_${{ github.sha }} DOCKER_LOAD_BOOL: false DOCKER_PUSH_BOOL: ${{ github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/tags/v') }} DOCKER_USERNAME: ${{ vars.DOCKER_USERNAME }} + DOCKER_PLATFORMS: '["linux/amd64","linux/arm64"]' secrets: inherit release: if: github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/tags/v') needs: [test, docker-build-and-image-scan] - uses: milsman2/python-app-template/.github/workflows/release.yaml@main + uses: ./.github/workflows/release.yaml permissions: contents: write secrets: inherit diff --git a/.github/workflows/docker-build-and-scan.yaml b/.github/workflows/docker-build-and-scan.yaml index 75c6e7f..0fcdb25 100644 --- a/.github/workflows/docker-build-and-scan.yaml +++ b/.github/workflows/docker-build-and-scan.yaml @@ -23,44 +23,62 @@ on: DOCKER_USERNAME: required: true type: string + DOCKER_PLATFORMS: + required: false + type: string + default: 'linux/amd64,linux/arm64' jobs: - build-and-scan: + build: + runs-on: ubuntu-latest + strategy: + matrix: + platform: ${{ fromJson(inputs.DOCKER_PLATFORMS) }} env: DOCKER_PATH_CONTEXT: ${{ inputs.DOCKER_PATH_CONTEXT }} DOCKER_BUILD_DOCKERFILE: ${{ inputs.DOCKER_BUILD_DOCKERFILE }} DOCKER_TAGS: ${{ inputs.DOCKER_TAGS }} - DOCKER_LOAD_BOOL: ${{ inputs.DOCKER_LOAD_BOOL }} - DOCKER_PUSH_BOOL: ${{ inputs.DOCKER_PUSH_BOOL }} DOCKER_USERNAME: ${{ inputs.DOCKER_USERNAME }} - runs-on: ubuntu-latest steps: - - name: Checkout repository - uses: actions/checkout@v6 - - name: Set up QEMU - uses: docker/setup-qemu-action@v4 - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v4 - - name: Login to DockerHub - uses: docker/login-action@v4 + - uses: actions/checkout@v6 + - uses: docker/setup-qemu-action@v4 + - uses: docker/setup-buildx-action@v4 + - uses: docker/login-action@v4 with: username: ${{ env.DOCKER_USERNAME }} password: ${{ secrets.DOCKERHUB_TOKEN }} - - name: Build (and maybe push) Docker image + - name: Build & push per-arch image uses: docker/build-push-action@v7 with: - context: ${{ env.DOCKER_PATH_CONTEXT }} - file: ${{ env.DOCKER_BUILD_DOCKERFILE}} - load: ${{ env.DOCKER_LOAD_BOOL }} - push: ${{ env.DOCKER_PUSH_BOOL }} - tags: ${{ env.DOCKER_TAGS }} - - name: Run Trivy vulnerability scanner (remote) - if: ${{ github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/tags/v') }} + context: ${{ env.DOCKER_PATH_CONTEXT }} + file: ${{ env.DOCKER_BUILD_DOCKERFILE }} + platforms: ${{ matrix.platform }} + push: true + tags: docker.io/${{ env.DOCKER_TAGS }}-${{ matrix.platform }} + - name: Run Trivy vulnerability scanner (per-arch) uses: aquasecurity/trivy-action@0.35.0 with: - image-ref: docker.io/${{ env.DOCKER_TAGS }} + image-ref: docker.io/${{ env.DOCKER_TAGS }}-${{ matrix.platform }} format: 'table' exit-code: '1' ignore-unfixed: true vuln-type: 'os,library' severity: 'CRITICAL,HIGH' + + manifest: + runs-on: ubuntu-latest + needs: build + env: + DOCKER_TAGS: ${{ inputs.DOCKER_TAGS }} + DOCKER_USERNAME: ${{ inputs.DOCKER_USERNAME }} + steps: + - uses: docker/login-action@v4 + with: + username: ${{ env.DOCKER_USERNAME }} + password: ${{ secrets.DOCKERHUB_TOKEN }} + - name: Create and push multi-arch manifest + run: | + docker buildx imagetools create \ + -t docker.io/${{ env.DOCKER_TAGS }} \ + docker.io/${{ env.DOCKER_TAGS }}-linux_amd64 \ + docker.io/${{ env.DOCKER_TAGS }}-linux_arm64 diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index a5fbb17..c09e3db 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -94,6 +94,7 @@ jobs: DOCKER_PATH_CONTEXT: ${{ inputs.DOCKER_PATH_CONTEXT }} DOCKER_BUILD_DOCKERFILE: ${{ inputs.DOCKER_BUILD_DOCKERFILE }} DOCKER_LOAD_BOOL: false - DOCKER_TAGS: ${{ inputs.DOCKER_USERNAME }}/${{ inputs.DOCKER_REPOSITORY }}:${{ needs.Semantic-Release.outputs.tag }} + DOCKER_TAGS: ${{ inputs.DOCKER_USERNAME }}_${{ inputs.DOCKER_REPOSITORY }}_${{ needs.Semantic-Release.outputs.tag }} DOCKER_PUSH_BOOL: true DOCKER_USERNAME: ${{ inputs.DOCKER_USERNAME }} + DOCKER_PLATFORMS: '["linux/amd64","linux/arm64"]' From 829fb83439f31f6fc7c1dbf83909440afae156f6 Mon Sep 17 00:00:00 2001 From: Miles Kane Date: Sun, 29 Mar 2026 22:08:04 -0500 Subject: [PATCH 2/7] fix: remove unnecessary blank line in CI/CD workflow --- .github/workflows/ci-cd.yaml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/ci-cd.yaml b/.github/workflows/ci-cd.yaml index 35c8ce1..8368254 100644 --- a/.github/workflows/ci-cd.yaml +++ b/.github/workflows/ci-cd.yaml @@ -42,7 +42,6 @@ jobs: DOCKER_USERNAME: ${{ vars.DOCKER_USERNAME }} DOCKER_PLATFORMS: '["linux/amd64","linux/arm64"]' secrets: inherit - release: if: github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/tags/v') needs: [test, docker-build-and-image-scan] From 3158fc03e42b37246582aee7b680abf43e3938a8 Mon Sep 17 00:00:00 2001 From: Miles Kane Date: Sun, 29 Mar 2026 22:13:07 -0500 Subject: [PATCH 3/7] fix: update Docker tags format in CI/CD and release workflows --- .github/workflows/ci-cd.yaml | 2 +- .github/workflows/docker-build-and-scan.yaml | 2 +- .github/workflows/release.yaml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci-cd.yaml b/.github/workflows/ci-cd.yaml index 8368254..7946b46 100644 --- a/.github/workflows/ci-cd.yaml +++ b/.github/workflows/ci-cd.yaml @@ -36,7 +36,7 @@ jobs: with: DOCKER_PATH_CONTEXT: . DOCKER_BUILD_DOCKERFILE: ./Dockerfile - DOCKER_TAGS: ${{ vars.DOCKER_USERNAME }}_${{ vars.DOCKER_REPOSITORY }}_${{ github.sha }} + DOCKER_TAGS: ${{ vars.DOCKER_USERNAME }}/${{ vars.DOCKER_REPOSITORY }}:${{ github.sha }} DOCKER_LOAD_BOOL: false DOCKER_PUSH_BOOL: ${{ github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/tags/v') }} DOCKER_USERNAME: ${{ vars.DOCKER_USERNAME }} diff --git a/.github/workflows/docker-build-and-scan.yaml b/.github/workflows/docker-build-and-scan.yaml index 0fcdb25..db027c9 100644 --- a/.github/workflows/docker-build-and-scan.yaml +++ b/.github/workflows/docker-build-and-scan.yaml @@ -54,7 +54,7 @@ jobs: file: ${{ env.DOCKER_BUILD_DOCKERFILE }} platforms: ${{ matrix.platform }} push: true - tags: docker.io/${{ env.DOCKER_TAGS }}-${{ matrix.platform }} + tags: docker.io/${{ env.DOCKER_TAGS }}-${{ matrix.platform.replace('/', '-') }} - name: Run Trivy vulnerability scanner (per-arch) uses: aquasecurity/trivy-action@0.35.0 with: diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index c09e3db..2f8fdd4 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -94,7 +94,7 @@ jobs: DOCKER_PATH_CONTEXT: ${{ inputs.DOCKER_PATH_CONTEXT }} DOCKER_BUILD_DOCKERFILE: ${{ inputs.DOCKER_BUILD_DOCKERFILE }} DOCKER_LOAD_BOOL: false - DOCKER_TAGS: ${{ inputs.DOCKER_USERNAME }}_${{ inputs.DOCKER_REPOSITORY }}_${{ needs.Semantic-Release.outputs.tag }} + DOCKER_TAGS: ${{ inputs.DOCKER_USERNAME }}/${{ inputs.DOCKER_REPOSITORY }}:${{ needs.Semantic-Release.outputs.tag }} DOCKER_PUSH_BOOL: true DOCKER_USERNAME: ${{ inputs.DOCKER_USERNAME }} DOCKER_PLATFORMS: '["linux/amd64","linux/arm64"]' From c76ec11695660a41333d62b5fb520b54f893433a Mon Sep 17 00:00:00 2001 From: Miles Kane Date: Sun, 29 Mar 2026 22:14:28 -0500 Subject: [PATCH 4/7] fix: correct platform handling and tag formatting in Docker build workflow --- .github/workflows/docker-build-and-scan.yaml | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/.github/workflows/docker-build-and-scan.yaml b/.github/workflows/docker-build-and-scan.yaml index db027c9..9590866 100644 --- a/.github/workflows/docker-build-and-scan.yaml +++ b/.github/workflows/docker-build-and-scan.yaml @@ -33,7 +33,11 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - platform: ${{ fromJson(inputs.DOCKER_PLATFORMS) }} + include: + - platform: linux/amd64 + suffix: linux-amd64 + - platform: linux/arm64 + suffix: linux-arm64 env: DOCKER_PATH_CONTEXT: ${{ inputs.DOCKER_PATH_CONTEXT }} DOCKER_BUILD_DOCKERFILE: ${{ inputs.DOCKER_BUILD_DOCKERFILE }} @@ -54,11 +58,11 @@ jobs: file: ${{ env.DOCKER_BUILD_DOCKERFILE }} platforms: ${{ matrix.platform }} push: true - tags: docker.io/${{ env.DOCKER_TAGS }}-${{ matrix.platform.replace('/', '-') }} + tags: docker.io/${{ env.DOCKER_TAGS }}-${{ matrix.suffix }} - name: Run Trivy vulnerability scanner (per-arch) uses: aquasecurity/trivy-action@0.35.0 with: - image-ref: docker.io/${{ env.DOCKER_TAGS }}-${{ matrix.platform }} + image-ref: docker.io/${{ env.DOCKER_TAGS }}-${{ matrix.suffix }} format: 'table' exit-code: '1' ignore-unfixed: true @@ -80,5 +84,5 @@ jobs: run: | docker buildx imagetools create \ -t docker.io/${{ env.DOCKER_TAGS }} \ - docker.io/${{ env.DOCKER_TAGS }}-linux_amd64 \ - docker.io/${{ env.DOCKER_TAGS }}-linux_arm64 + docker.io/${{ env.DOCKER_TAGS }}-linux-amd64 \ + docker.io/${{ env.DOCKER_TAGS }}-linux-arm64 From b08fdef0a9dcab51614456e43b1c9bd0ada689bd Mon Sep 17 00:00:00 2001 From: Miles Kane Date: Sun, 29 Mar 2026 22:17:39 -0500 Subject: [PATCH 5/7] feat: add wait step for Docker image availability in build workflow --- .github/workflows/docker-build-and-scan.yaml | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/.github/workflows/docker-build-and-scan.yaml b/.github/workflows/docker-build-and-scan.yaml index 9590866..90ed4ba 100644 --- a/.github/workflows/docker-build-and-scan.yaml +++ b/.github/workflows/docker-build-and-scan.yaml @@ -59,6 +59,18 @@ jobs: platforms: ${{ matrix.platform }} push: true tags: docker.io/${{ env.DOCKER_TAGS }}-${{ matrix.suffix }} + - name: Wait for image to be available in Docker Hub + run: | + for i in {1..20}; do + if docker buildx imagetools inspect docker.io/${{ env.DOCKER_TAGS }}-${{ matrix.suffix }} > /dev/null 2>&1; then + echo "Image found!" + exit 0 + fi + echo "Waiting for image to be available... ($i)" + sleep 6 + done + echo "Image not found after waiting. Exiting." + exit 1 - name: Run Trivy vulnerability scanner (per-arch) uses: aquasecurity/trivy-action@0.35.0 with: From 551e671c000033ed36714f7db637501dab7563cc Mon Sep 17 00:00:00 2001 From: Miles Kane Date: Sun, 29 Mar 2026 22:19:51 -0500 Subject: [PATCH 6/7] fix: remove DOCKER_PLATFORMS variable from CI/CD workflow --- .github/workflows/ci-cd.yaml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/ci-cd.yaml b/.github/workflows/ci-cd.yaml index 7946b46..8791dd0 100644 --- a/.github/workflows/ci-cd.yaml +++ b/.github/workflows/ci-cd.yaml @@ -40,7 +40,6 @@ jobs: DOCKER_LOAD_BOOL: false DOCKER_PUSH_BOOL: ${{ github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/tags/v') }} DOCKER_USERNAME: ${{ vars.DOCKER_USERNAME }} - DOCKER_PLATFORMS: '["linux/amd64","linux/arm64"]' secrets: inherit release: if: github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/tags/v') From 103a0afe8db8c85f6337d09d46ac59df852d7d65 Mon Sep 17 00:00:00 2001 From: Miles Kane Date: Mon, 30 Mar 2026 21:25:21 -0500 Subject: [PATCH 7/7] feat: simplify Docker build workflow by removing unused variables and updating multi-arch image handling --- .github/workflows/ci-cd.yaml | 2 - .github/workflows/docker-build-and-scan.yaml | 61 ++------------------ .github/workflows/release.yaml | 3 - 3 files changed, 6 insertions(+), 60 deletions(-) diff --git a/.github/workflows/ci-cd.yaml b/.github/workflows/ci-cd.yaml index 8791dd0..1b675a7 100644 --- a/.github/workflows/ci-cd.yaml +++ b/.github/workflows/ci-cd.yaml @@ -37,8 +37,6 @@ jobs: DOCKER_PATH_CONTEXT: . DOCKER_BUILD_DOCKERFILE: ./Dockerfile DOCKER_TAGS: ${{ vars.DOCKER_USERNAME }}/${{ vars.DOCKER_REPOSITORY }}:${{ github.sha }} - DOCKER_LOAD_BOOL: false - DOCKER_PUSH_BOOL: ${{ github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/tags/v') }} DOCKER_USERNAME: ${{ vars.DOCKER_USERNAME }} secrets: inherit release: diff --git a/.github/workflows/docker-build-and-scan.yaml b/.github/workflows/docker-build-and-scan.yaml index 90ed4ba..727d16f 100644 --- a/.github/workflows/docker-build-and-scan.yaml +++ b/.github/workflows/docker-build-and-scan.yaml @@ -12,32 +12,13 @@ on: DOCKER_TAGS: required: true type: string - DOCKER_LOAD_BOOL: - required: false - type: boolean - default: false - DOCKER_PUSH_BOOL: - required: false - type: boolean - default: false DOCKER_USERNAME: required: true type: string - DOCKER_PLATFORMS: - required: false - type: string - default: 'linux/amd64,linux/arm64' jobs: - build: + build-and-scan: runs-on: ubuntu-latest - strategy: - matrix: - include: - - platform: linux/amd64 - suffix: linux-amd64 - - platform: linux/arm64 - suffix: linux-arm64 env: DOCKER_PATH_CONTEXT: ${{ inputs.DOCKER_PATH_CONTEXT }} DOCKER_BUILD_DOCKERFILE: ${{ inputs.DOCKER_BUILD_DOCKERFILE }} @@ -51,50 +32,20 @@ jobs: with: username: ${{ env.DOCKER_USERNAME }} password: ${{ secrets.DOCKERHUB_TOKEN }} - - name: Build & push per-arch image + - name: Build & push multi-arch image uses: docker/build-push-action@v7 with: context: ${{ env.DOCKER_PATH_CONTEXT }} file: ${{ env.DOCKER_BUILD_DOCKERFILE }} - platforms: ${{ matrix.platform }} + platforms: linux/amd64,linux/arm64 push: true - tags: docker.io/${{ env.DOCKER_TAGS }}-${{ matrix.suffix }} - - name: Wait for image to be available in Docker Hub - run: | - for i in {1..20}; do - if docker buildx imagetools inspect docker.io/${{ env.DOCKER_TAGS }}-${{ matrix.suffix }} > /dev/null 2>&1; then - echo "Image found!" - exit 0 - fi - echo "Waiting for image to be available... ($i)" - sleep 6 - done - echo "Image not found after waiting. Exiting." - exit 1 - - name: Run Trivy vulnerability scanner (per-arch) + tags: docker.io/${{ env.DOCKER_TAGS }} + - name: Run Trivy vulnerability scanner (multi-arch manifest) uses: aquasecurity/trivy-action@0.35.0 with: - image-ref: docker.io/${{ env.DOCKER_TAGS }}-${{ matrix.suffix }} + image-ref: docker.io/${{ env.DOCKER_TAGS }} format: 'table' exit-code: '1' ignore-unfixed: true vuln-type: 'os,library' severity: 'CRITICAL,HIGH' - - manifest: - runs-on: ubuntu-latest - needs: build - env: - DOCKER_TAGS: ${{ inputs.DOCKER_TAGS }} - DOCKER_USERNAME: ${{ inputs.DOCKER_USERNAME }} - steps: - - uses: docker/login-action@v4 - with: - username: ${{ env.DOCKER_USERNAME }} - password: ${{ secrets.DOCKERHUB_TOKEN }} - - name: Create and push multi-arch manifest - run: | - docker buildx imagetools create \ - -t docker.io/${{ env.DOCKER_TAGS }} \ - docker.io/${{ env.DOCKER_TAGS }}-linux-amd64 \ - docker.io/${{ env.DOCKER_TAGS }}-linux-arm64 diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index 2f8fdd4..b0cb6fd 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -93,8 +93,5 @@ jobs: with: DOCKER_PATH_CONTEXT: ${{ inputs.DOCKER_PATH_CONTEXT }} DOCKER_BUILD_DOCKERFILE: ${{ inputs.DOCKER_BUILD_DOCKERFILE }} - DOCKER_LOAD_BOOL: false DOCKER_TAGS: ${{ inputs.DOCKER_USERNAME }}/${{ inputs.DOCKER_REPOSITORY }}:${{ needs.Semantic-Release.outputs.tag }} - DOCKER_PUSH_BOOL: true DOCKER_USERNAME: ${{ inputs.DOCKER_USERNAME }} - DOCKER_PLATFORMS: '["linux/amd64","linux/arm64"]'