Flex external releases
Customer-facing Flex releases. App tags use a v prefix in
opentrons.
oe-core uses the same stack tag.
ot3-firmware uses an ex*
coordination tag mapped from the stack v* tag, plus an integer vN
version tag on the same commit.
Stack repos
| Repo | Role | External tag pattern |
|---|---|---|
opentrons | App (taggable) | vX.Y.Z, alpha vX.Y.Z-alpha.N, beta vX.Y.Z-beta.N |
oe-core | Flex robot OS | Same stack tag as app (e.g. v10.0.0-beta.0) |
ot3-firmware | Flex firmware | exX.Y.Z… coordination tag + integer vN on same commit |
Robot-stack tooling
just go, just track-builds, and just invalidate-cloudfront
are advisory: they sync local clones under this workspace, print tables and analysis,
and emit copy-paste commands. A human (or agent) runs git tag -a, git push,
and aws cloudfront create-invalidation elsewhere. Nothing here pushes tags, triggers CI,
or invalidates CloudFront by itself.
robot-stack-infra is always cloned for reference; it is not included in release
tagging tables.
Plan a release non-interactively, for example:
just go --non-interactive --skip-assumptions --path flex --release-type external --stability stable
Release branch
Flex external defaults to isolation branches
chore_release-<version> (for example chore_release-9.1.0, without a
v prefix in the branch name) when that branch exists on the remote after
just go syncs repos. Otherwise go uses each repo's default branch.
Override with --app-branch and --stack-branch REPO=BRANCH when a flavor
ships from a different branch (for example chore_release-10.0.0-beta).
Suggested tag commands include git checkout <release-branch> before
git tag -a when that branch is not the repo default.
| Repo | Default branch |
|---|---|
opentrons | edge |
oe-core | main |
ot3-firmware | main |
Default version inference: highest chore_release-X.Y.Z on opentrons,
with fallback to the latest merged v* tag base.
When does just go say a new tag is needed?
For each repo, automation/go.py uses the release branch described
above and the coordinated stack tag for the selected stability lane. It compares branch tip commit
to whether that coordinated tag already points at HEAD.
New tag needed when the coordinated tag for this release is missing on the branch or branch HEAD has moved since that tag.
No new tag needed when branch HEAD already matches the coordinated tag.
Flex app tag numbers come from the app monorepo tag catalog
(opentrons or opentrons-ot2). At one semver base, stable, alpha, and beta
are independent lanes (for example ot3@4.0.0-alpha.3 and
ot3@4.0.0-beta.0 can coexist). Change logs compare against the prior tag in the
same lane on the release branch.
Tags must be annotated (git tag -a … -m 'chore(release): …').
When the release branch differs from the repo default, tag blocks print
git checkout <release-branch> first.
Pushing a tag triggers CI builds in the tagged repo. The app monorepo tag drives app artifacts; stack repo tags drive robot OS and firmware builds.
How the next tag is chosen
App (opentrons)
- Stable: tag is exactly the base version, e.g.
v9.1.0, if that tag does not already exist on the branch. - Alpha: find tags matching
v9.1.0-alpha.*on the branch and incrementN(first alpha isv9.1.0-alpha.0). - Beta: increment
v9.1.0-beta.N(VM isolation train).
Robot OS (oe-core)
Uses the same stack tag as the app (for example v9.1.0-alpha.7).
Firmware (ot3-firmware)
Dual-tag when the release commit does not already have an integer vN version tag.
CI checks out the ex* coordination tag; cmake reads the co-located vN.
Do not use semver v* as the firmware coordination tag.
git tag -a v70 -m "Flex firmware v70"
git tag -a ex9.1.0-alpha.7 -m "Coordinated release marker"
git push origin v70 ex9.1.0-alpha.7
Before pushing the app tag, run
just validate-release-tags --tag <app-tag>.
Tag push order
Push annotated tags in this order. Dependent stack repos first, app monorepo last.
just go prints this reminder at the end of a release run.
| Step | Repo | Notes |
|---|---|---|
| 1 | ot3-firmware | Firmware, if a new tag is needed |
| 2 | oe-core | Robot OS, if a new tag is needed |
| 3 | opentrons | App monorepo, always last |
Flex stack repos only get a new tag when their release branch tip is ahead of the latest channel tag on that branch.
Track release builds
After pushing the app tag, run (non-interactive form):
just track-builds --non-interactive --path flex --tag v9.1.0 --wait
automation/track_builds.py locates GitHub Actions runs for:
- App workflow on the monorepo (
App test, build, and deploy) - Kickoff cross-repo dispatch (
Start Flex build) - Robot OS build in
oe-core
The Rich table lists key jobs (deploy, desktop builds, dispatch spawn, robot image build). The Slack copy block includes only two links:
Flex release `v9.1.0` - app: <app workflow run URL> - flex: <robot OS workflow run URL>
--wait polls every 15 seconds until app, kickoff, and robot OS
workflow runs all appear (default timeout 900 seconds). Polling checks workflow runs only; job
details are fetched afterward with retries for transient GitHub 404s. Exit code 2 if
a run is still missing after the timeout.
CloudFront invalidation
CI does not invalidate CloudFront automatically. After builds finish, print a copy-paste command (it does not run invalidation):
just invalidate-cloudfront --non-interactive --path flex --tag v9.1.0
Invalidates /app/* and /ot3-oe/* on the channel host.
Uses AWS profile robotics_robot_stack_prod-admin when credentials allow distribution
lookup; otherwise the script prints a lookup command and placeholder ID.
Validate published artifacts
Optionally regenerate live manifest inventories:
just assets-pages
Or per-platform reports: just flex-assets / just ot2-assets.
Pages: external assets,
internal assets.
Where to find published releases
External Flex artifacts live on builds.opentrons.com.
| Artifact | URL |
|---|---|
App releases.json |
https://builds.opentrons.com/app/releases.json |
Robot releases.json (source of truth) |
https://builds.opentrons.com/ot3-oe/releases.json |
Robot releases.json is the source of truth for on-robot updates.
Desktop app updates use channel YAMLs (latest.yml, prerelease YAMLs) via
electron-updater; those YAMLs are authoritative.
App releases.json is parsed by a CloudFront edge function to pick the latest stable
semver from production and route latest* requests accordingly.
Electron-updater channel YAMLs:
alpha.ymlalpha-mac.ymlalpha-linux.ymlbeta.ymlbeta-mac.ymlbeta-linux.ymllatest.ymllatest-mac.ymllatest-linux.yml
See also the live inventory: Flex external assets.
Alpha and beta on Flex external
In just go, choose Release type: external and
Stability: alpha or beta for prerelease QA builds.
| Stability | App tag example | Typical app YAML |
|---|---|---|
| Stable | v9.1.0 | latest.yml (+ mac/linux) |
| Alpha | v9.1.0-alpha.0, v9.1.0-alpha.1, … | alpha.yml (+ mac/linux) |
| Beta | v9.1.0-beta.0, v9.1.0-beta.1, … | beta.yml (+ mac/linux) |
Alpha and beta are independent lanes at the same semver base; each
increments its own .N from the app repo tag catalog. Compare ranges use the
prior tag in the same lane merged into the release branch. Stable external releases drop the
prerelease segment entirely.
oe-core uses the same stack tag as the app.
ot3-firmware uses ex* mapped from the stack tag (for example
v9.1.0-beta.0 → ex9.1.0-beta.0) plus integer vN.
Validate with just validate-release-tags --tag <app-tag> before pushing
the app tag. See coordinated tagging and
release channel hierarchy.