Skip to content

linting, feedback #47971

linting, feedback

linting, feedback #47971

# Run tests, build the app, and deploy it cross platform
name: 'App test, build, and deploy'
on:
push:
paths:
- 'Makefile'
- 'app/**'
- 'app-shell/**'
- 'app-shell-odd/**'
- 'components/**'
- 'shared-data/**'
- 'discovery-client/**'
- '*.js'
- 'scripts/**'
- '*.json'
- 'yarn.lock'
- '.github/workflows/app-test-build-deploy.yaml'
- '.github/workflows/utils.js'
branches:
- '**'
tags:
- 'v*'
- 'ot3@*'
pull_request:
paths:
- 'Makefile'
- 'app/**'
- 'app-shell/**'
- 'app-shell-odd/**'
- 'components/**'
- 'shared-data/**'
- 'discovery-client/**'
- '*.js'
- '*.json'
- 'yarn.lock'
- 'scripts/**'
workflow_dispatch: {}
concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}-${{ github.ref_name != 'edge' || github.run_id}}-${{ github.ref_type != 'tag' || github.run_id }}
cancel-in-progress: true
env:
CI: "true"
_APP_DEPLOY_BUCKET_ROBOTSTACK: builds.opentrons.com
_APP_DEPLOY_FOLDER_ROBOTSTACK: app
_APP_DEPLOY_BUCKET_OT3: ot3-development.builds.opentrons.com
_APP_DEPLOY_FOLDER_OT3: app
jobs:
js-unit-test:
# unit tests for the app's view layer (not the node layer)
runs-on: 'ubuntu-24.04'
name: 'opentrons app frontend unit tests'
timeout-minutes: 60
steps:
- uses: 'actions/checkout@v4'
- uses: ./.github/actions/js/setup
- name: 'test frontend packages'
run: |
make -C app test-cov
- name: 'Upload coverage report'
uses: codecov/codecov-action@v3
with:
files: ./coverage/lcov.info
flags: app
backend-unit-test:
strategy:
matrix:
os: ['windows-2022', 'ubuntu-22.04', 'macos-latest']
shell: ['app-shell', 'app-shell-odd', 'discovery-client']
exclude:
- os: 'windows-2022'
shell: 'app-shell-odd'
name: 'opentrons ${{matrix.shell}} unit tests on ${{matrix.os}}'
timeout-minutes: 60
runs-on: ${{ matrix.os }}
steps:
- uses: 'actions/checkout@v4'
with:
fetch-depth: 0
- uses: ./.github/actions/js/setup
- uses: actions/setup-python@v4
with:
python-version: '3.10'
- name: check make version
run: make --version
- name: 'test native(er) packages'
run: make test-js-internal tests="${{matrix.shell}}/src" cov_opts="--coverage=true"
- name: 'Upload coverage report'
uses: 'codecov/codecov-action@v3'
with:
files: ./coverage/lcov.info
flags: app
determine-build-type:
runs-on: 'ubuntu-latest'
name: 'Determine build variant and type'
outputs:
variants: ${{steps.determine-build-type.outputs.variants}}
type: ${{steps.determine-build-type.outputs.type}}
steps:
- id: determine-build-type
run: |
echo "Determining build type for event ${{github.event_type}} and ref ${{github.ref}}"
if [ "${{ format('{0}', github.event_name == 'pull_request') }}" = "true" ] ; then
echo "No builds for pull requests"
echo 'variants=[]' >> $GITHUB_OUTPUT
echo 'type=develop' >> $GITHUB_OUTPUT
elif [ "${{ format('{0}', startsWith(github.ref, 'refs/tags/ot3')) }}" = "true" ] ; then
echo "internal-release release builds for ot3 tags"
echo 'variants=["internal-release"]' >> $GITHUB_OUTPUT
echo 'type=release' >> $GITHUB_OUTPUT
elif [ "${{ format('{0}', startsWith(github.ref, 'refs/tags/v')) }}" = "true" ] ; then
echo "release release builds for v tags"
echo 'variants=["release"]' >> $GITHUB_OUTPUT
echo 'type=release' >> $GITHUB_OUTPUT
elif [ "${{ format('{0}', startsWith(github.ref, 'refs/heads/internal-release')) }}" = "true" ] ; then
echo "internal-release develop builds for internal-release branches"
echo 'variants=["internal-release"]' >> $GITHUB_OUTPUT
echo 'type=develop' >> $GITHUB_OUTPUT
elif [ "${{ format('{0}', startsWith(github.ref, 'refs/heads/release') || startsWith(github.ref, 'refs/heads/chore_release')) }}" = "true" ] ; then
echo "Release develop builds for release branches"
echo 'variants=["release"]' >> $GITHUB_OUTPUT
echo 'type=develop' >> $GITHUB_OUTPUT
elif [ "${{ format('{0}', github.ref == 'refs/heads/edge') }}" = "true" ] ; then
echo "both develop builds for edge"
echo 'variants=["release", "internal-release"]' >> $GITHUB_OUTPUT
echo 'type=develop' >> $GITHUB_OUTPUT
elif [ "${{ format('{0}', contains(github.ref, 'app-build-internal')) }}" = "true" ] ; then
echo 'variants=["internal-release"]' >> $GITHUB_OUTPUT
if [ "${{ format('{0}', contains(github.ref, 'as-release')) }}" = "true" ] ; then
echo "internal-release as-release builds for app-build-internal + as-release suffixes"
echo 'type=as-release' >> $GITHUB_OUTPUT
else
echo "internal-release develop builds for app-build-internal suffixes"
echo 'type=develop' >> $GITHUB_OUTPUT
fi
elif [ "${{ format('{0}', contains(github.ref, 'app-build')) }}" = "true" ] ; then
echo 'variants=["release"]' >> $GITHUB_OUTPUT
if [ "${{ format('{0}', contains(github.ref, 'as-release')) }}" = "true" ] ; then
echo "release as-release builds for app-build + as-release suffixes"
echo 'type=as-release' >> $GITHUB_OUTPUT
else
echo "release develop builds for app-build suffixes"
echo 'type=develop' >> $GITHUB_OUTPUT
fi
elif [ "${{ format('{0}', contains(github.ref, 'app-build-both')) }}" = "true" ] ; then
echo 'variants=["release", "internal-release"]' >> $GITHUB_OUTPUT
if [ "${{ format('{0}', contains(github.ref, 'as-release')) }}" = "true" ] ; then
echo "Both as-release builds for app-build-both + as-release suffixes"
echo 'type=as-release' >> $GITHUB_OUTPUT
else
echo "Both develop builds for app-build-both + as-release suffixes"
echo 'type=develop' >> $GITHUB_OUTPUT
fi
else
echo "No build for ref ${{github.ref}} and event ${{github.event_type}}"
echo 'variants=[]' >> $GITHUB_OUTPUT
echo 'type=develop' >> $GITHUB_OUTPUT
fi
- name: set summary
run: |
echo 'Type: ${{steps.determine-build-type.outputs.type}} Variants: ${{steps.determine-build-type.outputs.variants}}' >> $GITHUB_STEP_SUMMARY
build-app:
needs: [determine-build-type]
if: needs.determine-build-type.outputs.variants != '[]'
strategy:
fail-fast: false
matrix:
os: ['windows-2022', 'ubuntu-22.04', 'macos-latest']
variant: ${{fromJSON(needs.determine-build-type.outputs.variants)}}
target: ['desktop', 'odd']
exclude:
- os: 'windows-2022'
target: 'odd'
- os: 'macos-latest'
target: 'odd'
runs-on: ${{ matrix.os }}
name: 'Build ${{matrix.variant}} ${{matrix.target}} app on ${{matrix.os}}'
steps:
- name: 'Get project name for variant'
id: project
shell: bash
run: |
if [ "${{matrix.variant}}" = "release" ] ; then
echo "Configuring project, bucket, and folder for robot-stack"
echo "project=robot-stack" >> $GITHUB_OUTPUT
echo "bucket=${{env._APP_DEPLOY_BUCKET_ROBOTSTACK}}" >> $GITHUB_OUTPUT
echo "folder=${{env._APP_DEPLOY_FOLDER_ROBOTSTACK}}" >> $GITHUB_OUTPUT
else
echo "Configuring project, bucket, and folder for ot3"
echo "project=ot3" >> $GITHUB_OUTPUT
echo "bucket=${{env._APP_DEPLOY_BUCKET_OT3}}" >> $GITHUB_OUTPUT
echo "folder=${{env._APP_DEPLOY_FOLDER_OT3}}" >> $GITHUB_OUTPUT
fi
- uses: 'actions/checkout@v4'
with:
fetch-depth: 0
- uses: ./.github/actions/js/setup
- uses: actions/setup-python@v4
with:
python-version: '3.10'
- name: check make version
run: make --version
# Do the frontend dist bundle
- name: 'bundle ${{matrix.variant}} frontend'
env:
OT_APP_MIXPANEL_ID: ${{ secrets.OT_APP_MIXPANEL_ID }}
OT_APP_INTERCOM_ID: ${{ secrets.OT_APP_INTERCOM_ID }}
OPENTRONS_PROJECT: ${{ steps.project.outputs.project }}
run: |
make -C app dist
- name: Install and Invoke TrustedSigning @0.5.3 on dummy executable
# Work around from https://github.com/electron-userland/electron-builder/issues/9076#issuecomment-2855541018
if: startsWith(matrix.os, 'windows') && contains(needs.determine-build-type.outputs.type, 'release')
shell: pwsh
run: |
Install-Module -Name TrustedSigning -RequiredVersion 0.5.3 -Force -Repository PSGallery
# Create a dummy executable file
$dummyExePath = Join-Path -Path $env:GITHUB_WORKSPACE -ChildPath "dummy.exe"
Set-Content -Path $dummyExePath -Value "This is a dummy executable for testing purposes."
# Invoke Trusted Signing on the dummy executable
# Necessary to force dependency resolution
try {
Invoke-TrustedSigning `
-Endpoint 'https://eus.codesigning.azure.net/' `
-CertificateProfileName 'dummyName' `
-CodeSigningAccountName 'dummyName' `
-TimestampRfc3161 'http://timestamp.acs.microsoft.com' `
-TimestampDigest 'SHA256' `
-FileDigest 'SHA256' `
-Files $dummyExePath
} catch {
Write-Host "Invoke-TrustedSigning failed: $($_.Exception.Message)"
# Prevent the script from exiting with a non-zero status
exit 0
}
# build the desktop app and deploy it
- name: 'build ${{matrix.variant}} app for ${{ matrix.os }}'
if: matrix.target == 'desktop'
timeout-minutes: 60
env:
OT_APP_MIXPANEL_ID: ${{ secrets.OT_APP_MIXPANEL_ID }}
OT_APP_INTERCOM_ID: ${{ secrets.OT_APP_INTERCOM_ID }}
WINDOWS_SIGN: ${{ format('{0}', contains(needs.determine-build-type.outputs.type, 'release')) }}
AZURE_TENANT_ID: ${{secrets.AZURE_TENANT_ID}}
AZURE_CLIENT_ID: ${{secrets.AZURE_CLIENT_ID}}
AZURE_CLIENT_SECRET: ${{secrets.AZURE_CLIENT_SECRET}}
CSC_LINK: ${{ secrets.OT_APP_CSC_MACOS_V2 }}
CSC_KEY_PASSWORD: ${{ secrets.OT_APP_CSC_KEY_MACOS_V2 }}
APPLE_ID: ${{ secrets.OT_APP_APPLE_ID_V2 }}
APPLE_APP_SPECIFIC_PASSWORD: ${{ secrets.OT_APP_APPLE_ID_PASSWORD_V2 }}
APPLE_TEAM_ID: ${{ secrets.OT_APP_APPLE_TEAM_ID_V2 }}
HOST_PYTHON: python
OPENTRONS_PROJECT: ${{ steps.project.outputs.project }}
OT_APP_DEPLOY_BUCKET: ${{ steps.project.outputs.bucket }}
OT_APP_DEPLOY_FOLDER: ${{ steps.project.outputs.folder }}
run: |
make -C app-shell dist-${{ matrix.os }} USE_HARD_LINKS=false
- name: 'upload github artifact'
if: matrix.target == 'desktop'
uses: actions/upload-artifact@v4
with:
name: 'opentrons-${{matrix.variant}}-${{ matrix.os }}'
path: app-shell/dist/publish
# build the ODD app
- if: matrix.target == 'odd'
name: 'build ${{matrix.variant}} ODD app'
timeout-minutes: 60
env:
OPENTRONS_PROJECT: ${{ steps.project.outputs.project }}
run: |
make -C app-shell-odd dist-ot3
deploy-release-app:
name: 'Deploy built app artifacts to S3'
runs-on: 'ubuntu-24.04'
needs:
['js-unit-test', 'backend-unit-test', 'build-app', 'determine-build-type']
if: contains(fromJSON(needs.determine-build-type.outputs.variants), 'release') || contains(fromJSON(needs.determine-build-type.outputs.variants), 'internal-release')
steps:
- name: 'download run app builds'
uses: 'actions/download-artifact@v4'
with:
path: ./artifacts
- name: 'separate release and internal-release artifacts'
run: |
for variant in release internal-release ; do
mkdir to_upload_${variant}
variant_pattern="./artifacts/opentrons-${variant}-*"
ls ${variant_pattern}/ 2>/dev/null 1>/dev/null || continue
echo "Moving ${variant} builds ${variant_pattern}/* to to_upload_${variant}"
cp ${variant_pattern}/* ./to_upload_${variant}/
echo "Moved $(ls ./to_upload_${variant})"
done
- name: 'configure s3 deploy creds'
run: |
aws configure set aws_access_key_id ${{ secrets.S3_OT3_APP_DEPLOY_KEY_ID }} --profile identity
aws configure set aws_secret_access_key ${{ secrets.S3_OT3_APP_DEPLOY_SECRET }} --profile identity
aws configure set region us-east-2 --profile identity
aws configure set output json --profile identity
aws configure set region us-east-2 --profile deploy
aws configure set role_arn ${{ secrets.OT_APP_OT3_DEPLOY_ROLE }} --profile deploy
aws configure set source_profile identity --profile deploy
shell: bash
- name: 'deploy release builds to s3'
run: |
aws --profile=deploy s3 sync --acl=public-read to_upload_release/ s3://${{ env._APP_DEPLOY_BUCKET_ROBOTSTACK }}/${{ env._APP_DEPLOY_FOLDER_ROBOTSTACK }}
- name: 'deploy internal-release release builds to s3'
run: |
aws s3 --profile=deploy sync --acl=public-read to_upload_internal-release/ s3://${{ env._APP_DEPLOY_BUCKET_OT3 }}/${{ env._APP_DEPLOY_FOLDER_OT3 }}
- name: 'upload windows artifacts to GH release'
uses: 'ncipollo/release-action@v1.12.0'
if: needs.determine-build-type.outputs.type == 'release'
with:
allowUpdates: true
omitBodyDuringUpdate: true
omitDraftDuringUpdate: true
omitNameDuringUpdate: true
omitPrereleaseDuringUpdate: true
artifacts: ./artifacts/*/*.exe
artifactContentType: application/vnd.microsoft.portable-executable
- name: 'upload macos artifacts to GH release'
uses: 'ncipollo/release-action@v1.12.0'
if: needs.determine-build-type.outputs.type == 'release'
with:
allowUpdates: true
omitBodyDuringUpdate: true
omitDraftDuringUpdate: true
omitNameDuringUpdate: true
omitPrereleaseDuringUpdate: true
artifacts: ./artifacts/*/*.dmg
artifactContentType: application/octet-stream
- name: 'upload linux artifacts to GH release'
uses: 'ncipollo/release-action@v1.12.0'
if: needs.determine-build-type.outputs.type == 'release'
with:
allowUpdates: true
omitBodyDuringUpdate: true
omitDraftDuringUpdate: true
omitNameDuringUpdate: true
omitPrereleaseDuringUpdate: true
artifacts: ./artifacts/*/*.AppImage
artifactContentType: application/octet-stream
- name: 'detect build data for notification'
id: names
shell: bash
run: |
for variant in release internal-release ; do
dir=./to_upload_${variant}
echo "Checking for ${variant} builds in ${dir}"
ls ${dir}/Opentrons* || continue
_windows_build=$(basename $(ls ./to_upload_${variant}/Opentrons*.exe))
_mac_build=$(basename $(ls ./to_upload_${variant}/Opentrons*.dmg))
_linux_build=$(basename $(ls ./to_upload_${variant}/Opentrons*.AppImage))
echo "windows-${variant}=$_windows_build">>$GITHUB_OUTPUT
echo "mac-${variant}=$_mac_build">>$GITHUB_OUTPUT
echo "linux-${variant}=$_linux_build">>$GITHUB_OUTPUT
done
- name: 'slack notify internal-release'
uses: slackapi/slack-github-action@v1.14.0
if: contains(fromJSON(needs.determine-build-type.outputs.variants), 'internal-release')
with:
payload: '{"branch_or_tag":"${{ github.ref_name }}","build_type":"${{ needs.determine-build-type.outputs.type }}", "gh_linkback":"https://github.com/Opentrons/opentrons/tree/${{ github.ref_name }}", "windows_build":"${{ env._ACCESS_URL }}/${{steps.names.outputs.windows-internal-release}}", "mac_build":"${{ env._ACCESS_URL }}/${{steps.names.outputs.mac-internal-release}}", "linux_build":"${{ env._ACCESS_URL }}/${{steps.names.outputs.linux-internal-release}}"}'
env:
SLACK_WEBHOOK_URL: ${{ secrets.OT_APP_OT3_SLACK_NOTIFICATION_WEBHOOK_URL }}
_ACCESS_URL: https://${{env._APP_DEPLOY_BUCKET_OT3}}/${{env._APP_DEPLOY_FOLDER_OT3}}
- name: 'slack notify release'
uses: slackapi/slack-github-action@v1.14.0
if: contains(fromJSON(needs.determine-build-type.outputs.variants), 'release')
with:
payload: '{"branch_or_tag":"${{ github.ref_name }}","build_type":"${{ needs.determine-build-type.outputs.type }}", "gh_linkback":"https://github.com/Opentrons/opentrons/tree/${{ github.ref_name }}", "windows_build":"${{ env._ACCESS_URL }}/${{steps.names.outputs.windows-release}}", "mac_build":"${{ env._ACCESS_URL }}/${{steps.names.outputs.mac-release}}", "linux_build":"${{ env._ACCESS_URL }}/${{steps.names.outputs.linux-release}}"}'
env:
SLACK_WEBHOOK_URL: ${{ secrets.OT_APP_ROBOTSTACK_SLACK_NOTIFICATION_WEBHOOK_URL }}
_ACCESS_URL: https://${{env._APP_DEPLOY_BUCKET_ROBOTSTACK}}/${{env._APP_DEPLOY_FOLDER_ROBOTSTACK}}
- name: 'pull repo for scripts'
uses: 'actions/checkout@v4'
- uses: ./.github/actions/js/setup
- name: 'update internal-releases releases.json'
if: needs.determine-build-type.outputs.type == 'release' && contains(fromJSON(needs.determine-build-type.outputs.variants), 'internal-release')
run: |
aws --profile=deploy s3 cp s3://${{ env._APP_DEPLOY_BUCKET_OT3 }}/${{ env._APP_DEPLOY_FOLDER_OT3 }}/releases.json ./to_upload_internal-release/releases.json
node ./scripts/update-releases-json ./to_upload_internal-release/releases.json ot3 ./to_upload_internal-release https://ot3-development.builds.opentrons.com/app/
aws --profile=deploy s3 cp ./to_upload_internal-release/releases.json s3://${{ env._APP_DEPLOY_BUCKET_OT3 }}/${{ env._APP_DEPLOY_FOLDER_OT3 }}/releases.json
- name: 'update release releases.json'
if: needs.determine-build-type.outputs.type == 'release' && contains(fromJSON(needs.determine-build-type.outputs.variants), 'release')
run: |
aws --profile=deploy s3 cp s3://${{ env._APP_DEPLOY_BUCKET_ROBOTSTACK }}/${{ env._APP_DEPLOY_FOLDER_ROBOTSTACK }}/releases.json ./to_upload_release/releases.json
node ./scripts/update-releases-json ./to_upload_release/releases.json robot-stack ./to_upload_release https://builds.opentrons.com/app/
aws --profile=deploy s3 cp ./to_upload_release/releases.json s3://${{ env._APP_DEPLOY_BUCKET_ROBOTSTACK }}/${{ env._APP_DEPLOY_FOLDER_ROBOTSTACK }}/releases.json