diff --git a/automation/build.sh b/automation/build.sh index 8cfe7fd4..121138f1 100755 --- a/automation/build.sh +++ b/automation/build.sh @@ -9,7 +9,6 @@ # * TAG: The default will be the current branch name # * PUSH_IMAGES # * CLEANUP -# * MIXPANEL_TOKEN: default key to inject in the supervisor image # * EXTRA_TAG: when PUSH_IMAGES is true, additional tag to push to the registries # # Builds the supervisor for the architecture defined by $ARCH. @@ -45,12 +44,12 @@ if ! [ -x "$(command -v npx)" ]; then 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 -TARGET_BUILD_IMAGE=balena/$ARCH-supervisor:$TAG-build - MASTER_IMAGE=balena/$ARCH-supervisor:master -MASTER_BUILD_IMAGE=balena/$ARCH-supervisor:master-build CACHE_FROM="" function useCache() { @@ -88,25 +87,50 @@ function processDockerfile() { 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 "${TARGET_BUILD_IMAGE}" useCache "${MASTER_IMAGE}" -useCache "${MASTER_BUILD_IMAGE}" # Wait for our cache to be downloaded wait -BUILD_ARGS="$CACHE_FROM --build-arg ARCH=${ARCH}" -# Try to build the first stage -processDockerfile | docker build -f - -t "${TARGET_BUILD_IMAGE}" --target BUILD ${BUILD_ARGS} . +BUILD_ARGS="$CACHE_FROM --buildArg ARCH=$ARCH" -# Now try to build the final stage -processDockerfile | docker build -f - -t "${TARGET_IMAGE}" ${BUILD_ARGS} . +# 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 - retryImagePush "${TARGET_BUILD_IMAGE}" & + # 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 @@ -121,7 +145,6 @@ wait if [ "$CLEANUP" = "true" ]; then docker rmi \ "${TARGET_IMAGE}" \ - "${TARGET_BUILD_IMAGE}" \ - "${MASTER_IMAGE}" \ - "${MASTER_BUILD_IMAGE}" + "${MASTER_IMAGE}" fi + diff --git a/balena.yml b/balena.yml new file mode 100644 index 00000000..54474041 --- /dev/null +++ b/balena.yml @@ -0,0 +1,6 @@ +name: balena-supervisor +description: >- + Balena Supervisor: balena's agent on devices. +joinable: false +type: sw.application +version: 12.10.10 diff --git a/circle.yml b/circle.yml index c8f05cb2..9bd65e05 100644 --- a/circle.yml +++ b/circle.yml @@ -8,7 +8,7 @@ defaults: &defaults version: 18.09.3 docker_layer_caching: true - run: - name: Check docker is running and install git + name: Check docker is running and install dependencies command: | docker info apk update && apk upgrade && apk add --no-cache \ @@ -19,9 +19,15 @@ defaults: &defaults nodejs \ nodejs-npm \ openssh-client + - run: + name: Install balena CLI and test it + command: | + apk add --no-cache curl python3 g++ linux-headers && \ + npm install balena-cli -g --production --unsafe-perm && \ + balena version -v - checkout - run: - name: Initialize the submodules (yocto layers) + name: Initialize the submodules command: | git submodule update --init --recursive git clean -fxd base-image @@ -43,15 +49,54 @@ defaults: &defaults else export PUSH_IMAGES=false fi - # start the build for this architecture + if [ "$PRODUCTION_API_TOKEN" != "" ]; then + balena login --token $PRODUCTION_API_TOKEN + export DEPLOY_TO_PRODUCTION=${DEPLOY_TO_PRODUCTION} + else + export DEPLOY_TO_PRODUCTION=false + fi + if [ "$STAGING_API_TOKEN" != "" ]; then + export DEPLOY_TO_PRODUCTION=${DEPLOY_TO_STAGING} + else + export DEPLOY_TO_STAGING=false + fi + # Create required env vars export TAG=$(echo ${CIRCLE_BRANCH} | sed 's/[^a-z0-9A-Z_.-]/-/g') export ARCH=${ARCH} - bash automation/build.sh - if [ "${CIRCLE_BRANCH}" = "master" ] && [ "${DEPLOY_TO_BALENA}" = "true" ]; then + export PROJECT_NAME=${ARCH}-supervisor + export SERVICE_NAME=main + # start the build for this architecture + bash -x automation/build.sh + if [ "${CIRCLE_BRANCH}" = "master" ] && [ "${DEPLOY_TO_STAGING}" = "true" ]; then echo "Deploying to balena API (staging)" - ARCH=${ARCH} TAG=$VERSION_TAG API_KEY=$STAGING_API_KEY API_ENDPOINT=$STAGING_API_ENDPOINT node automation/deploy-to-balena-cloud.js + BALENARC_BALENA_URL=$STAGING_API_ENDPOINT balena login --token $STAGING_API_TOKEN + # Create a draft release first in case the second step fails + releaseId=$(BALENARC_BALENA_URL=$STAGING_API_ENDPOINT balena deploy ${BALENA_OS_ORG}/${PROJECT_NAME} \ + --draft \ + --projectName ${PROJECT_NAME} --tag ${TAG} \ + --release-tag gh_branch ${TAG} version ${VERSION_TAG} | sed -n 's/.*Release: //p') + echo "Successfully deployed release ${releaseId}" + # Set release_version as is still needed some places + curl -X PATCH -H "Content-type: application/json" -H "Authorization: Bearer ${STAGING_API_TOKEN}" \ + "https://api.${STAGING_API_ENDPOINT}/v6/release?\$filter=commit%20eq%20'${releaseId}'%20and%20belongs_to__application/any(bta:bta/slug%20eq%20'${BALENA_OS_ORG}%2F${PROJECT_NAME}')" \ + -d "{\"release_version\": \"${VERSION_TAG}\", \"is_final\": true}" + # Cleanup credentials just in case + rm ~/.balena/token + fi + if [ "${CIRCLE_BRANCH}" = "master" ] && [ "${DEPLOY_TO_PRODUCTION}" = "true" ]; then echo "Deploying to balena API (production)" - ARCH=${ARCH} TAG=$VERSION_TAG API_KEY=$PRODUCTION_API_KEY API_ENDPOINT=$PRODUCTION_API_ENDPOINT node automation/deploy-to-balena-cloud.js + BALENARC_BALENA_URL=$PRODUCTION_API_ENDPOINT balena login --token $PRODUCTION_API_TOKEN + # Create a draft release first in case the second step fails + releaseId=$(BALENARC_BALENA_URL=$PRODUCTION_API_ENDPOINT balena deploy ${BALENA_OS_ORG}/${PROJECT_NAME} \ + --draft \ + --projectName ${PROJECT_NAME} --tag ${TAG} \ + --release-tag gh_branch ${TAG} version ${VERSION_TAG} | sed -n 's/.*Release: //p') + # Set release_version as is still needed some places + curl -X PATCH -H "Content-type: application/json" -H "Authorization: Bearer ${PRODUCTION_API_TOKEN}" \ + "https://api.${PRODUCTION_API_ENDPOINT}/v6/release?\$filter=commit%20eq%20'${releaseId}'%20and%20belongs_to__application/any(bta:bta/slug%20eq%20'${BALENA_OS_ORG}%2F${PROJECT_NAME}')" \ + -d "{\"release_version\": \"${VERSION_TAG}\", \"is_final\": true}" + # Cleanup credentials just in case + rm ~/.balena/token fi version: 2 @@ -74,8 +119,8 @@ jobs: DOCKER_USERNAME: travisciresin ARCH: amd64 PUSH_IMAGES: 'true' - STAGING_API_ENDPOINT: https://api.balena-staging.com - PRODUCTION_API_ENDPOINT: https://api.balena-cloud.com + STAGING_API_ENDPOINT: balena-staging.com + PRODUCTION_API_ENDPOINT: balena-cloud.com DEBUG: '' amd64: <<: *defaults @@ -83,8 +128,11 @@ jobs: DOCKER_USERNAME: travisciresin ARCH: amd64 PUSH_IMAGES: 'true' - STAGING_API_ENDPOINT: https://api.balena-staging.com - PRODUCTION_API_ENDPOINT: https://api.balena-cloud.com + STAGING_API_ENDPOINT: balena-staging.com + PRODUCTION_API_ENDPOINT: balena-cloud.com + DEPLOY_TO_PRODUCTION: 'true' + DEPLOY_TO_STAGING: 'true' + BALENA_OS_ORG: 'balena_os' DEBUG: '' i386: <<: *defaults @@ -92,8 +140,11 @@ jobs: DOCKER_USERNAME: travisciresin ARCH: i386 PUSH_IMAGES: 'true' - STAGING_API_ENDPOINT: https://api.balena-staging.com - PRODUCTION_API_ENDPOINT: https://api.balena-cloud.com + STAGING_API_ENDPOINT: balena-staging.com + PRODUCTION_API_ENDPOINT: balena-cloud.com + DEPLOY_TO_PRODUCTION: 'true' + DEPLOY_TO_STAGING: 'true' + BALENA_OS_ORG: 'balena_os' DEBUG: '' armv7hf: <<: *defaults @@ -101,8 +152,11 @@ jobs: DOCKER_USERNAME: travisciresin ARCH: armv7hf PUSH_IMAGES: 'true' - STAGING_API_ENDPOINT: https://api.balena-staging.com - PRODUCTION_API_ENDPOINT: https://api.balena-cloud.com + STAGING_API_ENDPOINT: balena-staging.com + PRODUCTION_API_ENDPOINT: balena-cloud.com + DEPLOY_TO_PRODUCTION: 'true' + DEPLOY_TO_STAGING: 'true' + BALENA_OS_ORG: 'balena_os' DEBUG: '' aarch64: <<: *defaults @@ -110,8 +164,11 @@ jobs: DOCKER_USERNAME: travisciresin ARCH: aarch64 PUSH_IMAGES: 'true' - STAGING_API_ENDPOINT: https://api.balena-staging.com - PRODUCTION_API_ENDPOINT: https://api.balena-cloud.com + STAGING_API_ENDPOINT: balena-staging.com + PRODUCTION_API_ENDPOINT: balena-cloud.com + DEPLOY_TO_PRODUCTION: 'true' + DEPLOY_TO_STAGING: 'true' + BALENA_OS_ORG: 'balena_os' DEBUG: '' rpi: <<: *defaults @@ -119,8 +176,11 @@ jobs: DOCKER_USERNAME: travisciresin ARCH: rpi PUSH_IMAGES: 'true' - STAGING_API_ENDPOINT: https://api.balena-staging.com - PRODUCTION_API_ENDPOINT: https://api.balena-cloud.com + STAGING_API_ENDPOINT: balena-staging.com + PRODUCTION_API_ENDPOINT: balena-cloud.com + DEPLOY_TO_PRODUCTION: 'true' + DEPLOY_TO_STAGING: 'true' + BALENA_OS_ORG: 'balena_os' DEBUG: '' workflows: