Skip to content

Commit 5a3b534

Browse files
committed
Document Flex firmware ex* coordination and add strategy pages.
Align go, validate-release-tags, and release guides with oe-core build-refs mapping (stack v* to firmware ex* plus integer vN), and publish coordinated tagging and beta-then-alpha sequencing docs with centered site nav.
1 parent bae9eb4 commit 5a3b534

14 files changed

Lines changed: 1184 additions & 105 deletions

.cursor/rules/robot-stack.mdc

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -78,23 +78,24 @@ External tags use a `v` prefix; internal tags use `internal@`.
7878
| **Flex external** | `chore_release-<version>` when present, else default branch | Highest `chore_release-X.Y.Z` on `opentrons`, fallback latest `v*` tag base |
7979
| **OT-2 internal / external** | Default branch HEAD (`opentrons-ot2`: `edge`, `buildroot`: `opentrons-develop`) | Calendar semver for app + internal (`YY.M.DNN` internal, `YY.M.N` external app); buildroot external patch bump from latest traditional `v*` tag |
8080

81-
Internal tags: `ot3@` / `v*` on **all three** Flex repos (`opentrons`, `oe-core`, `ot3-firmware`) for coordinated releases. OT-2 app and buildroot share `internal@` calendar tags. OT-2 external app uses calendar `vYY.M.N`; buildroot external uses independent traditional `vX.Y.Z`.
81+
Internal tags: coordinated `ot3@` / `v*` on `opentrons` and `oe-core`. `ot3-firmware` uses the same `ot3@*` internally; external stack `v*` maps to `ex*` on firmware plus integer `vN` on the same commit. OT-2 app and buildroot share `internal@` calendar tags. OT-2 external app uses calendar `vYY.M.N`; buildroot external uses independent traditional `vX.Y.Z`.
8282

8383
### Flex semver tagging
8484

85-
Flex uses **coordinated tags**: the same tag on `opentrons`, `oe-core`, and `ot3-firmware`. In `just go`, Flex prompts for **Stability: stable/alpha/beta** (legacy `unstable` maps to `alpha`).
85+
Flex uses **coordinated stack tags** on `opentrons` and `oe-core`. In `just go`, Flex prompts for **Stability: stable/alpha/beta** (legacy `unstable` maps to `alpha`).
8686

8787
| Repo | Internal | External |
8888
|---|---|---|
8989
| `opentrons` (app) | `ot3@X.Y.Z`, alpha `ot3@X.Y.Z-alpha.N`, beta `ot3@X.Y.Z-beta.N` | `vX.Y.Z`, alpha `vX.Y.Z-alpha.N`, beta `vX.Y.Z-beta.N` |
90-
| `oe-core` (robot OS) | same coordinated tag as app | same coordinated tag as app |
91-
| `ot3-firmware` | same coordinated tag as app | same coordinated tag as app |
90+
| `oe-core` (robot OS) | same stack tag as app | same stack tag as app |
91+
| `ot3-firmware` | same `ot3@*` as app + integer `vN` | `exX.Y.Z…` (from stack `v*`) + integer `vN` |
9292

9393
Tag suggestion rules in `automation/go.py`:
9494

9595
- **App (`opentrons`):** base `X.Y.Z` comes from the prompted release version. Stable: `ot3@X.Y.Z` / `vX.Y.Z` if missing, else patch bump. Alpha/beta: increment prerelease `.N` from branch tags.
96-
- **Robot OS (`oe-core`) and firmware (`ot3-firmware`):** same tag as the app. Retag unchanged commits when a repo did not change.
97-
- **Before pushing the app tag:** run `just validate-release-tags --tag <app-tag>` to confirm all three local clones have the tag. `oe-core` `build-refs` also requires the matching tag on each repo for tag dispatches (no `:latest:` fallback).
96+
- **Robot OS (`oe-core`):** same stack tag as the app. Retag unchanged commits when the repo did not change.
97+
- **Firmware (`ot3-firmware`):** coordination tag is `ot3@*` (internal) or `ex*` mapped from stack `v*` (external). Integer `vN` is globally unique in the firmware repo; suggest a new `vN` only when the release commit does not already have one. Retag-only releases need only the coordination tag.
98+
- **Before pushing the app tag:** run `just validate-release-tags --tag <app-tag>`. `oe-core` `build-refs` maps external stack tags to `ex*` on firmware ([PR #329](https://github.com/Opentrons/oe-core/pull/329)); missing tags fail (no `:latest:` fallback).
9899

99100
**Internal prerelease trains** (Flex only; OT-2 unchanged): on the same `X.Y.Z` base, **beta** (`ot3@X.Y.Z-beta.N`) serves the VM isolation train and **alpha** (`ot3@X.Y.Z-alpha.N`) serves CRS. When both channels need updates in one cycle, ship beta before alpha (beta desktop builds overwrite alpha updater YAML).
100101

@@ -260,14 +261,14 @@ just invalidate-cloudfront --non-interactive --path ot2 --tag internal@26.5.2601
260261

261262
### `just validate-release-tags` (`automation/validate_release_tags.py`)
262263

263-
Verifies a coordinated Flex release tag exists locally in `opentrons`, `oe-core`, and `ot3-firmware`. Read-only unless `--fetch` (runs `git fetch --tags` in each clone).
264+
Verifies coordinated Flex release tags locally. Checks stack tags on `opentrons` and `oe-core`, the mapped coordination tag on `ot3-firmware` (`ex*` for external stack `v*`, same `ot3@*` for internal), and an integer `vN` version tag on the same firmware commit. Read-only unless `--fetch` (runs `git fetch --tags` in each clone).
264265

265266
| Flag | Purpose |
266267
|---|---|
267-
| `--tag` | Release tag to verify (required; e.g. `ot3@8.5.0-beta.0`, `v10.0.0-alpha.0`) |
268+
| `--tag` | Stack release tag (required; e.g. `ot3@8.5.0-beta.0`, `v10.0.0-alpha.0`) |
268269
| `--fetch` | Fetch tags from origin before checking |
269270

270-
**Exit codes:** `0` if tag present in all three repos; `1` if any missing or fetch fails.
271+
**Exit codes:** `0` if all checks pass; `1` if any missing or fetch fails.
271272

272273
```bash
273274
just validate-release-tags --tag ot3@8.5.0-beta.1

README.md

Lines changed: 24 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ Prompt Cursor as above and it will walk you through the full release in order, r
1414

1515
1. **Plan tags** — runs `just go` to show what needs tags and the tag shape per repo.
1616
2. **Push tags** — prints `git tag` / `git push` commands for you to run (stack repos first, app last).
17-
3. **Validate coordinated tags (Flex)** — runs `just validate-release-tags` to confirm the same tag exists in `opentrons`, `oe-core`, and `ot3-firmware` before you push the app tag.
17+
3. **Validate coordinated tags (Flex)** — runs `just validate-release-tags` to confirm stack tags on `opentrons`/`oe-core`, the mapped `ex*`/`ot3@` coordination tag plus integer `vN` on `ot3-firmware`, before you push the app tag.
1818
4. **Track builds** — runs `just track-builds` after the app tag is pushed to surface app, kickoff, and robot OS workflow runs.
1919
5. **Verify builds** — reminds you to wait for CI and spot-check manifests if needed.
2020
6. **Invalidate CDN** — runs `just invalidate-cloudfront` to print the exact `aws cloudfront create-invalidation` command (distribution and paths) for your tag and channel.
@@ -79,15 +79,34 @@ Push annotated tags in this order. Stack repos first, app monorepo last.
7979

8080
### Flex semver (coordinated tags)
8181

82-
Flex releases use the **same tag** on `opentrons`, `oe-core`, and `ot3-firmware`. The tag identifies which commit participated in that release, even when a stack repo did not change. Tag-based CI in `oe-core` (`build-refs`) resolves only that exact tag on each repo; missing tags fail instead of falling back to latest or default branch.
82+
Flex releases use coordinated stack tags on `opentrons` and `oe-core`. `ot3-firmware` uses the same `ot3@*` tag internally; for external releases, semver `v*` stack tags map to `ex*` on firmware (see [oe-core PR #329](https://github.com/Opentrons/oe-core/pull/329)). Do not place semver `v*` coordination tags on `ot3-firmware`: they break cmake `git describe --match=v*`.
83+
84+
Every firmware release commit needs a coordination tag (`ot3@*` or `ex*`). Add a new integer **`vN` version tag** only when that commit does not already have one; `vN` must be globally unique across the firmware repo. CI checks out the coordination tag; cmake reads the co-located `vN`.
85+
86+
Tag-based CI in `oe-core` (`build-refs`) resolves only the expected tag on each repo; missing tags fail instead of falling back to latest or default branch.
8387

8488
In `just go`, Flex uses **stable**, **alpha**, or **beta** stability (legacy `unstable` maps to `alpha`). OT-2 tagging is unchanged.
8589

8690
| Repo | Internal | External |
8791
|---|---|---|
8892
| `opentrons` (app) | `ot3@X.Y.Z`, `ot3@X.Y.Z-alpha.N`, `ot3@X.Y.Z-beta.N` | `vX.Y.Z`, `vX.Y.Z-alpha.N`, `vX.Y.Z-beta.N` |
89-
| `oe-core` (robot OS) | same coordinated tag as app | same coordinated tag as app |
90-
| `ot3-firmware` | same coordinated tag as app | same coordinated tag as app |
93+
| `oe-core` (robot OS) | same stack tag as app | same stack tag as app |
94+
| `ot3-firmware` | same `ot3@*` tag as app + integer `vN` | `exX.Y.Z…` (from `vX.Y.Z…`) + integer `vN` |
95+
96+
**External firmware example** (stack `v9.1.0-alpha.7`):
97+
98+
```bash
99+
# ot3-firmware only:
100+
git tag -a v70 -m "Flex firmware v70"
101+
git tag -a ex9.1.0-alpha.7 -m "Coordinated release marker"
102+
git push origin v70 ex9.1.0-alpha.7
103+
104+
# opentrons / oe-core:
105+
git tag -a v9.1.0-alpha.7 -m "Coordinated release marker"
106+
git push origin v9.1.0-alpha.7
107+
```
108+
109+
**Internal firmware example** (stack `ot3@4.0.0-beta.0`): same `ot3@*` on all three repos plus `vN` on firmware.
91110

92111
**Internal prerelease trains** (same `X.Y.Z` base, different stability suffix):
93112

@@ -100,7 +119,7 @@ Pair beta then alpha when both channels need updates in the same cycle: beta des
100119

101120
Before pushing the app tag, run `just validate-release-tags --tag <app-tag>`. `go` prints this in the Next steps panel.
102121

103-
**Example (June 2026):** recent internal alphas used base `4.0.0` (`ot3@4.0.0-alpha.3` is the latest). The first coordinated internal release on that line is **`ot3@4.0.0-beta.0`** on all three repos.
122+
**Example (June 2026):** recent internal alphas used base `4.0.0` (`ot3@4.0.0-alpha.3` is the latest). The first coordinated internal release on that line is **`ot3@4.0.0-beta.0`** on opentrons/oe-core, plus **`ot3@4.0.0-beta.0`** and the next **`vN`** on firmware.
104123

105124
```bash
106125
just go --non-interactive --skip-assumptions --path flex --release-type internal --stability beta --version v4.0.0
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
"""Flex coordinated release tag naming shared by go.py and validate_release_tags.py."""
2+
3+
from __future__ import annotations
4+
5+
import re
6+
from typing import Final, Optional
7+
8+
FIRMWARE_VERSION_TAG_RE: Final[re.Pattern[str]] = re.compile(r"^v\d+$")
9+
EXTERNAL_STACK_TAG_RE: Final[re.Pattern[str]] = re.compile(r"^v\d+\.\d+\.\d+(?:-(?:alpha|beta)\.\d+)?$")
10+
11+
12+
def normalize_tag(tag: str) -> str:
13+
"""Strip refs/tags/ prefix so git commands receive a plain tag name."""
14+
if tag.startswith("refs/tags/"):
15+
return tag[len("refs/tags/") :]
16+
return tag
17+
18+
19+
def is_firmware_version_tag(tag: str) -> bool:
20+
"""Return True for integer-only firmware version tags such as v70."""
21+
return bool(FIRMWARE_VERSION_TAG_RE.match(normalize_tag(tag)))
22+
23+
24+
def is_external_stack_coordination_tag(tag: str) -> bool:
25+
"""Return True for external semver coordination tags (vX.Y.Z, not integer vN)."""
26+
clean = normalize_tag(tag)
27+
return bool(clean.startswith("v") and EXTERNAL_STACK_TAG_RE.match(clean))
28+
29+
30+
def stack_coordinated_tag_to_firmware_tag(stack_tag: str) -> Optional[str]:
31+
"""Map an external stack tag to the ot3-firmware coordination tag (ex* prefix).
32+
33+
External ``v9.1.0-alpha.7`` becomes ``ex9.1.0-alpha.7``. Internal ``ot3@*`` and
34+
integer ``vN`` version tags are not mapped (returns None).
35+
"""
36+
clean = normalize_tag(stack_tag)
37+
if clean.startswith("ex"):
38+
return None
39+
if clean.startswith("v") and not is_firmware_version_tag(clean):
40+
return f"ex{clean[1:]}"
41+
return None
42+
43+
44+
def coordinated_tag_for_repo(repo_name: str, stack_tag: str) -> str:
45+
"""Return the coordination tag expected in a given Flex repo for a stack dispatch."""
46+
clean = normalize_tag(stack_tag)
47+
if repo_name == "ot3-firmware":
48+
return stack_coordinated_tag_to_firmware_tag(clean) or clean
49+
return clean

0 commit comments

Comments
 (0)