#!/bin/bash # # balena-supervisor automated build # # Required variables: # * ARCH # # Optional variables: # * TAG: The default will be the current branch name # * PUSH_IMAGES # * CLEANUP # * EXTRA_TAG: when PUSH_IMAGES is true, additional tag to push to the registries # # Builds the supervisor for the architecture defined by $ARCH. # Will produce and push an image tagged as balena/$ARCH-supervisor:$TAG # # It pulls intermediate images for caching, if available: # balena/$ARCH-supervisor-node:$TAG # # In all of these cases it will use "master" if $TAG is not found. # # If PUSH_IMAGES is "true", it will also push the supervisor and intermediate images (must be logged in to dockerhub) # to the docker registry. # If CLEANUP is "true", all images will be removed after pushing - including any relevant images # that may have been on the host from before the build, so be careful! # # Requires docker >= 17.05 # set -e THIS_FILE=$0 if [ -z "$ARCH" ] ; then awk '{if(/^#/)print;else exit}' "${THIS_FILE}" | tail -n +2 | sed 's/\#//' exit 1 fi if [ -z "$TAG" ]; then TAG=$(git rev-parse --abbrev-ref HEAD) fi if ! [ -x "$(command -v npx)" ]; then echo 'NPM/npx is required to execute this script' >&2 exit 1 fi PROJECT_NAME="${PROJECT_NAME:-${ARCH}-supervisor}" SERVICE_NAME="${SERVICE_NAME:-main}" # This is the supervisor image we will produce TARGET_IMAGE=balena/$ARCH-supervisor:$TAG MASTER_IMAGE=balena/$ARCH-supervisor:master CACHE_FROM="" function useCache() { image=$1 # Always add the cache because we can't do it from # a subshell and specifying a missing image is fine CACHE_FROM="${CACHE_FROM} --cache-from $image" # Pull in parallel for speed docker pull "$image" & } function retryImagePush() { local image=$1 local -i retries local success=1 while (( retries < 3 )); do retries+=1 if docker push "${image}"; then success=0 break fi done return $success } # If we're building for an ARM architecture, we uncomment # the cross-build commands, to enable emulation function processDockerfile() { if [ "${ARCH}" == "aarch64" ] || [ "${ARCH}" == "armv7hf" ] || [ "${ARCH}" == "rpi" ]; then sed -E 's/#(.*"cross-build-(start|end)".*)/\1/g' Dockerfile else cat Dockerfile fi } function deviceType() { case ${ARCH} in aarch64) echo "raspberrypi4-64" ;; armv7hf) echo "raspberrypi3" ;; rpi) echo "raspberry-pi" ;; i386) echo "qemux86" ;; amd64) echo "intel-nuc" ;; *) echo "unrecognized architecture ${ARCH}" exit 1 ;; esac } export ARCH useCache "${TARGET_IMAGE}" useCache "${MASTER_IMAGE}" # Wait for our cache to be downloaded wait BUILD_ARGS="$CACHE_FROM --buildArg ARCH=$ARCH" # Make a copy of the file to match the architecture processDockerfile > Dockerfile.${ARCH} # Build the image balena build --deviceType $(deviceType) --arch ${ARCH} --dockerfile ./Dockerfile.${ARCH} \ --projectName ${PROJECT_NAME} --tag ${TAG} ${BUILD_ARGS} if [ "${PUSH_IMAGES}" == "true" ]; then # Tag the CLI generated image with the target docker tag "${PROJECT_NAME}_${SERVICE_NAME}:${TAG}" ${TARGET_IMAGE} retryImagePush "${TARGET_IMAGE}" & if [ -n "${EXTRA_TAG}" ]; then docker tag "${TARGET_IMAGE}" "balena/${ARCH}-supervisor:${EXTRA_TAG}" retryImagePush "balena/${ARCH}-supervisor:${EXTRA_TAG}" & fi fi # Wait for any ongoing deploys wait if [ "$CLEANUP" = "true" ]; then docker rmi \ "${TARGET_IMAGE}" \ "${MASTER_IMAGE}" fi