image: mono refs/heads/edge, core -, fw - #4116
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
name: 'Build Flex image on github workflows' | |
run-name: 'image: mono ${{ inputs.monorepo-ref }}, core ${{ inputs.oe-core-ref }}, fw ${{ inputs.ot3-firmware-ref }}' | |
on: | |
workflow_dispatch: | |
inputs: | |
monorepo-ref: | |
description: | | |
Ref of https://github.com/opentrons/opentrons to build. This MUST be a full ref, e.g. refs/heads/edge, or '-' to indicate not-specified. If not specified, will be determined from the oe-core ref if specified, and then default to edge. | |
required: true | |
default: '-' | |
oe-core-ref: | |
description: | | |
Ref of https://github.com/opentrons/oe-core to build. This is different from the ref specified in the github api/webUI when starting this workflow - that ref is what contains this workflow, this ref specifies what gets built. It MUST be a full ref, e.g. refs/heads/main, or '-' to indicate not-specified. If not specified, will be decided based on the monorepo ref; if that isn't specified, will be main. | |
required: true | |
default: '-' | |
ot3-firmware-ref: | |
description: | | |
Ref of https://github.com/opentrons/ot3-firmware to build. It MUST be a full ref, e.g. refs/heads/main, or '-' to indicate not-specified. If not specified, will be decided based on the monorepo ref; if that isn't specified, will be main. | |
required: false | |
default: '-' | |
infra-stage: | |
description: | | |
What infra stage to run on. This should almost always be prod; staging and dev are useful when you explicitly want to test those infra stages. | |
required: true | |
type: choice | |
options: | |
- 'stage-prod' | |
- 'stage-staging' | |
- 'stage-dev' | |
default: 'stage-prod' | |
jobs: | |
decide-refs: | |
runs-on: ubuntu-latest | |
outputs: | |
oe-core: ${{ steps.build-refs.outputs.oe-core }} | |
monorepo: ${{ steps.build-refs.outputs.monorepo }} | |
ot3-firmware: ${{ steps.build-refs.outputs.ot3-firmware }} | |
variant: ${{ steps.build-refs.outputs.variant }} | |
build-type: ${{ steps.build-refs.outputs.build-type }} | |
name: 'deciding refs to build' | |
steps: | |
- name: Fetch initial sources for action | |
uses: 'actions/checkout@v3' | |
with: | |
submodules: false | |
path: ./oe-core-for-workflow | |
- name: Decide refs to build | |
id: build-refs | |
uses: './oe-core-for-workflow/.github/actions/build-refs' | |
with: | |
token: ${{ github.token }} | |
monorepo: ${{ inputs.monorepo-ref }} | |
oe-core: ${{ inputs.oe-core-ref }} | |
ot3-firmware: ${{ inputs.ot3-firmware-ref }} | |
run-build: | |
needs: decide-refs | |
strategy: | |
matrix: | |
build_env: [ '${{ inputs.infra-stage }}' ] | |
name: 'Building ${{needs.decide-refs.outputs.variant}} images on ${{ matrix.build_env }}' | |
timeout-minutes: 480 | |
runs-on: ['self-hosted', '${{matrix.build_env}}', '${{needs.decide-refs.outputs.variant}}'] | |
steps: | |
- name: Fetch initial sources for action | |
uses: 'actions/checkout@v3' | |
with: | |
submodules: false | |
fetch-depth: 0 | |
path: ./oe-core-for-workflow | |
- name: Fetch oe-core source | |
uses: 'actions/checkout@v3' | |
with: | |
submodules: false | |
fetch-depth: 0 | |
ref: ${{needs.decide-refs.outputs.oe-core}} | |
path: ./oe-core | |
- name: Fetch monorepo source | |
uses: 'actions/checkout@v3' | |
with: | |
fetch-depth: 0 | |
ref: ${{ needs.decide-refs.outputs.monorepo }} | |
repository: Opentrons/opentrons | |
path: ./opentrons | |
- name: Fetch ot3-firmware source | |
uses: 'actions/checkout@v3' | |
with: | |
fetch-depth: 0 | |
ref: ${{ needs.decide-refs.outputs.ot3-firmware }} | |
repository: Opentrons/ot3-firmware | |
path: ./ot3-firmware | |
- name: Sync oe-core submodules | |
run: | | |
chown -R `whoami` oe-core | |
chown -R `whoami` opentrons | |
chown -R `whoami` ot3-firmware | |
cd oe-core | |
./update.sh | |
cd .. | |
- name: Configure AWS Credentials | |
uses: './oe-core-for-workflow/.github/actions/aws-credentials' | |
id: aws | |
with: | |
access_key_id: ${{ secrets.ROBOT_STACK_AWS_ACCESS_KEY_ID }} | |
secret_access_key: ${{ secrets.ROBOT_STACK_AWS_SECRET_ACCESS_KEY }} | |
region: us-east-2 | |
stage: ${{ matrix.build_env }} | |
- name: Build container | |
run: | | |
cd oe-core | |
tmp_dir=$(mktemp -d -t ci-XXXXXXX) | |
cp start.sh $tmp_dir/ | |
docker build -f ./Dockerfile --tag "ot3-image:latest" $tmp_dir | |
cd .. | |
- name: Apply unconditional CI config overrides | |
run: | | |
cd oe-core | |
echo "" >> ./build/conf/local.conf | |
echo 'DL_DIR = "/volumes/cache/downloads"' >> ./build/conf/local.conf | |
echo 'GITDIR = "/volumes/cache/git"' >> ./build/conf/local.conf | |
echo 'SSTATE_DIR = "/volumes/cache/sstate"' >> ./build/conf/local.conf | |
echo 'OT_BUILD_TYPE = "${{needs.decide-refs.outputs.build-type}}"' >> ./build/conf/local.conf | |
echo 'YARN_CACHE_DIR = "/volumes/cache/yarn"' >> ./build/conf/local.conf | |
echo 'ELECTRON_CACHE_DIR = "/volumes/cache/electron"' >> ./build/conf/local.conf | |
cd .. | |
- name: Apply internal-release variant CI config overrides | |
if: needs.decide-refs.outputs.variant == 'internal-release' | |
run: | | |
cd oe-core | |
echo 'OPENTRONS_PROJECT = "ot3"' >> ./build/conf/local.conf | |
cd .. | |
- name: Apply release variant CI config overrides | |
if: needs.decide-refs.outputs.variant == 'release' | |
run: | | |
cd oe-core | |
echo 'OPENTRONS_PROJECT = "robot-stack"' >> ./build/conf/local.conf | |
cd .. | |
- name: Apply release-build-only CI config overrides | |
if: needs.decide-refs.outputs.build-type == 'release' | |
run: | | |
cd oe-core | |
echo 'MIXPANEL_ID = "${{ secrets.MIXPANEL_ID }}"' >> ./build/conf/local.conf | |
# setup signing key | |
echo 'SIGNING_KEY = "${TOPDIR}/.signing-key"' >> ./build/conf/local.conf | |
cat <<EOF >./build/.signing-key | |
${{secrets.ROBOT_SIGNING_KEY}} | |
EOF | |
cd .. | |
- name: Pull S3 cache | |
shell: bash | |
run: | | |
# fetch cache if the size is less than 20GB, so we have enough space to build image. | |
sizeInBytes=`aws --profile=${{ steps.aws.outputs.profile_name }} s3 ls s3://${S3_CACHE_ARN/arn:aws:s3:::/} --recursive --summarize | awk END'{print}' | grep -o [0-9].*` | |
sizeInGigabytes=$(($sizeInBytes/1024/1024/1024)) | |
if [[ sizeInGigabytes -gt 50 ]]; then | |
echo "Doing clean build without cache, size of cache: ${sizeInGigabytes}GB!" | |
else | |
aws_cp="aws --profile=${{ steps.aws.outputs.profile_name }} s3 cp --no-progress" | |
cachedir=${LOCAL_CACHE:-./cache} | |
for cachetype in downloads sstate git ; do | |
localzip=$(realpath ${cachedir}/../${cachetype}.zip) | |
thiscache=${cachedir}/${cachetype} | |
echo "Fetching cache for ${cachetype} to ${localzip}" | |
mkdir -p ${thiscache} | |
TIME="%E" time $aws_cp s3://${S3_CACHE_ARN/arn:aws:s3:::/}/${cachetype}.zip ${localzip} 2>elapsed || continue | |
echo "Fetched $(du -h ${localzip} | cut -f 1)B in $(cat elapsed), extracting to ${thiscache}" || 0 | |
TIME="%E" time unzip -q -u -o ${localzip} -d ${thiscache} 2>elapsed | |
echo "Extracted $(du -h -d 1 $thiscache | tail -n 1 | cut -f 1)B to ${thiscache} in $(cat elapsed)" || 0 | |
done | |
fi | |
- name: Download sources | |
run: | | |
here=$(pwd) | |
oe_mount="type=bind,src=$here/oe-core,dst=/volumes/oe-core,consistency=delegated" | |
monorepo_mount="type=bind,src=$here/opentrons,dst=/volumes/opentrons,consistency=delegated" | |
ot3_firmware_mount="type=bind,src=$here/ot3-firmware,dst=/volumes/ot3-firmware,consistency=delegated" | |
cache_mount="type=bind,src=${LOCAL_CACHE:-./cache},dst=/volumes/cache,consistency=delegated" | |
tmp_mount="type=tmpfs,dst=/tmp" | |
echo "docker run --rm --mount $oe_mount --mount $monorepo_mount --mount $ot3_firmware_mount --mount $cache_mount --mount $tmp_mount ot3-image:latest opentrons-ot3-image --runall=fetch" | |
docker run --rm --mount $oe_mount --mount $monorepo_mount --mount $ot3_firmware_mount --mount $cache_mount --mount $tmp_mount ot3-image:latest opentrons-ot3-image --runall=fetch | |
- name: Build image | |
run: | | |
here=$(pwd) | |
oe_mount="type=bind,src=$here/oe-core,dst=/volumes/oe-core,consistency=delegated" | |
monorepo_mount="type=bind,src=$here/opentrons,dst=/volumes/opentrons,consistency=delegated" | |
ot3_firmware_mount="type=bind,src=$here/ot3-firmware,dst=/volumes/ot3-firmware,consistency=delegated" | |
cache_mount="type=bind,src=${LOCAL_CACHE:-./cache},dst=/volumes/cache,consistency=delegated" | |
tmp_mount="type=tmpfs,dst=/tmp" | |
echo "docker run --rm --mount $oe_mount --mount $monorepo_mount --mount $ot3_firmware_mount --mount $cache_mount --mount $tmp_mount ot3-image:latest opentrons-ot3-image" | |
docker run --rm --mount $oe_mount --mount $monorepo_mount --mount $ot3_firmware_mount --mount $cache_mount --mount $tmp_mount ot3-image:latest opentrons-ot3-image | |
- name: Prune images | |
if: always() | |
run: docker image prune -af | |
- name: Push S3 cache | |
shell: bash | |
continue-on-error: true | |
run: | | |
aws_cp="aws --profile=${{ steps.aws.outputs.profile_name }} s3 cp --no-progress" | |
cachedir=${LOCAL_CACHE:-./cache} | |
for cachetype in downloads sstate git ; do | |
df -h | |
localzip=$(realpath ${cachedir}/../${cachetype}.zip) | |
thiscache=${cachedir}/${cachetype} | |
cd ${thiscache} | |
echo "Refreshing cache for ${cachetype} from ${thiscache} to ${localzip}" | |
TIME="%E" time zip -q -r --filesync --symlinks ${localzip} ./* 2>elapsed | |
echo "Refreshed cache in $(cat elapsed)" || 0 | |
TIME="%E" time ${aws_cp} ${localzip} s3://${S3_CACHE_ARN/arn:aws:s3:::/}/${cachetype}.zip 2>elapsed | |
echo "Uploaded $(du -h $localzip | cut -f 1)B in $(cat elapsed)" || 0 | |
done | |
- name: Gather results | |
id: artifacts | |
run: | | |
_oe_build=$(pwd)/oe-core/build | |
echo "found build dir at ${_oe_build}" | |
_artifact_root=${_oe_build}/deploy | |
_artifact_unversioned_subdir=opentrons | |
_artifact_s3=${_artifact_root}/${_artifact_unversioned_subdir} | |
_artifact_versioned=${_artifact_root}/opentrons-versioned | |
_oe_images=${_oe_build}/deploy/images | |
_oe_tmp=${_oe_build}/tmp | |
mkdir -p ${_artifact_s3} | |
find ${_oe_images} -name "*opentrons-ot3-image-Tezi*" -exec cp {} ${_artifact_s3}/ot3-fullimage.tar \; | |
find ${_oe_images} -name "ot3-system.zip" -exec cp {} ${_artifact_s3} \; | |
find ${_oe_images} -name "VERSION.json" -exec cp {} ${_artifact_s3} \; | |
find ${_oe_images} -name "release-notes.md" -exec cp {} ${_artifact_s3} \; | |
tar czf ${_artifact_s3}/buildstats.tar.gz ${_oe_tmp}/buildstats | |
mkdir -p ${_artifact_versioned} | |
_fulltag="${{needs.decide-refs.outputs.monorepo}}" | |
echo "monorepo_shorttag=${_fulltag:10}" >> $GITHUB_OUTPUT | |
_vers_suffix=${{needs.decide-refs.outputs.variant == 'release' && '${_fulltag:11}' || '${_fulltag:14}'}} | |
_system_zipname="ot3-system-${_vers_suffix}.zip" | |
_version_jsonname="VERSION-${_vers_suffix}.json" | |
_fullimage_tarname="ot3-fullimage-${_vers_suffix}.tar" | |
cp ${_artifact_s3}/ot3-system.zip "${_artifact_versioned}/${_system_zipname}" | |
cp ${_artifact_s3}/VERSION.json "${_artifact_versioned}/${_version_jsonname}" | |
cp ${_artifact_s3}/ot3-fullimage.tar "${_artifact_versioned}/${_fullimage_tarname}" | |
echo "versioned_system_zip=${_artifact_versioned}/${_system_zipname}" >> $GITHUB_OUTPUT | |
echo "versioned_version_json=${_artifact_versioned}/${_version_jsonname}" >> $GITHUB_OUTPUT | |
echo "versioned_fullimage_tar=${_artifact_versioned}/${_fullimage_tarname}" >> $GITHUB_OUTPUT | |
echo "artifact_root=${_artifact_root}" >> $GITHUB_OUTPUT | |
echo "artifact_unversioned_subdir=${_artifact_unversioned_subdir}" >> $GITHUB_OUTPUT | |
echo "artifact_versioned=${_artifact_versioned}" >> $GITHUB_OUTPUT | |
- name: Handle Release | |
if: ${{ needs.decide-refs.outputs.build-type == 'release' }} | |
shell: bash | |
id: 'handle-release' | |
run: | | |
pushd oe-core/scripts | |
aws --profile=${{ steps.aws.outputs.profile_name }} s3 cp --acl=public-read s3://${S3_ARTIFACT_ARN/arn:aws:s3:::/}/ot3-oe/releases.json releases.json | |
base_url=https://${S3_ARTIFACT_ARN/arn:aws:s3:::/}/ot3-oe/${{ github.run_id }} | |
version_file=${{ steps.artifacts.outputs.artifact_root }}/${{ steps.artifacts.outputs.artifact_unversioned_subdir }}/VERSION.json | |
python3 update_releases_file.py --releases-file releases.json --version-file ${version_file} --base-url $base_url | |
aws --profile=${{ steps.aws.outputs.profile_name }} s3 cp --acl=public-read releases.json s3://${S3_ARTIFACT_ARN/arn:aws:s3:::/}/ot3-oe/ | |
popd | |
- name: Upload results to S3 | |
shell: bash | |
id: 'upload-results' | |
run: | | |
pushd ${{ steps.artifacts.outputs.artifact_root }} | |
aws --profile=${{ steps.aws.outputs.profile_name }} s3 cp --acl=public-read --recursive ${{ steps.artifacts.outputs.artifact_unversioned_subdir }} s3://${S3_ARTIFACT_ARN/arn:aws:s3:::/}/ot3-oe/${{ github.run_id }} | |
root_url=https://${S3_ARTIFACT_ARN/arn:aws:s3:::/}/ot3-oe/${{ github.run_id }} | |
echo "console_url=https://s3.console.aws.amazon.com/s3/buckets/${S3_ARTIFACT_ARN/arn:aws:s3::::/}?prefix=${{ github.run_id }}" >> $GITHUB_OUTPUT | |
echo "version_file_url=$root_url/VERSION.json" >> $GITHUB_OUTPUT | |
echo "release_notes_file_url=$root_url/release-notes.md" >> $GITHUB_OUTPUT | |
echo "system_url=$root_url/ot3-system.zip" >> $GITHUB_OUTPUT | |
echo "fullimage_url=$root_url/ot3-fullimage.tar" >> $GITHUB_OUTPUT | |
popd | |
- name: Upload system zip to monorepo release | |
if: needs.decide-refs.outputs.build-type == 'release' | |
uses: 'ncipollo/[email protected]' | |
with: | |
allowUpdates: true | |
omitBody: true | |
omitName: true | |
omitPrereleaseDuringUpdate: true | |
omitDraftDuringUpdate: true | |
repo: opentrons | |
tag: ${{ steps.artifacts.outputs.monorepo_shorttag }} | |
artifacts: ${{ steps.artifacts.outputs.versioned_system_zip }} | |
artifactContentType: application/zip | |
token: ${{secrets.MONOREPO_RELEASE_ARTIFACT_UPLOAD_TOKEN}} | |
- name: Upload fullimage tar to monorepo release | |
if: needs.decide-refs.outputs.build-type == 'release' | |
uses: 'ncipollo/[email protected]' | |
with: | |
allowUpdates: true | |
omitBody: true | |
omitName: true | |
omitPrereleaseDuringUpdate: true | |
omitDraftDuringUpdate: true | |
repo: opentrons | |
tag: ${{ steps.artifacts.outputs.monorepo_shorttag }} | |
artifacts: ${{ steps.artifacts.outputs.versioned_fullimage_tar }} | |
artifactContentType: application/x-tar | |
token: ${{secrets.MONOREPO_RELEASE_ARTIFACT_UPLOAD_TOKEN}} | |
- name: Upload version json to monorepo release | |
if: needs.decide-refs.outputs.build-type == 'release' | |
uses: 'ncipollo/[email protected]' | |
with: | |
allowUpdates: true | |
omitBody: true | |
omitName: true | |
omitPrereleaseDuringUpdate: true | |
omitDraftDuringUpdate: true | |
repo: opentrons | |
tag: ${{ steps.artifacts.outputs.monorepo_shorttag }} | |
artifacts: ${{ steps.artifacts.outputs.versioned_version_json }} | |
artifactContentType: application/json | |
token: ${{secrets.MONOREPO_RELEASE_ARTIFACT_UPLOAD_TOKEN}} | |
- name: Post results as internal-release | |
if: ${{ matrix.build_env == 'stage-prod' && needs.decide-refs.outputs.variant=='internal-release' }} | |
uses: slackapi/[email protected] | |
with: | |
payload: "{\"s3-url\":\"${{ steps.upload-results.outputs.console_url }}/\",\"type\":\"branch\", \"reflike\":\"${{ needs.decide-refs.outputs.oe-core }}\",\"monorepo-reflike\":\"${{ needs.decide-refs.outputs.monorepo }}\",\"firmware-reflike\":\"${{ needs.decide-refs.outputs.ot3-firmware }}\",\"full-image\":\"${{ steps.upload-results.outputs.fullimage_url }}\", \"system-update\":\"${{ steps.upload-results.outputs.system_url }}\", \"version-file\":\"${{ steps.upload-results.outputs.version_file_url }}\", \"release-notes\":\"${{ steps.upload-results.outputs.release_notes_file_url }}\"}" | |
env: | |
SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }} | |
- name: Post results as release | |
if: ${{ matrix.build_env == 'stage-prod' && needs.decide-refs.outputs.variant== 'release' }} | |
uses: slackapi/[email protected] | |
with: | |
payload: "{\"s3-url\":\"${{ steps.upload-results.outputs.console_url }}/\",\"type\":\"branch\", \"reflike\":\"${{ needs.decide-refs.outputs.oe-core }}\",\"monorepo-reflike\":\"${{ needs.decide-refs.outputs.monorepo }}\",\"firmware-reflike\":\"${{ needs.decide-refs.outputs.ot3-firmware }}\",\"full-image\":\"${{ steps.upload-results.outputs.fullimage_url }}\", \"system-update\":\"${{ steps.upload-results.outputs.system_url }}\", \"version-file\":\"${{ steps.upload-results.outputs.version_file_url }}\", \"release-notes\":\"${{ steps.upload-results.outputs.release_notes_file_url }}\"}" | |
env: | |
SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL_RELEASE }} | |
- name: Remove build data | |
if: always() | |
run: | | |
rm -rf ./* | |
- name: Remove poisoned cache | |
if: ${{ failure() }} | |
run: | | |
rm -rf ${LOCAL_CACHE:-./cache}/* |