#!/bin/bash source ./hooks/arches.sh export DOCKER_CLI_EXPERIMENTAL=enabled # Join a list of args with a single char. # Ref: https://stackoverflow.com/a/17841619 join() { local IFS="$1"; shift; echo "$*"; } set -ex echo ">>> Starting local Docker registry..." # Docker Buildx's `docker-container` driver is needed for multi-platform # builds, but it can't access existing images on the Docker host (like the # cross-compiled ones we just built). Those images first need to be pushed to # a registry -- Docker Hub could be used, but since it's not trivial to clean # up those intermediate images on Docker Hub, it's easier to just run a local # Docker registry, which gets cleaned up automatically once the build job ends. # # https://docs.docker.com/registry/deploying/ # https://hub.docker.com/_/registry # # Use host networking so the buildx container can access the registry via # localhost. # docker run -d --name registry --network host registry:2 # defaults to port 5000 # Docker Hub sets a `DOCKER_REPO` env var with the format `index.docker.io/user/repo`. # Strip the registry portion to construct a local repo path for use in `Dockerfile.buildx`. LOCAL_REGISTRY="localhost:5000" REPO="${DOCKER_REPO#*/}" LOCAL_REPO="${LOCAL_REGISTRY}/${REPO}" echo ">>> Pushing images to local registry..." for arch in ${arches[@]}; do docker_image="${DOCKER_REPO}:${DOCKER_TAG}-${arch}" local_image="${LOCAL_REPO}:${DOCKER_TAG}-${arch}" docker tag "${docker_image}" "${local_image}" docker push "${local_image}" done echo ">>> Setting up Docker Buildx..." # Same as earlier, use host networking so the buildx container can access the # registry via localhost. # # Ref: https://github.com/docker/buildx/issues/94#issuecomment-534367714 # docker buildx create --name builder --use --driver-opt network=host echo ">>> Running Docker Buildx..." tags=("${DOCKER_REPO}:${DOCKER_TAG}") # If the Docker tag starts with a version number, assume the latest release # is being pushed. Add an extra tag (`latest` or `alpine`, as appropriate) # to make it easier for users to track the latest release. if [[ "${DOCKER_TAG}" =~ ^[0-9]+\.[0-9]+\.[0-9]+ ]]; then if [[ "${DOCKER_TAG}" == *alpine ]]; then tags+=(${DOCKER_REPO}:alpine) else tags+=(${DOCKER_REPO}:latest) fi fi tag_args=() for tag in "${tags[@]}"; do tag_args+=(--tag "${tag}") done # Docker Buildx takes a list of target platforms (OS/arch/variant), so map # the arch list to a platform list (assuming the OS is always `linux`). declare -A arch_to_platform=( [amd64]="linux/amd64" [armv6]="linux/arm/v6" [armv7]="linux/arm/v7" [arm64]="linux/arm64" ) platforms=() for arch in ${arches[@]}; do platforms+=("${arch_to_platform[$arch]}") done platforms="$(join "," "${platforms[@]}")" # Run the build, pushing the resulting images and multi-arch manifest list to # Docker Hub. The Dockerfile is read from stdin to avoid sending any build # context, which isn't needed here since the actual cross-compiled images # have already been built. docker buildx build \ --network host \ --build-arg LOCAL_REPO="${LOCAL_REPO}" \ --build-arg DOCKER_TAG="${DOCKER_TAG}" \ --platform "${platforms}" \ "${tag_args[@]}" \ --push \ - < ./docker/Dockerfile.buildx # Add an extra arch-specific tag for `arm32v6`; Docker can't seem to properly # auto-select that image on ARMv6 platforms like Raspberry Pi 1 and Zero # (https://github.com/moby/moby/issues/41017). # # Note that we use `arm32v6` instead of `armv6` to be consistent with the # existing vaultwarden tags, which adhere to the naming conventions of the # Docker per-architecture repos (e.g., https://hub.docker.com/u/arm32v6). # Unfortunately, these per-arch repo names aren't always consistent with the # corresponding platform (OS/arch/variant) IDs, particularly in the case of # 32-bit ARM arches (e.g., `linux/arm/v6` is used, not `linux/arm32/v6`). # # TODO: It looks like this issue should be fixed starting in Docker 20.10.0, # so this step can be removed once fixed versions are in wider distribution. # # Tags: # # testing => testing-arm32v6 # testing-alpine => # x.y.z => x.y.z-arm32v6, latest-arm32v6 # x.y.z-alpine => # if [[ "${DOCKER_TAG}" != *alpine ]]; then image="${DOCKER_REPO}":"${DOCKER_TAG}" # Fetch the multi-arch manifest list and find the digest of the armv6 image. filter='.manifests|.[]|select(.platform.architecture=="arm" and .platform.variant=="v6")|.digest' digest="$(docker manifest inspect "${image}" | jq -r "${filter}")" # Pull the armv6 image by digest, retag it, and repush it. docker pull "${DOCKER_REPO}"@"${digest}" docker tag "${DOCKER_REPO}"@"${digest}" "${image}"-arm32v6 docker push "${image}"-arm32v6 if [[ "${DOCKER_TAG}" =~ ^[0-9]+\.[0-9]+\.[0-9]+ ]]; then docker tag "${image}"-arm32v6 "${DOCKER_REPO}:latest"-arm32v6 docker push "${DOCKER_REPO}:latest"-arm32v6 fi fi