### Problem Statement --- ### Summary When the SDK's `options.release` doesn't match the release name the server derives from an uploaded artifact, `sentry-cli build upload` leaves us with two Releases for the same build: one holds every event, session and commit, the other holds only the build / size analysis. Nothing links them, this creates a broken window to analyze releases in our mobile app o11y. ### Setup - Flutter app, Android and iOS in a **single** Sentry project - `sentry_flutter` 9.26.0; `sentry-cli` 3.6.2 in CI (`matbour/setup-sentry-cli@v1`, `version: latest`) - CD, per platform: `sentry-cli releases new` → `releases set-commits --commit <repo>@<prev>..<cur>` → `sentry-cli build upload <aab|xcarchive>` - Store promotion, later: `releases finalize` → `releases deploys new -e production` ### Why our release name differs from the bundle id Both platforms share one project, and Release Health doesn't group by `os` or `platform`, so the platform has to be part of the release string for per-platform crash-free rate to be readable. Our production bundle ids do differ per platform (e.g.: `com.mobile` on Android, `com.app` on iOS), so the information was technically there — but a Releases list where the only distinguishing token is a bundle id is hard to scan. So we set `options.release` to `<os>@<version>+<build>`, e.g. `android@1.1+930`. ### What we observe For a single Android build (1.1, build 930), two releases created ~25 seconds apart: | release | created | first / last event | commits | deploy | |---|---|---|---|---| | `android@1.1.1+930` | 19:25:52Z | 19:27:22Z / 20:51:10Z | 3 | production | | `com.mobile@1.1+930` | 19:26:17Z | none / none | 0 | — | The second is created by the `build upload` step and never receives a single event. In the Releases list it shows 0% adoption, no crash-free rate, and a "Finalize" button. iOS produces the same pair (`ios@…` plus `br.com.zippi.app@…`). So every build yields one real release plus one dead duplicate, per platform. ### Solution Brainstorm ### The key to join them is already being sent `build upload` sends `vcs_info` in the `assemble_build` request, and it is persisted on the build record (`GET /organizations/{org}/builds/`): ```json "vcs_info": { "head_sha": "<commit_sha>", "provider": "github", "head_repo_name": "<org>/<repo>", "head_ref": "main" } ``` And that same sha is one of the three commits associated with `android@1.1+930` (`GET /organizations/{org}/releases/android@1.1+930/commits/`). Both sides already carry what's needed to associate the build with the release the events actually land on. Two ways to close it, in our order of preference: 1. **Add `--release` to `sentry-cli build upload`** — let the caller state the release name explicitly, carried through `ChunkedBuildRequest`. Today the command exposes `--build-configuration`, `--release-notes`, `--install-group` and `--dsym`, and nothing that influences the release the server derives. 2. **Join server-side on `vcs_info.head_sha`** — associate an uploaded build with the release (same project) whose commit range contains that sha. Needs no new input from the client: the data already flows and is already stored on both records. Either one would be enough for us. ### Product Area Releases