From bf1889077e6ab2792d49c405b06f2148fb67459b Mon Sep 17 00:00:00 2001 From: lotyp Date: Sat, 14 Sep 2024 14:30:18 +0300 Subject: [PATCH] feat!: migrate to docker-bake.hcl approach --- .dive-ci.yml | 17 +++ .github/workflows/build-latest.yml | 42 ------ .github/workflows/build-release.yml | 49 ------- .github/workflows/build.yml | 204 ++++++++++++++++++++++++++++ .github/workflows/test.yml | 119 ++++++++++++++++ docker-bake.hcl | 49 +++++++ 6 files changed, 389 insertions(+), 91 deletions(-) create mode 100644 .dive-ci.yml delete mode 100644 .github/workflows/build-latest.yml delete mode 100644 .github/workflows/build-release.yml create mode 100644 .github/workflows/build.yml create mode 100644 .github/workflows/test.yml create mode 100644 docker-bake.hcl diff --git a/.dive-ci.yml b/.dive-ci.yml new file mode 100644 index 0000000..9c4dc1c --- /dev/null +++ b/.dive-ci.yml @@ -0,0 +1,17 @@ +--- + +rules: + # If the efficiency is measured below X%, mark as failed. + # Expressed as a ratio between 0-1. + lowestEfficiency: 0.95 + + # If the amount of wasted space is at least X or larger than X, mark as failed. + # Expressed in B, KB, MB, and GB. + highestWastedBytes: 20MB + + # If the amount of wasted space makes up for X% or more of the image, mark as failed. + # Note: the base image layer is NOT included in the total image size. + # Expressed as a ratio between 0-1; fails if the threshold is met or crossed. + highestUserWastedPercent: 0.20 + +... diff --git a/.github/workflows/build-latest.yml b/.github/workflows/build-latest.yml deleted file mode 100644 index 61f692a..0000000 --- a/.github/workflows/build-latest.yml +++ /dev/null @@ -1,42 +0,0 @@ ---- - -on: # yamllint disable-line rule:truthy - workflow_dispatch: - pull_request: - branches: - - master - -name: ๐Ÿš€ Build docker images with latest tag - -jobs: - # https://docs.github.com/en/enterprise-cloud@latest/actions/learn-github-actions/expressions#example-returning-a-json-object - prepare: - runs-on: "ubuntu-latest" - outputs: - matrix: ${{ steps.matrix.outputs.matrix }} - steps: - - name: โš™๏ธ Generate matrix - id: matrix - run: | - echo 'matrix={ - "build_type": ["dev", "k8s"], - "os_name": ["alpine"] - }' | tr -d '\n' >> "$GITHUB_OUTPUT" - - build: - needs: prepare - strategy: - matrix: ${{ fromJson(needs.prepare.outputs.matrix )}} - uses: wayofdev/gh-actions/.github/workflows/build-image.yml@master - with: - os: "ubuntu-latest" - push-to-hub: true - image-namespace: "wayofdev/nginx" - image-template-path: "./dist" - image-template: ${{ matrix.build_type }}-${{ matrix.os_name }} - image-version: latest - secrets: - docker-username: ${{ secrets.DOCKER_USERNAME }} - docker-password: ${{ secrets.DOCKER_TOKEN }} - -... diff --git a/.github/workflows/build-release.yml b/.github/workflows/build-release.yml deleted file mode 100644 index f3c3193..0000000 --- a/.github/workflows/build-release.yml +++ /dev/null @@ -1,49 +0,0 @@ ---- - -on: # yamllint disable-line rule:truthy - release: - types: - - released - -name: ๐Ÿš€ Build docker images with release tag - -jobs: - # https://docs.github.com/en/enterprise-cloud@latest/actions/learn-github-actions/expressions#example-returning-a-json-object - prepare: - runs-on: "ubuntu-latest" - outputs: - matrix: ${{ steps.matrix.outputs.matrix }} - version: ${{ steps.version.outputs.version }} - steps: - - name: โš™๏ธ Generate matrix - id: matrix - run: | - echo 'matrix={ - "build_type": ["dev", "k8s"], - "os_name": ["alpine"] - }' | tr -d '\n' >> "$GITHUB_OUTPUT" - - - name: โš™๏ธ Get version for image tag - id: version - run: | - version=${{ github.ref_name }} - version=${version#v} - echo "version=$version" >> "$GITHUB_OUTPUT" - - build: - needs: prepare - strategy: - matrix: ${{ fromJson(needs.prepare.outputs.matrix )}} - uses: wayofdev/gh-actions/.github/workflows/build-image.yml@master - with: - os: "ubuntu-latest" - push-to-hub: true - image-namespace: "wayofdev/nginx" - image-template-path: "./dist" - image-template: ${{ matrix.build_type }}-${{ matrix.os_name }} - image-version: ${{ needs.prepare.outputs.version }} - secrets: - docker-username: ${{ secrets.DOCKER_USERNAME }} - docker-password: ${{ secrets.DOCKER_TOKEN }} - -... diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml new file mode 100644 index 0000000..dae766c --- /dev/null +++ b/.github/workflows/build.yml @@ -0,0 +1,204 @@ +--- + +on: # yamllint disable-line rule:truthy + workflow_dispatch: + push: + branches: + - master + release: + types: + - released + schedule: + - cron: "30 10 * * *" + +env: + DOCKER_NAMESPACE: wayofdev/nginx + GHCR_NAMESPACE: ghcr.io/wayofdev/docker-nginx + +name: ๐Ÿš€ Build docker images with latest tag + +concurrency: + group: "${{ github.workflow }}-${{ github.ref }}" + cancel-in-progress: true + +jobs: + build: + strategy: + fail-fast: false + matrix: + os_name: ["alpine"] + nginx_type: ["dev", "k8s"] + builder: [{arch: "amd64", os: "ubuntu-latest"}, {arch: "arm64", os: "ubuntu-latest"}] + runs-on: ${{ matrix.builder.os }} + steps: + + - name: ๐ŸŒŽ Set environment variables + run: | + tag="${{ matrix.nginx_type }}-${{ matrix.os_name }}-${{ matrix.builder.arch }}" + target="nginx-${{ matrix.nginx_type }}-${{ matrix.os_name }}" + echo "TARGET=${target}" >> "$GITHUB_ENV" + echo "PLATFORM_CACHE_TAG=${tag}" >> "$GITHUB_ENV" + + - name: ๐Ÿ“ฆ Check out the codebase + uses: actions/checkout@v4.1.7 + + - name: ๐Ÿค– Generate dist files + run: ansible-playbook src/playbook.yml -l ${{ matrix.nginx_type }}-${{ matrix.os_name }} + + - name: ๐Ÿ–ฅ๏ธ Setup docker QEMU + uses: docker/setup-qemu-action@v3 + with: + platforms: linux/${{ matrix.builder.arch }} + + - name: ๐Ÿ› ๏ธ Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + with: + platforms: linux/${{ matrix.builder.arch }} + buildkitd-flags: "--debug" + + - name: ๐Ÿณ Extract docker meta data + id: meta + uses: docker/metadata-action@v5 + with: + images: | + ${{ env.DOCKER_NAMESPACE }} + ${{ env.GHCR_NAMESPACE }} + tags: | + type=raw,event=branch,value=latest + type=ref,event=tag + type=semver,pattern={{version}} + type=semver,pattern={{major}}.{{minor}} + flavor: | + latest=false + prefix=${{ matrix.nginx_type }}-${{ matrix.os_name }}- + + - name: โš™๏ธ Rename meta bake definition file + run: | + mv "${{ steps.meta.outputs.bake-file }}" "/tmp/bake-meta-${{ env.PLATFORM_CACHE_TAG }}.json" + + - name: ๐Ÿ“ค Upload meta bake definition + uses: actions/upload-artifact@v4 + with: + name: bake-meta-${{ env.PLATFORM_CACHE_TAG }}.json + path: /tmp/bake-meta-*.json + if-no-files-found: error + retention-days: 1 + + - name: ๐Ÿ”‘ Login to docker-hub + uses: docker/login-action@v3 + with: + registry: docker.io + username: ${{ secrets.DOCKER_USERNAME }} + password: ${{ secrets.DOCKER_TOKEN }} + + - name: ๐Ÿ”‘ Login to GHCR + uses: docker/login-action@v3 + with: + registry: ghcr.io + username: ${{ github.repository_owner }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: ๐Ÿš€ Bake image and push to docker-hub and GHCR + id: bake + uses: docker/bake-action@v5.7.0 + with: + targets: ${{ env.TARGET }} + files: | + ./docker-bake.hcl + /tmp/bake-meta-${{ env.PLATFORM_CACHE_TAG }}.json + set: | + *.tags= + *.platform=linux/${{ matrix.builder.arch }} + *.cache-from=type=gha,scope=build-${{ env.PLATFORM_CACHE_TAG }} + *.cache-to=type=gha,scope=build-${{ env.PLATFORM_CACHE_TAG }} + *.output=type=image,"name=${{ env.DOCKER_NAMESPACE }},${{ env.GHCR_NAMESPACE }}",push-by-digest=true,name-canonical=true,push=true + + - name: ๐Ÿ“ฅ Export digest + run: | + mkdir -p /tmp/digests + echo "Bake Metadata: ${{ steps.bake.outputs.metadata }}" + digest=$(echo '${{ steps.bake.outputs.metadata }}' | jq -r '.["${{ env.TARGET }}"]["containerimage.digest"]') + if [[ -z "$digest" || "$digest" == "null" ]]; then + echo "Digest not found." + exit 1 + fi + echo "Digest: $digest" + touch "/tmp/digests/${digest#sha256:}" + + - name: ๐Ÿ“ค Upload digest + uses: actions/upload-artifact@v4 + with: + name: digests-${{ env.PLATFORM_CACHE_TAG }} + path: /tmp/digests/* + if-no-files-found: error + retention-days: 1 + + merge: + needs: build + runs-on: ubuntu-latest + steps: + + - name: ๐Ÿ“ฅ Download meta bake definitions + uses: actions/download-artifact@v4 + with: + pattern: bake-meta-* + path: /tmp + merge-multiple: true + + - name: ๐Ÿ“ฅ Download meta bake definitions + uses: actions/download-artifact@v4 + with: + pattern: digests-* + path: /tmp/digests + + - name: ๐Ÿ”‘ Login to docker-hub + uses: docker/login-action@v3 + with: + username: ${{ secrets.DOCKER_USERNAME }} + password: ${{ secrets.DOCKER_TOKEN }} + + - name: ๐Ÿ”‘ Login to GHCR + uses: docker/login-action@v3 + with: + registry: ghcr.io + username: ${{ github.repository_owner }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: ๐Ÿ–ฅ๏ธ Setup docker QEMU + uses: docker/setup-qemu-action@v3 + with: + platforms: linux/amd64,linux/arm64 + + - name: ๐Ÿ› ๏ธ Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + with: + platforms: linux/amd64,linux/arm64 + buildkitd-flags: "--debug" + + - name: ๐Ÿ“ฆ Check out the codebase + uses: actions/checkout@v4.1.7 + + - name: ๐Ÿš€ Create manifest list and push + working-directory: /tmp + run: | + variants=($(ls bake-meta-*.json | sed -E 's/bake-meta-//; s/-amd64.json|-arm64.json//g' | sort -u)) + for variant in "${variants[@]}"; do + + # Fetch digests for amd64 and arm64 architectures + DIGEST_AMD64=$(basename $(ls /tmp/digests/digests-${variant}-amd64/*)) + DIGEST_ARM64=$(basename $(ls /tmp/digests/digests-${variant}-arm64/*)) + echo "Digest AMD64: $DIGEST_AMD64" + echo "Digest ARM64: $DIGEST_ARM64" + + # Create the manifest list for Docker Hub + docker buildx imagetools create $(jq -cr ".target.\"docker-metadata-action\".tags | map(select(startswith(\"${DOCKER_NAMESPACE}\")) | \"-t \" + .) | join(\" \")" /tmp/bake-meta-${variant}-amd64.json) \ + "${DOCKER_NAMESPACE}@sha256:${DIGEST_AMD64}" \ + "${DOCKER_NAMESPACE}@sha256:${DIGEST_ARM64}" + + # Create the manifest list for GHCR + docker buildx imagetools create $(jq -cr ".target.\"docker-metadata-action\".tags | map(select(startswith(\"${GHCR_NAMESPACE}\")) | \"-t \" + .) | join(\" \")" /tmp/bake-meta-${variant}-amd64.json) \ + "${GHCR_NAMESPACE}@sha256:${DIGEST_AMD64}" \ + "${GHCR_NAMESPACE}@sha256:${DIGEST_ARM64}" + done + +... diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml new file mode 100644 index 0000000..1ad11bc --- /dev/null +++ b/.github/workflows/test.yml @@ -0,0 +1,119 @@ +--- + +on: # yamllint disable-line rule:truthy + pull_request: + paths-ignore: + - '**.md' + +env: + DOCKER_NAMESPACE: wayofdev/nginx + GHCR_NAMESPACE: ghcr.io/wayofdev/docker-nginx + +name: ๐Ÿงช Test Docker images + +concurrency: + group: "${{ github.workflow }}-${{ github.ref }}" + cancel-in-progress: true + +jobs: + test: + strategy: + fail-fast: false + matrix: + os_name: ["alpine"] + nginx_type: ["dev", "k8s"] + builder: [{arch: "amd64", os: "ubuntu-latest"}] + runs-on: ${{ matrix.builder.os }} + steps: + + - name: ๐ŸŒŽ Set environment variables + run: | + tag="${{ matrix.nginx_type }}-${{ matrix.os_name }}-${{ matrix.builder.arch }}" + target="nginx-${{ matrix.nginx_type }}-${{ matrix.os_name }}" + echo "TARGET=${target}" >> "$GITHUB_ENV" + echo "PLATFORM_CACHE_TAG=${tag}" >> "$GITHUB_ENV" + + - name: ๐Ÿ“ฆ Check out the codebase + uses: actions/checkout@v4.1.7 + + - name: ๐Ÿ› ๏ธ Install goss and dgoss + uses: e1himself/goss-installation-action@v1.2.1 + with: + version: v0.4.6 + + - name: ๐Ÿค– Generate dist files + run: ansible-playbook src/playbook.yml -l ${{ matrix.nginx_type }}-${{ matrix.os_name }} + + - name: ๐Ÿ–ฅ๏ธ Setup docker QEMU + uses: docker/setup-qemu-action@v3 + + - name: ๐Ÿ› ๏ธ Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + with: + buildkitd-flags: "--debug" + + - name: ๐Ÿ”‘ Login to docker-hub + uses: docker/login-action@v3 + with: + username: ${{ secrets.DOCKER_USERNAME }} + password: ${{ secrets.DOCKER_TOKEN }} + + - name: ๐Ÿ”‘ Login to GHCR + uses: docker/login-action@v3 + with: + registry: ghcr.io + username: ${{ github.repository_owner }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: ๐Ÿณ Extract docker meta data + id: meta + uses: docker/metadata-action@v5 + with: + images: | + ${{ env.DOCKER_NAMESPACE }} + ${{ env.GHCR_NAMESPACE }} + tags: | + type=raw,event=branch,value=latest + type=ref,event=pr + type=semver,pattern={{version}} + type=semver,pattern={{major}}.{{minor}} + flavor: | + latest=false + prefix=${{ matrix.nginx_type }}-${{ matrix.os_name }}- + + - name: ๐Ÿงช Bake image for testing + id: bake + uses: docker/bake-action@v5.7.0 + with: + targets: ${{ env.TARGET }} + files: | + ./docker-bake.hcl + ${{ steps.meta.outputs.bake-file }} + push: false + set: | + *.tags= + *.platform=linux/${{ matrix.builder.arch }} + *.cache-from=type=gha,scope=build-${{ env.PLATFORM_CACHE_TAG }} + *.cache-to=type=gha,scope=build-${{ env.PLATFORM_CACHE_TAG }} + *.output=type=docker,"name=${{ env.DOCKER_NAMESPACE }},${{ env.GHCR_NAMESPACE }}",name-canonical=true + + - name: ๐Ÿงช Test Docker image + run: | + export IMAGE_TEMPLATE=${{ matrix.nginx_type }}-${{ matrix.os_name }} + export IMAGE_TAG=${{ env.DOCKER_NAMESPACE }}:latest + make test + + - name: ๐Ÿ” Run Docker Scout + id: docker-scout + uses: docker/scout-action@v1 + with: + command: cves,recommendations + ignore-unchanged: true + only-fixed: true + only-severities: critical,high + keep-previous-comments: true + github-token: ${{ secrets.GITHUB_TOKEN }} + dockerhub-user: ${{ secrets.DOCKER_USERNAME }} + dockerhub-password: ${{ secrets.DOCKER_TOKEN }} + +... diff --git a/docker-bake.hcl b/docker-bake.hcl new file mode 100644 index 0000000..239b6ff --- /dev/null +++ b/docker-bake.hcl @@ -0,0 +1,49 @@ +## Documentation: +## https://docs.docker.com/build/ci/github-actions/multi-platform/#with-bake + +variable "DEFAULT_TAG" { default = "wayofdev/nginx:local" } + +## Special target: https://github.com/docker/metadata-action#bake-definition +target "docker-metadata-action" { + tags = ["${DEFAULT_TAG}"] +} + +########################### +## Nginx Dev +########################### +target "nginx-dev-alpine" { + inherits = ["docker-metadata-action"] + context = "dist/dev-alpine" + dockerfile = "./Dockerfile" +} + +########################### +## Nginx K8s +########################### +target "nginx-k8s-alpine" { + inherits = ["docker-metadata-action"] + context = "dist/k8s-alpine" + dockerfile = "./Dockerfile" +} + +########################### +## Groups +########################### +group "all" { + targets = [ + "nginx-dev-alpine", + "nginx-k8s-alpine", + ] +} + +group "dev" { + targets = [ + "nginx-dev-alpine", + ] +} + +group "k8s" { + targets = [ + "nginx-k8s-alpine", + ] +}