name: build-test-deploy on: workflow_call: inputs: architecture: description: 'The architecture of the images that will be build.' required: false default: 'linux/amd64' type: string mailu_version: description: 'The main version that is build. E.g. master or x.y.' required: true type: string pinned_mailu_version: description: 'The specific version that is build. E.g. commit hash or x.y.z.' required: true type: string docker_org: description: 'The docker organisation where the images are pushed to.' required: true type: string branch: description: 'The branch that triggered this workflow.' required: true type: string deploy: description: Deploy to docker hub. Happens for all branches but staging. Use string true or false. default: true required: false type: string release: description: Tag and create the github release. Use string true or false. default: false required: false type: string workflow_dispatch: inputs: architecture: description: 'The architecture of the images that will be build.' required: false default: 'linux/amd64' type: string mailu_version: description: 'The main version that is build. E.g. master or x.y.' required: true type: string pinned_mailu_version: description: 'The specific version that is build. E.g. commit hash or x.y.z.' required: true type: string docker_org: description: 'The docker organisation where the images are pushed to.' required: true type: string branch: description: 'The branch that triggered this workflow.' required: true type: string deploy: description: Deploy to docker hub. Happens for all branches but staging. Use string true or false. default: true required: false type: string release: description: Tag and create the github release. Use string true or false. default: false required: false type: string env: HCL_FILE: ./tests/build.hcl jobs: # This job calculates what images must be build. It reads the build.hcl file and then outputs all targets (images) in it. # This is used by the next build job. targets: name: create targets runs-on: ubuntu-latest outputs: matrix: ${{ steps.targets.outputs.matrix }} steps: - name: Checkout uses: actions/checkout@v3 - name: Create matrix id: targets run: | echo matrix=$(docker buildx bake -f ${{env.HCL_FILE}} --print | jq -cr '.group.default.targets') >> $GITHUB_OUTPUT - name: Show matrix run: | echo ${{ steps.targets.outputs.matrix }} ## This job builds the base image. The base image is used by all other images. build-base-image-x64: name: Build base image x64 if: inputs.architecture == 'linux/amd64' needs: - targets runs-on: ubuntu-latest permissions: contents: read packages: write steps: - uses: actions/checkout@v3 - name: Retrieve global variables shell: bash run: | echo "BRANCH=${{ inputs.branch }}" >> $GITHUB_ENV echo "MAILU_VERSION=${{ inputs.mailu_version }}" >> $GITHUB_ENV echo "PINNED_MAILU_VERSION=${{ inputs.pinned_mailu_version }}" >> $GITHUB_ENV echo "DOCKER_ORG=${{ inputs.docker_org }}" >> $GITHUB_ENV - name: Set up QEMU uses: docker/setup-qemu-action@v2 - uses: crazy-max/ghaction-github-runtime@v2 - name: Set up Docker Buildx uses: docker/setup-buildx-action@v2 - name: Login to GitHub Container Registry uses: docker/login-action@v2 with: registry: ghcr.io username: ${{ github.repository_owner }} password: ${{ secrets.GITHUB_TOKEN }} - name: Login to Docker Hub uses: docker/login-action@v2 with: username: ${{ secrets.Docker_Login }} password: ${{ secrets.Docker_Password }} - name: Helper to convert docker org to lowercase id: string uses: ASzc/change-string-case-action@v5 with: string: ${{ github.repository_owner }} - name: Build all docker images env: DOCKER_ORG: ghcr.io/${{ steps.string.outputs.lowercase }} MAILU_VERSION: ${{ env.MAILU_VERSION }} PINNED_MAILU_VERSION: ${{ env.PINNED_MAILU_VERSION }} uses: docker/bake-action@v2 with: files: ${{env.HCL_FILE}} targets: base load: false push: false set: | *.cache-from=type=registry,ref=ghcr.io/${{ steps.string.outputs.lowercase }}/base:${{ hashFiles('core/base/Dockerfile','core/base/requirements-prod.txt') }} *.cache-to=type=registry,ref=ghcr.io/${{ steps.string.outputs.lowercase }}/base:${{ hashFiles('core/base/Dockerfile','core/base/requirements-prod.txt') }},mode=max *.platform=${{ inputs.architecture }} ## This job builds the base image. The base image is used by all other images. build-base-image-arm: name: Build base image arm if: inputs.architecture != 'linux/amd64' needs: - targets runs-on: self-hosted permissions: contents: read packages: write steps: - uses: actions/checkout@v3 - name: Retrieve global variables shell: bash run: | echo "BRANCH=${{ inputs.branch }}" >> $GITHUB_ENV echo "MAILU_VERSION=${{ inputs.mailu_version }}" >> $GITHUB_ENV echo "PINNED_MAILU_VERSION=${{ inputs.pinned_mailu_version }}" >> $GITHUB_ENV echo "DOCKER_ORG=${{ inputs.docker_org }}" >> $GITHUB_ENV - name: Set up QEMU uses: docker/setup-qemu-action@v2 - uses: crazy-max/ghaction-github-runtime@v2 - name: Set up Docker Buildx uses: docker/setup-buildx-action@v2 - name: Login to GitHub Container Registry uses: docker/login-action@v2 with: registry: ghcr.io username: ${{ github.repository_owner }} password: ${{ secrets.GITHUB_TOKEN }} - name: Login to Docker Hub uses: docker/login-action@v2 with: username: ${{ secrets.Docker_Login }} password: ${{ secrets.Docker_Password }} - name: Helper to convert docker org to lowercase id: string uses: ASzc/change-string-case-action@v5 with: string: ${{ github.repository_owner }} - name: Build all docker images env: DOCKER_ORG: ghcr.io/${{ steps.string.outputs.lowercase }} MAILU_VERSION: ${{ env.MAILU_VERSION }} PINNED_MAILU_VERSION: ${{ env.PINNED_MAILU_VERSION }} uses: docker/bake-action@v2 with: files: ${{env.HCL_FILE}} targets: base load: false push: false set: | *.cache-from=type=registry,ref=ghcr.io/${{ steps.string.outputs.lowercase }}/base:${{ hashFiles('core/base/Dockerfile','core/base/requirements-prod.txt') }}-arm *.cache-to=type=registry,ref=ghcr.io/${{ steps.string.outputs.lowercase }}/base:${{ hashFiles('core/base/Dockerfile','core/base/requirements-prod.txt') }}-arm,mode=max *.platform=${{ inputs.architecture }} # This job builds all the images. The build cache is stored in the github actions cache. # In further jobs, this cache is used to quickly rebuild the images. build: name: Build images for linux/amd64 if: inputs.architecture == 'linux/amd64' needs: - targets - build-base-image-x64 strategy: fail-fast: false matrix: target: ${{ fromJson(needs.targets.outputs.matrix) }} runs-on: ubuntu-latest permissions: contents: read packages: write steps: - uses: actions/checkout@v3 - name: Retrieve global variables shell: bash run: | echo "BRANCH=${{ inputs.branch }}" >> $GITHUB_ENV echo "MAILU_VERSION=${{ inputs.mailu_version }}" >> $GITHUB_ENV echo "PINNED_MAILU_VERSION=${{ inputs.pinned_mailu_version }}" >> $GITHUB_ENV echo "DOCKER_ORG=${{ inputs.docker_org }}" >> $GITHUB_ENV - name: Set up QEMU uses: docker/setup-qemu-action@v2 - uses: crazy-max/ghaction-github-runtime@v2 - name: Set up Docker Buildx uses: docker/setup-buildx-action@v2 - name: Login to GitHub Container Registry uses: docker/login-action@v2 with: registry: ghcr.io username: ${{ github.repository_owner }} password: ${{ secrets.GITHUB_TOKEN }} - name: Login to Docker Hub uses: docker/login-action@v2 with: username: ${{ secrets.Docker_Login }} password: ${{ secrets.Docker_Password }} - name: Helper to convert docker org to lowercase id: string uses: ASzc/change-string-case-action@v5 with: string: ${{ github.repository_owner }} - name: Build all docker images env: DOCKER_ORG: ghcr.io/${{ steps.string.outputs.lowercase }} MAILU_VERSION: ${{ env.MAILU_VERSION }} PINNED_MAILU_VERSION: ${{ env.PINNED_MAILU_VERSION }} uses: docker/bake-action@v2 with: files: ${{env.HCL_FILE}} targets: ${{ matrix.target }} load: false push: true set: | *.cache-from=type=registry,ref=ghcr.io/${{ steps.string.outputs.lowercase }}/${{ matrix.target }}:buildcache *.cache-to=type=registry,ref=ghcr.io/${{ steps.string.outputs.lowercase }}/${{ matrix.target }}:buildcache,mode=max *.cache-from=type=registry,ref=ghcr.io/${{ steps.string.outputs.lowercase }}/base:${{ hashFiles('core/base/Dockerfile','core/base/requirements-prod.txt') }} *.platform=${{ inputs.architecture }} # This job builds all the images. The build cache is stored in the github actions cache. # In further jobs, this cache is used to quickly rebuild the images. build-arm: name: Build images for ARM64 & ARM/V7 if: inputs.architecture != 'linux/amd64' needs: - targets - build-base-image-arm strategy: fail-fast: false matrix: target: ${{ fromJson(needs.targets.outputs.matrix) }} runs-on: self-hosted permissions: contents: read packages: write steps: - uses: actions/checkout@v3 - name: Retrieve global variables shell: bash run: | echo "BRANCH=${{ inputs.branch }}" >> $GITHUB_ENV echo "MAILU_VERSION=${{ inputs.mailu_version }}" >> $GITHUB_ENV echo "PINNED_MAILU_VERSION=${{ inputs.pinned_mailu_version }}" >> $GITHUB_ENV echo "DOCKER_ORG=${{ inputs.docker_org }}" >> $GITHUB_ENV - name: Set up QEMU uses: docker/setup-qemu-action@v2 - uses: crazy-max/ghaction-github-runtime@v2 - name: Set up Docker Buildx uses: docker/setup-buildx-action@v2 - name: Login to GitHub Container Registry uses: docker/login-action@v2 with: registry: ghcr.io username: ${{ github.repository_owner }} password: ${{ secrets.GITHUB_TOKEN }} - name: Login to Docker Hub uses: docker/login-action@v2 with: username: ${{ secrets.Docker_Login }} password: ${{ secrets.Docker_Password }} - name: Helper to convert docker org to lowercase id: string uses: ASzc/change-string-case-action@v5 with: string: ${{ github.repository_owner }} - name: Build all docker images env: DOCKER_ORG: ghcr.io/${{ steps.string.outputs.lowercase }} MAILU_VERSION: ${{ env.MAILU_VERSION }} PINNED_MAILU_VERSION: ${{ env.PINNED_MAILU_VERSION }} uses: docker/bake-action@v2 with: files: ${{env.HCL_FILE}} targets: ${{ matrix.target }} load: false push: true set: | *.cache-from=type=registry,ref=ghcr.io/${{ steps.string.outputs.lowercase }}/${{ matrix.target }}:buildcache-arm *.cache-to=type=registry,ref=ghcr.io/${{ steps.string.outputs.lowercase }}/${{ matrix.target }}:buildcache-arm,mode=max *.cache-from=type=registry,ref=ghcr.io/${{ steps.string.outputs.lowercase }}/base:${{ hashFiles('core/base/Dockerfile','core/base/requirements-prod.txt') }}-arm *.platform=${{ inputs.architecture }} # This job runs all the tests. tests: name: tests if: inputs.architecture == 'linux/amd64' runs-on: ubuntu-latest permissions: contents: read packages: read needs: - build strategy: fail-fast: false matrix: target: ["core", "fetchmail", "filters", "webmail", "webdav"] time: ["2"] include: - target: "filters" time: "3" exclude: - target: "filters" time: "2" steps: - uses: actions/checkout@v3 - name: Retrieve global variables shell: bash run: | echo "BRANCH=${{ inputs.branch }}" >> $GITHUB_ENV echo "MAILU_VERSION=${{ inputs.mailu_version }}" >> $GITHUB_ENV echo "PINNED_MAILU_VERSION=${{ inputs.pinned_mailu_version }}" >> $GITHUB_ENV echo "DOCKER_ORG=${{ inputs.docker_org }}" >> $GITHUB_ENV - name: Set up QEMU uses: docker/setup-qemu-action@v2 - uses: crazy-max/ghaction-github-runtime@v2 - name: Set up Docker Buildx uses: docker/setup-buildx-action@v2 - name: Login to GitHub Container Registry uses: docker/login-action@v2 with: registry: ghcr.io username: ${{ github.repository_owner }} password: ${{ secrets.GITHUB_TOKEN }} - name: Helper to convert docker org to lowercase id: string uses: ASzc/change-string-case-action@v5 with: string: ${{ github.repository_owner }} - name: Install python packages run: python3 -m pip install -r tests/requirements.txt - name: Copy all certs run: sudo -- sh -c 'mkdir -p /mailu && cp -r tests/certs /mailu && chmod 600 /mailu/certs/*' - name: Test ${{ matrix.target }} run: python tests/compose/test.py ${{ matrix.target }} ${{ matrix.time }} env: DOCKER_ORG: ghcr.io/${{ steps.string.outputs.lowercase }} MAILU_VERSION: ${{ env.MAILU_VERSION }} PINNED_MAILU_VERSION: ${{ env.PINNED_MAILU_VERSION }} deploy: name: Deploy images # Deploying is not required for staging if: inputs.deploy == 'true' runs-on: ubuntu-latest needs: - tests strategy: fail-fast: false matrix: target: ["setup", "docs", "fetchmail", "webmail", "admin", "traefik-certdumper", "radicale", "clamav", "rspamd", "postfix", "dovecot", "unbound", "nginx"] steps: - uses: actions/checkout@v3 - name: Retrieve global variables shell: bash run: | echo "BRANCH=${{ inputs.branch }}" >> $GITHUB_ENV echo "MAILU_VERSION=${{ inputs.mailu_version }}" >> $GITHUB_ENV echo "PINNED_MAILU_VERSION=${{ inputs.pinned_mailu_version }}" >> $GITHUB_ENV echo "DOCKER_ORG=${{ inputs.docker_org }}" >> $GITHUB_ENV - name: Set up QEMU uses: docker/setup-qemu-action@v2 - uses: crazy-max/ghaction-github-runtime@v2 - name: Set up Docker Buildx uses: docker/setup-buildx-action@v2 - name: Login to Docker Hub uses: docker/login-action@v2 with: username: ${{ secrets.Docker_Login }} password: ${{ secrets.Docker_Password }} - name: Helper to convert docker org to lowercase id: string uses: ASzc/change-string-case-action@v5 with: string: ${{ github.repository_owner }} - name: Push image to Docker shell: bash run: | if [ '${{ env.MAILU_VERSION }}' == 'master' ]; then pinned_mailu_version='master'; else pinned_mailu_version=${{ env.PINNED_MAILU_VERSION}}; fi; docker buildx imagetools create \ --tag ${{ inputs.docker_org }}/${{ matrix.target }}:${{ env.MAILU_VERSION }} \ --tag ${{ inputs.docker_org }}/${{ matrix.target }}:$pinned_mailu_version \ --tag ${{ inputs.docker_org }}/${{ matrix.target }}:latest \ ghcr.io/${{ steps.string.outputs.lowercase }}/${{ matrix.target }}:${{ env.MAILU_VERSION }} deploy-arm: name: Deploy images for arm # Deploying is not required for staging if: inputs.deploy == 'true' && inputs.architecture != 'linux/amd64' runs-on: self-hosted needs: - build-arm strategy: fail-fast: false matrix: target: ["setup", "docs", "fetchmail", "webmail", "admin", "traefik-certdumper", "radicale", "clamav", "rspamd", "postfix", "dovecot", "unbound", "nginx"] steps: - uses: actions/checkout@v3 - name: Retrieve global variables shell: bash run: | echo "BRANCH=${{ inputs.branch }}" >> $GITHUB_ENV echo "MAILU_VERSION=${{ inputs.mailu_version }}" >> $GITHUB_ENV echo "PINNED_MAILU_VERSION=${{ inputs.pinned_mailu_version }}" >> $GITHUB_ENV echo "DOCKER_ORG=${{ inputs.docker_org }}" >> $GITHUB_ENV - name: Set up QEMU uses: docker/setup-qemu-action@v2 - uses: crazy-max/ghaction-github-runtime@v2 - name: Set up Docker Buildx uses: docker/setup-buildx-action@v2 - name: Login to Docker Hub uses: docker/login-action@v2 with: username: ${{ secrets.Docker_Login }} password: ${{ secrets.Docker_Password }} - name: Helper to convert docker org to lowercase id: string uses: ASzc/change-string-case-action@v5 with: string: ${{ github.repository_owner }} - name: Push image to Docker shell: bash run: | if [ '${{ env.MAILU_VERSION }}' == 'master-arm' ]; then pinned_mailu_version='master-arm'; else pinned_mailu_version=${{ env.PINNED_MAILU_VERSION}}; fi; docker buildx imagetools create \ --tag ${{ inputs.docker_org }}/${{ matrix.target }}:${{ env.MAILU_VERSION }} \ --tag ${{ inputs.docker_org }}/${{ matrix.target }}:$pinned_mailu_version \ --tag ${{ inputs.docker_org }}/${{ matrix.target }}:latest \ ghcr.io/${{ steps.string.outputs.lowercase }}/${{ matrix.target }}:${{ env.MAILU_VERSION }} #This job creates a tagged release. A tag is created for the pinned version x.y.z. The GH release refers to this tag. tag-release: if: inputs.release == 'true' runs-on: ubuntu-latest needs: - deploy steps: - uses: actions/checkout@v3 with: # fetch-depth 0 is required to also retrieve all tags. fetch-depth: 0 - name: Retrieve global variables shell: bash run: | echo "BRANCH=${{ inputs.branch }}" >> $GITHUB_ENV echo "MAILU_VERSION=${{ inputs.mailu_version }}" >> $GITHUB_ENV echo "PINNED_MAILU_VERSION=${{ inputs.pinned_mailu_version }}" >> $GITHUB_ENV echo "DOCKER_ORG=${{ inputs.docker_org }}" >> $GITHUB_ENV - name: Create tag for branch x.y. shell: bash run: | echo git tag ${{ env.PINNED_MAILU_VERSION }} $(/usr/bin/git rev-parse HEAD) git tag ${{ env.PINNED_MAILU_VERSION }} $(/usr/bin/git rev-parse HEAD) git push origin ${{ env.PINNED_MAILU_VERSION }} - name: Create release for tag x.y.z. uses: ncipollo/release-action@v1 with: bodyFile: "RELEASE_TEMPLATE.md" tag: ${{ env.PINNED_MAILU_VERSION }} token: ${{ secrets.GITHUB_TOKEN }}