name: 'Build the DMS Container Image' on: workflow_call: inputs: platforms: required: false type: string default: linux/amd64 outputs: build-cache-key: description: The cache key to use when restoring an image later value: ${{ jobs.build-image.outputs.build-cache-key }} permissions: contents: read # `actions/cache` does not upload a new cache until completing a job successfully. # To better cache image builds, tests are handled in a dependent job afterwards. # This way failing tests will not prevent caching of an image. Useful when the build context # is not changed by new commits. jobs: build-image: name: 'Build' runs-on: ubuntu-22.04 outputs: build-cache-key: ${{ steps.derive-image-cache-key.outputs.digest }} steps: - name: 'Checkout' uses: actions/checkout@v3 with: submodules: recursive # Can potentially be replaced by: `${{ hashFiles('target/**', 'Dockerfile', 'VERSION') }}` # Must not be affected by file metadata changes and have a consistent sort order: # https://docs.github.com/en/actions/learn-github-actions/expressions#hashfiles # Keying by the relevant build context is more re-usable than a commit SHA. - name: 'Derive Docker image cache key from content' id: derive-image-cache-key shell: bash run: | ADDITIONAL_FILES=( 'Dockerfile' 'VERSION' ) # Recursively collect file paths from `target/` and pipe a list of # checksums to be sorted (by hash value) and finally generate a checksum # of that list, using `awk` to only return the hash value (digest): IMAGE_CHECKSUM=$(\ find ./target -type f -exec sha256sum "${ADDITIONAL_FILES[@]}" {} + \ | sort \ | sha256sum \ | awk '{ print $1 }' \ ) echo "digest=${IMAGE_CHECKSUM}" >>"${GITHUB_OUTPUT}" # Attempts to restore the build cache from a prior build run. # If the exact key is not restored, then upon a successful job run # the new cache is uploaded for this key containing the contents at `path`. # Cache storage has a limit of 10GB, and uploads expire after 7 days. # When full, the least accessed cache upload is evicted to free up storage. # https://docs.github.com/en/actions/using-workflows/caching-dependencies-to-speed-up-workflows - name: 'Handle Docker build layer cache' uses: actions/cache@v3 with: path: /tmp/.buildx-cache key: cache-buildx-${{ steps.derive-image-cache-key.outputs.digest }} # If no exact cache-hit for key found, lookup caches with a `cache-buildx-` key prefix: # This is safe due to cache layer invalidation via the image build context. restore-keys: | cache-buildx- - name: 'Set up QEMU' uses: docker/setup-qemu-action@v2.1.0 with: platforms: arm64 - name: 'Set up Docker Buildx' uses: docker/setup-buildx-action@v2.4.0 # NOTE: AMD64 can build within 2 minutes - name: 'Build images' uses: docker/build-push-action@v3.3.0 with: context: . # Build at least the AMD64 image (which runs against the test suite). platforms: ${{ inputs.platforms }} # Paired with steps `actions/cache` and `Replace cache` (replace src with dest): # NOTE: `mode=max` is only for `cache-to`, it configures exporting all image layers. # https://github.com/docker/buildx/blob/master/docs/reference/buildx_build.md#cache-from cache-from: type=local,src=/tmp/.buildx-cache cache-to: type=local,dest=/tmp/.buildx-cache-new,mode=max # This job just builds the image and stores to cache, no other exporting required: # https://github.com/docker/build-push-action/issues/546#issuecomment-1122631106 outputs: type=cacheonly # WORKAROUND: The `cache-to: type=local` input for `build-push-action` persists old-unused cache. # The workaround is to write the new build cache to a different location that replaces the # original restored cache after build, reducing frequency of eviction due to cache storage limit (10GB). # https://github.com/docker/build-push-action/blob/965c6a410d446a30e95d35052c67d6eded60dad6/docs/advanced/cache.md?plain=1#L193-L199 # NOTE: This does not affect `cache-hit == 'true'` (which skips upload on direct cache key hit) - name: 'Replace cache' run: | rm -rf /tmp/.buildx-cache mv /tmp/.buildx-cache-new /tmp/.buildx-cache