From 1156684a70f938280a9d8a105189301df847b9fc Mon Sep 17 00:00:00 2001 From: Kyle Harding Date: Sat, 10 Feb 2024 13:18:31 -0500 Subject: [PATCH] Create workflow to build and deploy balenaOS Change-type: minor Signed-off-by: Kyle Harding --- .../workflows/dispatch-yocto-build-deploy.yml | 117 ++++ .github/workflows/yocto-build-deploy.yml | 603 ++++++++++++++++++ automation/Dockerfile_s3-deploy-env | 13 + 3 files changed, 733 insertions(+) create mode 100644 .github/workflows/dispatch-yocto-build-deploy.yml create mode 100644 .github/workflows/yocto-build-deploy.yml create mode 100644 automation/Dockerfile_s3-deploy-env diff --git a/.github/workflows/dispatch-yocto-build-deploy.yml b/.github/workflows/dispatch-yocto-build-deploy.yml new file mode 100644 index 000000000..1741cf5bc --- /dev/null +++ b/.github/workflows/dispatch-yocto-build-deploy.yml @@ -0,0 +1,117 @@ +name: "Call Yocto Build and Deploy Workflow" + +on: + pull_request: + types: [opened, synchronize] + branches: + - "main" + - "master" + pull_request_target: + types: [opened, synchronize] + branches: + - "main" + - "master" + + workflow_dispatch: + # you may only define up to 10 `inputs` for a `workflow_dispatch` event + inputs: + device-repo: + description: balenaOS device repository (owner/repo) + required: true + type: string + default: balena-os/balena-generic + device-repo-ref: + description: balenaOS device repository tag, branch, or commit to build + required: false + type: string + default: master + meta-balena-ref: + description: meta-balena ref if not the currently pinned version + required: false + type: string + yocto-scripts-ref: + description: balena-yocto-scripts ref if not the currently pinned version + required: false + type: string + machine: + description: yocto board name + required: true + type: string + default: generic-amd64 + environment: + description: Select deploy environment + required: false + type: choice + options: + - > + { + "environment": "balena-staging.com", + "s3-bucket": "resin-staging-img", + "s3-region": "us-east-1", + "aws-subnet": "subnet-0d73c1f0da85add17", + "aws-security-group": "sg-09dd285d11b681946" + } + - > + { + "environment": "balena-cloud.com", + "s3-bucket": "resin-production-img-cloudformation", + "s3-region": "us-east-1", + "aws-subnet": "subnet-02d18a08ea4058574", + "aws-security-group": "sg-057937f4d89d9d51c" + } + deploy-s3: + description: Whether to deploy images to S3 + required: false + type: boolean + default: true + deploy-hostapp: + description: Whether to deploy a hostApp container image to a balena environment + required: false + type: boolean + default: true + deploy-ami: + description: Whether to deploy an AMI to AWS + required: false + type: boolean + default: false + sign-image: + description: Whether to sign image for secure boot + required: false + type: boolean + default: false + # do-esr: + # description: Enable to deploy ESR + # required: false + # type: boolean + # default: false + +jobs: + yocto-build-deploy: + name: Yocto Build and Deploy + uses: ./.github/workflows/yocto-build-deploy.yml + # prevent duplicate workflow executions for pull_request and pull_request_target + if: | + ( + github.event.pull_request.head.repo.full_name == github.repository && + github.event_name == 'pull_request' + ) || ( + github.event.pull_request.head.repo.full_name != github.repository && + github.event_name == 'pull_request_target' + ) + secrets: inherit + with: + device-repo: ${{ inputs.device-repo || 'balena-generic' }} + device-repo-ref: ${{ inputs.device-repo-ref || 'master' }} + meta-balena-ref: ${{ inputs.meta-balena-ref }} + yocto-scripts-ref: ${{ inputs.yocto-scripts-ref }} + machine: ${{ inputs.machine || 'generic-amd64' }} + balena-environment: ${{ fromJSON(inputs.environment || '{}').environment || 'balena-cloud.com' }} + s3-region: ${{ fromJSON(inputs.environment || '{}').s3-region || 'us-east-1' }} + s3-bucket: ${{ fromJSON(inputs.environment || '{}').s3-bucket || 'resin-staging-img' }} + aws-subnet: ${{ fromJSON(inputs.environment || '{}').aws-subnet || 'subnet-0d73c1f0da85add17' }} + aws-security-group: ${{ fromJSON(inputs.environment || '{}').aws-security-group || 'sg-09dd285d11b681946' }} + deploy-s3: ${{ inputs.deploy-s3 || false }} + deploy-hostapp: ${{ inputs.deploy-hostapp || false }} + deploy-ami: ${{ inputs.deploy-ami || false }} + sign-image: ${{ inputs.sign-image || false }} + do-esr: ${{ inputs.do-esr || false }} diff --git a/.github/workflows/yocto-build-deploy.yml b/.github/workflows/yocto-build-deploy.yml new file mode 100644 index 000000000..37b3305f8 --- /dev/null +++ b/.github/workflows/yocto-build-deploy.yml @@ -0,0 +1,603 @@ +name: "Yocto Build and Deploy" + +on: + workflow_call: + secrets: + BALENA_API_KEY: + description: balena API key for deploying releases + required: false + S3_ACCESS_KEY: + description: S3 access key + required: false + S3_SECRET_KEY: + description: S3 secret key + required: false + DOCKERHUB_USER: + description: Dockerhub user for pulling private helper images + required: false + DOCKERHUB_TOKEN: + description: Dockerhub token for pulling private helper images + required: false + SIGN_KMOD_KEY_APPEND: + description: Base64-encoded public key of a kernel module signing keypair + required: false + SIGN_API_KEY: + description: Base64-encoded private key of a kernel module signing keypair + required: false + inputs: + device-repo: + description: balenaOS device repository (owner/repo) + required: true + type: string + default: ${{ github.repository }} + device-repo-ref: + description: balenaOS device repository tag, branch, or commit to build + required: false + type: string + default: ${{ github.ref }} + meta-balena-ref: + description: meta-balena ref if not the currently pinned version + required: false + type: string + yocto-scripts-ref: + description: balena-yocto-scripts ref if not the currently pinned version + required: false + type: string + machine: + description: yocto board name + required: true + type: string + balena-environment: + description: balena environment URL (e.g. balena-cloud.com) + required: true + type: string + default: ${{ vars.HOSTAPP_ENVIRONMENT || 'balena-cloud.com' }} + hostapp-org: + description: balenaCloud Organization for deploying hostapp releases + required: false + type: string + default: ${{ vars.HOSTAPP_ORG || 'balena_os' }} + s3-region: + description: S3 region (e.g. 'us-east-1') + required: false + type: string + default: ${{ vars.S3_REGION || 'us-east-1' }} + s3-bucket: + description: S3 bucket (e.g. resin-production-img-cloudformation) + required: false + type: string + default: ${{ vars.S3_BUCKET || 'resin-production-img-cloudformation' }} + aws-subnet: + description: AWS subnet for AMI deploys + required: false + type: string + default: ${{ vars.AWS_SUBNET_ID || 'subnet-02d18a08ea4058574' }} + aws-security-group: + description: AWS security group for AMI deploys + required: false + type: string + default: ${{ vars.AWS_SECURITY_GROUP_ID || 'sg-057937f4d89d9d51c' }} + sign-api-url: + description: URL for signing secure-boot images + required: false + type: string + default: ${{ vars.SIGN_API_URL || 'https://sign.balena-cloud.com' }} + yocto-cache-host: + description: Yocto NFS sstate cache host + required: false + type: string + default: ${{ vars.YOCTO_CACHE_HOST || 'nfs.product-os.io' }} + deploy-s3: + description: Whether to deploy images to S3 + required: false + type: boolean + default: true + deploy-hostapp: + description: Whether to deploy a hostApp container image to a balena environment + required: false + type: boolean + default: true + deploy-ami: + description: Whether to deploy an AMI to AWS + required: false + type: boolean + default: false + sign-image: + description: Whether to sign image for secure boot + required: false + type: boolean + default: false + do-esr: + description: Enable to deploy ESR + required: false + type: boolean + default: false + +jobs: + build: + runs-on: [self-hosted, X64] + + env: + MACHINE: "${{ inputs.machine }}" + automation_dir: "${{ github.workspace }}/balena-yocto-scripts/automation" + BALENARC_BALENA_URL: "${{ inputs.balena-environment }}" + WORKSPACE: ${{ github.workspace }} + VERBOSE: verbose + YOCTO_CACHE_DIR: /var/lib/yocto + BARYS_ARGUMENTS_VAR: "" + + outputs: + hostapp_path: ${{ steps.build.outputs.hostapp_path }} + os_version: ${{ steps.build-lib.outputs.os_version }} + device_slug: ${{ steps.build-lib.outputs.device_slug }} + deploy_artifact: ${{ steps.build-lib.outputs.deploy_artifact }} + is_private: ${{ steps.build-lib.outputs.is_private }} + dt_arch: ${{ steps.build-lib.outputs.dt_arch }} + meta_balena_version: ${{ steps.build-lib.outputs.meta_balena_version }} + yocto_scripts_ref: ${{ steps.build-lib.outputs.yocto_scripts_ref }} + + defaults: + run: + working-directory: ${{ github.workspace }} + shell: bash --noprofile --norc -eo pipefail -x {0} + + steps: + # https://github.com/actions/checkout + - name: Clone device repository + uses: actions/checkout@v3 + with: + repository: ${{ inputs.device-repo }} + token: ${{ secrets.GITHUB_TOKEN }} + ref: ${{ inputs.device-repo-ref }} + submodules: true + + - name: Update meta-balena submodule to ${{ inputs.meta-balena-ref }} + if: inputs.meta-balena-ref != '' + working-directory: ${{ github.workspace }}/layers/meta-balena + run: | + git config --add remote.origin.fetch '+refs/pull/*:refs/remotes/origin/pr/*' + git fetch --all + git checkout --force "${{ inputs.meta-balena-ref }}" + git submodule update --init --recursive + + - name: Update balena-yocto-scripts submodule to ${{ inputs.yocto-scripts-ref }} + if: inputs.yocto-scripts-ref != '' + working-directory: ${{ github.workspace }}/balena-yocto-scripts + run: | + git config --add remote.origin.fetch '+refs/pull/*:refs/remotes/origin/pr/*' + git fetch --all + git checkout --force "${{ inputs.yocto-scripts-ref }}" + git submodule update --init --recursive + + - name: Device repository check + run: | + if [ "$(yq '.type' repo.yml)" != "yocto-based OS image" ]; then + echo "::error::Repository does not appear to be of type 'yocto-based OS image'" + exit 1 + fi + + - name: Set build outputs + id: build-lib + run: | + source "${automation_dir}/include/balena-api.inc" + source "${automation_dir}/include/balena-lib.inc" + + device_slug="$(balena_lib_get_slug "${MACHINE}")" + echo "device_slug=${device_slug}" >> $GITHUB_OUTPUTS + + os_version="$(balena_lib_get_os_version)" + echo "os_version=${os_version}" >> $GITHUB_OUTPUTS + + meta_balena_version="$(balena_lib_get_meta_balena_base_version)" + echo "meta_balena_version=${meta_balena_version}" >> $GITHUB_OUTPUTS + + yocto_scripts_ref="$(git submodule status balena-yocto-scripts | awk '{print $1}')" + echo "yocto_scripts_ref=${yocto_scripts_ref}" >> $GITHUB_OUTPUTS + + deploy_artifact="$(balena_lib_get_deploy_artifact)" + echo "deploy_artifact=${deploy_artifact}" >> $GITHUB_OUTPUTS + + dt_arch="$(balena_lib_get_dt_arch "${MACHINE}")" + echo "dt_arch=${dt_arch}" >> $GITHUB_OUTPUTS + + is_private="$(balena_api_is_dt_private "${{ inputs.machine }}")" + echo "is_private=${is_private}" >> $GITHUB_OUTPUTS + + if [ ! -f "${WORKSPACE}/balena.yml" ]; then + _contract=$(balena_lib_build_contract "${device_slug}") + cp "${_contract}" "${WORKSPACE}/balena.yml" + fi + + - name: Enable signed images + if: inputs.sign-image == true && inputs.sign-api-url != '' + env: + SIGN_API: "${{ inputs.sign-api-url }}" + SIGN_API_KEY: "${{ secrets.SIGN_API_KEY }}" + SIGN_GRUB_KEY_ID: 2EB29B4CE0132F6337897F5FB8A88D1C62FCC729 + SIGN_KMOD_KEY_APPEND: "${{ secrets.SIGN_KMOD_KEY_APPEND }}" + run: | + echo "BARYS_ARGUMENTS_VAR=${BARYS_ARGUMENTS_VAR} -a SIGN_API=${SIGN_API} -a SIGN_API_KEY=${SIGN_API_KEY} -a SIGN_GRUB_KEY_ID=${SIGN_GRUB_KEY_ID} -a SIGN_KMOD_KEY_APPEND=${SIGN_KMOD_KEY_APPEND} --bb-args --no-setscene" >> $GITHUB_ENV + + - name: Mount shared cache + if: inputs.yocto-cache-host != '' + run: | + sudo mkdir -p "${YOCTO_CACHE_DIR}" + sudo chown $(id -u):$(id -g) "${YOCTO_CACHE_DIR}" + sudo mount -t nfs "${{ inputs.yocto-cache-host }}:/" "${YOCTO_CACHE_DIR}" -o fsc + + - name: Build + id: build + run: | + ./balena-yocto-scripts/build/balena-build.sh -d "${MACHINE}" -t "${{ secrets.BALENA_API_KEY }}" -s "${YOCTO_CACHE_DIR}" -g "${BARYS_ARGUMENTS_VAR}" + hostapp_path=$(find "${WORKSPACE}/build/tmp/deploy/" -name "balena-image-${MACHINE}.docker" -type l || true) + echo "hostapp_path=${hostapp_path}" >> $GITHUB_OUTPUTS + + - name: Prepare artifacts + id: prepare + run: | + source "${automation_dir}/include/balena-deploy.inc" + balena_deploy_artifacts "${{ inputs.machine }}" "${WORKSPACE}/gh-deploy" false + tar --auto-compress -cvf "${{ runner.temp }}/build-artifacts.tar.zst" "${WORKSPACE}/gh-deploy" + + # https://github.com/actions/upload-artifact + - name: Upload artifacts + uses: actions/upload-artifact@26f96dfa697d77e81fd5907df203aa23a56210a8 # v4.3.0 + with: + name: build-artifacts + path: ${{ runner.temp }}/build-artifacts.tar.zst + retention-days: 3 + + s3-deploy: + runs-on: [self-hosted, X64] + needs: build + if: inputs.deploy-s3 == true + + env: + MACHINE: "${{ inputs.machine }}" + BALENARC_BALENA_URL: "${{ inputs.balena-environment }}" + WORKSPACE: ${{ github.workspace }} + VERBOSE: verbose + + defaults: + run: + working-directory: ${{ github.workspace }} + shell: bash --noprofile --norc -eo pipefail -x {0} + + steps: + # TODO: clone balena-yocto-scripts @ yocto_scripts_ref instead + # https://github.com/actions/checkout + - name: Clone device repository + uses: actions/checkout@v3 + with: + repository: ${{ inputs.device-repo || github.repository }} + token: ${{ secrets.GITHUB_TOKEN }} + ref: ${{ inputs.device-repo-ref || github.ref }} + submodules: true + + - name: Update balena-yocto-scripts submodule to ${{ inputs.yocto-scripts-ref }} + if: inputs.yocto-scripts-ref != '' + working-directory: ${{ github.workspace }}/balena-yocto-scripts + run: | + git config --add remote.origin.fetch '+refs/pull/*:refs/remotes/origin/pr/*' + git fetch --all + git checkout --force "${{ inputs.yocto-scripts-ref }}" + git submodule update --init --recursive + + # https://github.com/actions/download-artifact + - name: Download build artifacts + uses: actions/download-artifact@eaceaf801fd36c7dee90939fad912460b18a1ffe # v4.1.2 + with: + path: ${{ runner.temp }} + name: build-artifacts + + - name: Extract build artifacts + run: | + tar -xvf ${{ runner.temp }}/build-artifacts.tar.zst + find . + + - name: Set s3 outputs + id: s3-lib + run: | + echo "s3_policy=private" >> $GITHUB_OUTPUTS + if [ "${{ needs.build.outputs.is_private }}" = "false" ]; then + echo "s3_policy=public-read" >> $GITHUB_OUTPUTS + fi + + echo "deployer_uid=$(id -u)" >> $GITHUB_OUTPUTS + echo "deployer_gid=$(id -g)" >> $GITHUB_OUTPUTS + + s3_bucket_path="${{ inputs.s3-bucket}}/images" + if [ "${{ inputs.do-esr}}" = "true" ]; then + s3_bucket_path="${{ inputs.s3-bucket}}/esr-images" + fi + + # https://github.com/docker/setup-buildx-action + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + # login required to pull private balena/balena-img image + # https://github.com/docker/login-action + - name: Login to Docker Hub + uses: docker/login-action@343f7c4344506bcbf9b4de18042ae17996df046d # v3.0.0 + with: + registry: docker.io + username: ${{ secrets.DOCKERHUB_USER }} + password: ${{ secrets.DOCKERHUB_TOKEN }} + + # https://github.com/docker/build-push-action + - name: Build helper image + id: helper-image + uses: docker/build-push-action@v5.1.0 + with: + context: "${{ github.workspace }}/balena-yocto-scripts/automation" + file: Dockerfile_s3-deploy-env + push: false + build-args: | + DEPLOYER_UID=${{ steps.s3-lib.outputs.deployer_uid }} + DEPLOYER_GID=${{ steps.s3-lib.outputs.deployer_gid }} + + - name: Deploy to S3 + id: s3-deploy + env: + BASE_DIR: /host/images + S3_CMD: "s4cmd --access-key=${{ secrets.S3_ACCESS_KEY }} --secret-key=${{ secrets.S3_SECRET_KEY }} --API-ServerSideEncryption=AES256" + S3_SYNC_OPTS: "--recursive --API-ACL=${{ steps.s3-lib.outputs.s3_policy }}" + S3_BUCKET: "${{ inputs.s3-bucket }}" + SLUG: "${{ needs.build.outputs.device_slug }}" + DEPLOY_ARTIFACT: "${{ needs.build.outputs.deploy_artifact }}" + HOSTOS_VERSION: "${{ needs.build.outputs.os_version }}" + # TODO: images path must be found in artifacts + IMAGES_PATH: ${{ github.workspace }}/build-artifacts + run: | + docker run --rm -t --user deployer \ + -e BASE_DIR \ + -e S3_CMD \ + -e S3_SYNC_OPTS \ + -e S3_BUCKET \ + -e SLUG \ + -e DEPLOY_ARTIFACT \ + -e HOSTOS_VERSION \ + -v "${IMAGES_PATH}:/host/images" \ + "${{ steps.helper-image.outputs.imageid }}" /bin/sh -e -c '\ + echo "${HOSTOS_VERSION}" > "/host/images/${SLUG}/latest" + if [ "$DEPLOY_ARTIFACT" != "docker-image" ]; then + /usr/src/app/node_modules/.bin/ts-node /usr/src/app/scripts/prepare.ts + fi + if [ -z "$($S3_CMD ls s3://${S3_BUCKET}/${SLUG}/${HOSTOS_VERSION}/)" ] || [ -n "$($S3_CMD ls s3://${S3_BUCKET}/${SLUG}/${HOSTOS_VERSION}/IGNORE)" ]; then + touch /host/images/${SLUG}/${HOSTOS_VERSION}/IGNORE + $S3_CMD del -rf s3://${S3_BUCKET}/${SLUG}/${HOSTOS_VERSION} + $S3_CMD put /host/images/${SLUG}/${HOSTOS_VERSION}/IGNORE s3://${S3_BUCKET}/${SLUG}/${HOSTOS_VERSION}/ + $S3_CMD $S3_SYNC_OPTS dsync /host/images/${SLUG}/${HOSTOS_VERSION}/ s3://${S3_BUCKET}/${SLUG}/${HOSTOS_VERSION}/ + $S3_CMD put /host/images/${SLUG}/latest s3://${S3_BUCKET}/${SLUG}/ --API-ACL=public-read -f + $S3_CMD del s3://${S3_BUCKET}/${SLUG}/${HOSTOS_VERSION}/IGNORE + else + echo "WARNING: Deployment already done for ${SLUG} at version ${HOSTOS_VERSION}" + fi + ' + + ami-deploy: + runs-on: [self-hosted, X64] + needs: build + if: inputs.deploy-ami == true + + env: + MACHINE: "${{ inputs.machine }}" + BALENARC_BALENA_URL: "${{ inputs.balena-environment }}" + WORKSPACE: ${{ github.workspace }} + VERBOSE: verbose + + defaults: + run: + working-directory: ${{ github.workspace }} + shell: bash --noprofile --norc -eo pipefail -x {0} + + steps: + # TODO: clone balena-yocto-scripts @ yocto_scripts_ref instead + # https://github.com/actions/checkout + - name: Clone device repository + uses: actions/checkout@v3 + with: + repository: ${{ inputs.device-repo || github.repository }} + token: ${{ secrets.GITHUB_TOKEN }} + ref: ${{ inputs.device-repo-ref || github.ref }} + submodules: true + + - name: Update balena-yocto-scripts submodule to ${{ inputs.yocto-scripts-ref }} + if: inputs.yocto-scripts-ref != '' + working-directory: ${{ github.workspace }}/balena-yocto-scripts + run: | + git config --add remote.origin.fetch '+refs/pull/*:refs/remotes/origin/pr/*' + git fetch --all + git checkout --force "${{ inputs.yocto-scripts-ref }}" + git submodule update --init --recursive + + # https://github.com/actions/download-artifact + - name: Download build artifacts + uses: actions/download-artifact@eaceaf801fd36c7dee90939fad912460b18a1ffe # v4.1.2 + with: + path: ${{ runner.temp }} + name: build-artifacts + + - name: Extract build artifacts + run: | + tar -xvf ${{ runner.temp }}/build-artifacts.tar.zst + find . + + - name: Set AMI outputs + id: ami-lib + run: | + if [ "${dt_arch}" = "amd64" ]; then + echo "ami_arch=x86_64" >> $GITHUB_OUTPUTS + elif [ "${dt_arch}" = "aarch64" ]; then + echo "ami_arch=arm64" >> $GITHUB_OUTPUTS + fi + + # AMI name format: balenaOS(-installer?)(-secureboot?)-VERSION-DEVICE_TYPE + if [ "${{ inputs.sign-image }}" = "true" ]; then + echo "ami_name=balenaOS-secureboot-${VERSION}-${MACHINE}" >> $GITHUB_OUTPUTS + else + echo "ami_name=balenaOS-${VERSION}-${MACHINE}" >> $GITHUB_OUTPUTS + fi + + # https://github.com/docker/setup-buildx-action + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + # https://github.com/docker/build-push-action + - name: Build helper image + id: helper-image + uses: docker/build-push-action@v5.1.0 + with: + context: "${{ github.workspace }}/balena-yocto-scripts/automation" + file: Dockerfile_yocto-build-env + target: yocto-generate-ami-env + push: false + + - name: Deploy AMI + env: + S3_REGION: "${{ inputs.s3-region }}" + S3_BUCKET: "${{ inputs.s3-bucket }}" + AWS_ACCESS_KEY_ID: "${{ secrets.S3_ACCESS_KEY}}" + AWS_SECRET_ACCESS_KEY: "${{ secrets.S3_SECRET_KEY }}" + AWS_DEFAULT_REGION: "${{ inputs.s3-region }}" + AWS_SESSION_TOKEN: "" # only required if MFA is enabled + AWS_SUBNET_ID: "${{ inputs.aws-subnet }}" + AWS_SECURITY_GROUP_ID: "${{ inputs.aws-security-group }}" + BALENACLI_TOKEN: ${{ secrets.BALENA_API_KEY }} + HOSTOS_VERSION: "${{ needs.build.outputs.os_version }}" + AMI_NAME: "${{ steps.ami-lib.outputs.ami_name }}" + AMI_ARCHITECTURE: "${{ steps.ami-lib.outputs.ami_arch }}" + AMI_SECUREBOOT: "${{ inputs.sign-image }}" + BALENA_PRELOAD_APP: "balena_os/cloud-config-${{ steps.ami-lib.outputs.ami_arch }}" + BALENA_PRELOAD_COMMIT: current + # TODO: image path must be found in artifacts + IMAGE: balena-image-${MACHINE}.balenaos-img + run: | + docker run --rm -t \ + --privileged \ + --network host \ + -v "${WORKSPACE}:${WORKSPACE}" \ + -v /var/run/docker.sock:/var/run/docker.sock \ + -e VERBOSE \ + -e AWS_ACCESS_KEY_ID \ + -e AWS_SECRET_ACCESS_KEY \ + -e AWS_DEFAULT_REGION \ + -e AWS_SESSION_TOKEN \ + -e AMI_NAME \ + -e AMI_ARCHITECTURE \ + -e AMI_SECUREBOOT \ + -e S3_BUCKET \ + -e BALENA_PRELOAD_APP \ + -e BALENARC_BALENA_URL \ + -e BALENACLI_TOKEN \ + -e BALENA_PRELOAD_COMMIT \ + -e IMAGE \ + -e MACHINE \ + -e HOSTOS_VERSION \ + -e AWS_SUBNET_ID \ + -e AWS_SECURITY_GROUP_ID \ + -w "${WORKSPACE}" \ + "${{ steps.helper-image.outputs.imageid }}" /balena-generate-ami.sh + + balena-deploy: + runs-on: [self-hosted, X64] + needs: build + if: inputs.deploy-hostapp == true + + env: + MACHINE: "${{ inputs.machine }}" + BALENARC_BALENA_URL: "${{ inputs.balena-environment }}" + WORKSPACE: ${{ github.workspace }} + VERBOSE: verbose + SLUG: "${{ needs.build.outputs.device_slug }}" + SECURE_BOOT: "${{ inputs.sign-image }}" + SIGN_API_URL: "${{ inputs.sign-api-url }}" + + defaults: + run: + working-directory: ${{ github.workspace }} + shell: bash --noprofile --norc -eo pipefail -x {0} + + steps: + # TODO: clone balena-yocto-scripts @ yocto_scripts_ref instead + # https://github.com/actions/checkout + - name: Clone device repository + uses: actions/checkout@v3 + with: + repository: ${{ inputs.device-repo || github.repository }} + token: ${{ secrets.GITHUB_TOKEN }} + ref: ${{ inputs.device-repo-ref || github.ref }} + submodules: true + + # https://github.com/actions/download-artifact + - name: Download build artifacts + uses: actions/download-artifact@eaceaf801fd36c7dee90939fad912460b18a1ffe # v4.1.2 + with: + path: ${{ runner.temp }} + name: build-artifacts + + - name: Extract build artifacts + run: | + tar -xvf ${{ runner.temp }}/build-artifacts.tar.zst + find . + + - name: Set deploy outputs + run: | + if [ -n "${{ inputs.sign-image }}" = "true" ]; then + echo "SECURE_BOOT_FEATURE_FLAG=yes" >> $GITHUB_ENV + else + echo "SECURE_BOOT_FEATURE_FLAG=no" >> $GITHUB_ENV + fi + + # https://github.com/docker/setup-buildx-action + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + # https://github.com/docker/build-push-action + - name: Build helper image + id: helper-image + uses: docker/build-push-action@v5.1.0 + with: + context: "${{ github.workspace }}/balena-yocto-scripts/automation" + file: Dockerfile_yocto-build-env + target: yocto-build-env + push: false + + - name: Deploy to balena + env: + APPNAME: "${{ needs.build.outputs.device_slug }}" + API_ENV: "${{ inputs.balena-environment }}" + BALENAOS_TOKEN: "${{ secrets.BALENA_API_KEY }}" + BALENAOS_ACCOUNT: "${{ inputs.hostapp-org }}" + META_BALENA_VERSION: "${{ needs.build.outputs.meta_balena_version }}" + RELEASE_VERSION: "${{ needs.build.outputs.os_version }}" + BOOTABLE: 1 + DEPLOY: yes + FINAL: yes + ESR: "${{ inputs.do-esr }}" + balenaCloudEmail: + balenaCloudPassword: + # TODO: hostapp path must be found in artifacts + HOSTAPP_PATH: "${{ needs.build.outputs.hostapp_path }}" + run: | + docker run --rm -t \ + --privileged \ + -e APPNAME \ + -e API_ENV \ + -e BALENAOS_TOKEN \ + -e BALENAOS_ACCOUNT \ + -e META_BALENA_VERSION \ + -e RELEASE_VERSION \ + -e MACHINE \ + -e VERBOSE \ + -e BOOTABLE \ + -e DEPLOY \ + -e FINAL \ + -e ESR \ + -e SECURE_BOOT_FEATURE_FLAG \ + -e balenaCloudEmail \ + -e balenaCloudPassword \ + -v "$(readlink --canonicalize "${HOSTAPP_PATH}")":/host/appimage.docker \ + -v "${WORKSPACE}":/work \ + -v "${WORKSPACE}":/deploy \ + "${{ steps.helper-image.outputs.imageid }}" /balena-deploy-block.sh diff --git a/automation/Dockerfile_s3-deploy-env b/automation/Dockerfile_s3-deploy-env new file mode 100644 index 000000000..91b98406c --- /dev/null +++ b/automation/Dockerfile_s3-deploy-env @@ -0,0 +1,13 @@ +# private base image requires docker login +FROM balena/balena-img:6.20.26 + +ARG DEPLOYER_UID +ARG DEPLOYER_GID + +# hadolint ignore=DL3008 +RUN apt-get update \ + && apt-get install -y --no-install-recommends s4cmd \ + && rm -rf /var/lib/apt/lists/* + +RUN groupadd -g $DEPLOYER_GID deployer \ + useradd -m -u $DEPLOYER_UID -g $DEPLOYER_GID deployer