opf/openproject

View on GitHub
.github/workflows/docker.yml

Summary

Maintainability
Test Coverage
name: Docker

on:
  # build dev daily
  schedule:
    - cron: '20 2 * * *' # Daily at 02:20

  push:
    tags:
      - v*
  workflow_dispatch:
    inputs:
      tag:
        description: "The tag to release. Note that this happens by default on the tag push. Only run this action when something went wrong!"
        required: false

permissions:
  contents: read # to fetch code (actions/checkout)

env:
  REGISTRY_IMAGE: openproject/openproject

jobs:
  extract_version:
    runs-on: ubuntu-latest
    steps:
      - name: Extract version
        id: extract_version
        run: |
          if [[ ${{ github.event_name }} == 'push' ]]; then
            TAG_REF=${GITHUB_REF#refs/tags/}
            CHECKOUT_REF=$GITHUB_REF
          elif [[ ${{ github.event_name }} == 'schedule' ]]; then
            TAG_REF=dev
            CHECKOUT_REF=refs/heads/dev
          elif [[ ${{ github.event_name }} == 'workflow_dispatch' ]]; then
            TAG_REF=${{ inputs.tag }}
            CHECKOUT_REF=${{ inputs.tag }}
          else
            echo "Unsupported event"
            exit 1
          fi

          if [ -z "$TAG_REF" ] || [ -z "$CHECKOUT_REF" ]; then
            echo "No TAG_REF or CHECKOUT_REF set. Aborting"
            exit 1
          fi

          VERSION=${TAG_REF#v}
          echo "Version: $VERSION"
          echo "version=$VERSION" >> "$GITHUB_OUTPUT"
          echo "checkout_ref=$CHECKOUT_REF" >> "$GITHUB_OUTPUT"
    outputs:
      version: ${{ steps.extract_version.outputs.version }}
      checkout_ref: ${{ steps.extract_version.outputs.checkout_ref }}
  build:
    needs:
      - extract_version
    if: github.repository == 'opf/openproject'
    runs-on: runs-on,runner=32cpu-linux-x64,run-id=${{ github.run_id }}
    strategy:
      matrix:
        include:
          - platform: linux/amd64
            target: slim
          - platform: linux/arm64/v8
            target: slim
          - platform: linux/amd64
            target: all-in-one
          - platform: linux/ppc64le
            bim_support: false
            target: all-in-one
          - platform: linux/arm64/v8
            bim_support: false
            target: all-in-one
    steps:
      - name: Extract version
        id: extract_version
        run: |
          if [[ ${{ github.event_name }} == 'push' ]]; then
            TAG_REF=${GITHUB_REF#refs/tags/}
            CHECKOUT_REF=$GITHUB_REF
          elif [[ ${{ github.event_name }} == 'schedule' ]]; then
            TAG_REF=dev
            CHECKOUT_REF=refs/heads/dev
          elif [[ ${{ github.event_name }} == 'workflow_dispatch' ]]; then
            TAG_REF=${{ inputs.tag }}
            CHECKOUT_REF=${{ inputs.tag }}
          else
            echo "Unsupported event"
            exit 1
          fi
          
          if [ -z "$TAG_REF" ] || [ -z "$CHECKOUT_REF" ]; then
            echo "No TAG_REF or CHECKOUT_REF set. Aborting"
            exit 1
          fi

          VERSION=${TAG_REF#v}
          echo "Version: $VERSION"
          echo "::set-output name=version::$VERSION"
          echo "::set-output name=checkout_ref::$CHECKOUT_REF"
      - name: Checkout
        with:
          ref: ${{ steps.extract_version.outputs.checkout_ref }}
        uses: actions/checkout@v4
      - name: Prepare docker files
        run: |
          cp ./docker/prod/Dockerfile ./Dockerfile

          # Add build information
          echo "${{ steps.extract_version.outputs.checkout_ref }}" > PRODUCT_VERSION
          echo "https://github.com/opf/openproject/commits/${{ steps.extract_version.outputs.checkout_ref }}" > PRODUCT_URL
          date -u +"%Y-%m-%dT%H:%M:%SZ" > RELEASE_DATE
      - name: Set up QEMU
        uses: docker/setup-qemu-action@v2
      - name: Set up Docker Buildx
        id: buildx
        uses: docker/setup-buildx-action@v2
      - name: Login to Docker Hub
        uses: docker/login-action@v2
        with:
          username: ${{ secrets.DOCKER_USERNAME }}
          password: ${{ secrets.DOCKER_PASSWORD }}
      - name: Docker meta
        id: meta
        uses: docker/metadata-action@v4
        with:
          context: git
          labels: |
            io.artifacthub.package.readme-url=https://www.openproject.org/docs/installation-and-operations/installation/docker/
            org.opencontainers.image.documentation=https://www.openproject.org/docs/
            org.opencontainers.image.vendor=OpenProject GmbH
          tags: |
            type=semver,pattern={{version}},value=${{ needs.extract_version.outputs.version }}
          images: |
            ${{ env.REGISTRY_IMAGE }}
      - name: Build image
        id: build
        uses: docker/build-push-action@v4
        with:
          context: .
          platforms: ${{ matrix.platform }}
          target: ${{ matrix.target }}
          build-args: |
            BIM_SUPPORT=${{ matrix.bim_support }}
          pull: true
          load: true
          tags: ${{ steps.meta.outputs.tags }}
          labels: ${{ steps.meta.outputs.labels }}
      - name: Test
        # We only test the native container. If that fails the builds for the others
        # will be cancelled as well.
        if: matrix.platform == 'linux/amd64' && matrix.target == 'all-in-one'
        run: |
          docker run \
            --name openproject \
            -d -p 8080:80 --platform ${{ matrix.platform }} \
            -e SUPERVISORD_LOG_LEVEL=debug \
            -e OPENPROJECT_LOGIN__REQUIRED=false \
            -e OPENPROJECT_HTTPS=false \
            ${{ steps.build.outputs.imageid }}

          sleep 60

          docker logs openproject --tail 100
          wget -O- --retry-on-http-error=503,502 --retry-connrefused http://localhost:8080/api/v3
      - name: Push image
        id: push
        uses: docker/build-push-action@v4
        with:
          context: .
          platforms: ${{ matrix.platform }}
          target: ${{ matrix.target }}
          build-args: |
            BIM_SUPPORT=${{ matrix.bim_support }}
          labels: ${{ steps.meta.outputs.labels }}
          outputs: type=image,name=${{ env.REGISTRY_IMAGE }},push-by-digest=true,name-canonical=true,push=true
      - name: Export digest
        run: |
          mkdir -p /tmp/digests
          digest="${{ steps.push.outputs.digest }}"
          touch "/tmp/digests/${digest#sha256:}"
      - name: Upload digest
        uses: actions/upload-artifact@v3
        with:
          name: digests-${{ matrix.target }}
          path: /tmp/digests/*
          if-no-files-found: error
          retention-days: 1
  merge:
    runs-on: ubuntu-latest
    strategy:
      matrix:
        target: [slim, all-in-one]
    needs:
      - extract_version
      - build
    steps:
      - name: Download digests
        uses: actions/download-artifact@v3
        with:
          name: digests-${{ matrix.target }}
          path: /tmp/digests
      - name: Set suffix
        id: set_suffix
        run: |
          suffix="-${{ matrix.target }}"
          if [ "$suffix" = "-all-in-one" ]; then suffix="" ; fi
          echo "suffix=$suffix" >> "$GITHUB_OUTPUT"
      - name: Set up Docker Buildx
        uses: docker/setup-buildx-action@v2
      - name: Docker meta
        id: meta
        uses: docker/metadata-action@v4
        with:
          images: ${{ env.REGISTRY_IMAGE }}
          labels: |
            io.artifacthub.package.readme-url=https://www.openproject.org/docs/installation-and-operations/installation/docker/
            org.opencontainers.image.documentation=https://www.openproject.org/docs/
            org.opencontainers.image.vendor=OpenProject GmbH
          flavor: |
            latest=false
            suffix=${{ steps.set_suffix.outputs.suffix }}
          tags: |
            type=semver,pattern={{version}},value=${{ needs.extract_version.outputs.version }}
            type=semver,pattern={{major}}.{{minor}},value=${{ needs.extract_version.outputs.version }}
            type=semver,pattern={{major}},value=${{ needs.extract_version.outputs.version }}
            type=raw,value=dev,priority=200,enable={{is_default_branch}}
      - name: Login to Docker Hub
        uses: docker/login-action@v2
        with:
          username: ${{ secrets.DOCKER_USERNAME }}
          password: ${{ secrets.DOCKER_PASSWORD }}
      - name: Create manifest list and push
        working-directory: /tmp/digests
        run: |
          docker buildx imagetools create $(jq -cr '.tags | map("-t " + .) | join(" ")' <<< "$DOCKER_METADATA_OUTPUT_JSON") \
            $(printf '${{ env.REGISTRY_IMAGE }}@sha256:%s ' *)
      - name: Inspect image
        run: |
          docker buildx imagetools inspect ${{ env.REGISTRY_IMAGE }}:${{ steps.meta.outputs.version }}