mirror of
https://github.com/block/buzz.git
synced 2026-10-27 04:30:52 +00:00
chore(mobile): lighter-weight release process (#2144)
Signed-off-by: npub1sv749mw8zcmld4ygjx2mx2aqcn3zvtuj2nlmgatxgad3t9uweu9q5marze <833d52edc71637f6d4889195b32ba0c4e2262f9254ffb47566475b15978ecf0a@sprout-oss.stage.blox.sqprod.co> Signed-off-by: npub1ux8n2yfs8qfvgd75s7kyhar2mztac355v6vmrz4juc9l3msw4pgstums9e <e18f3511303812c437d487ac4bf46ad897dc46946699b18ab2e60bf8ee0ea851@sprout-oss.stage.blox.sqprod.co> Signed-off-by: npub1re830n24qhgstulznk5dxdluccspxkxxdq643lm4q3zwwwjgsuqqmcmdrm <1e4f17cd5505d105f3e29da8d337fcc6201358c6683558ff750444e73a488700@buzz.block.builderlab.xyz> Signed-off-by: npub102wg7q285p64ch2fjvstmf2ntn2sz3c4u5hmwatalc76mhsuauysftjtfj <7a9c8f0147a0755c5d499320bda5535cd5014715e52fb7757dfe3dadde1cef09@buzz.block.builderlab.xyz> Co-authored-by: npub1sv749mw8zcmld4ygjx2mx2aqcn3zvtuj2nlmgatxgad3t9uweu9q5marze <833d52edc71637f6d4889195b32ba0c4e2262f9254ffb47566475b15978ecf0a@sprout-oss.stage.blox.sqprod.co> Co-authored-by: npub1ux8n2yfs8qfvgd75s7kyhar2mztac355v6vmrz4juc9l3msw4pgstums9e <e18f3511303812c437d487ac4bf46ad897dc46946699b18ab2e60bf8ee0ea851@sprout-oss.stage.blox.sqprod.co> Co-authored-by: npub1re830n24qhgstulznk5dxdluccspxkxxdq643lm4q3zwwwjgsuqqmcmdrm <1e4f17cd5505d105f3e29da8d337fcc6201358c6683558ff750444e73a488700@buzz.block.builderlab.xyz> Co-authored-by: 7a9c8f0147a0755c5d499320bda5535cd5014715e52fb7757dfe3dadde1cef09@buzz.block.builderlab.xyz <7a9c8f0147a0755c5d499320bda5535cd5014715e52fb7757dfe3dadde1cef09@buzz.block.builderlab.xyz>
This commit is contained in:
269
RELEASING.md
269
RELEASING.md
@@ -1,25 +1,18 @@
|
||||
# Releasing Buzz
|
||||
|
||||
Buzz has three independent release lanes, each driven by a release PR — no human
|
||||
ever pushes a git tag:
|
||||
Buzz has three independent release lanes. Desktop and relay use release PRs.
|
||||
Mobile uses immutable release-candidate tags cut directly from remote `main`:
|
||||
|
||||
| Lane | Recipe | Artifact |
|
||||
|------|--------|----------|
|
||||
| Lane | Entry point | Artifact |
|
||||
|------|-------------|----------|
|
||||
| Desktop | `just release-desktop` | Signed desktop app (macOS/Linux) |
|
||||
| Relay | `just release-relay` | `ghcr.io/block/buzz` container image |
|
||||
| Mobile | `just release-mobile` | Buzz mobile app (tag is the `sprout_ref` for the internal build) |
|
||||
| Mobile | `scripts/mobile-release.sh candidate X.Y.Z` | Exact `mobile-vX.Y.Z-rc.N` source identity |
|
||||
|
||||
The three lanes version independently: the desktop version lives in
|
||||
`desktop/package.json`, the relay version in `crates/buzz-relay/Cargo.toml`, and
|
||||
the mobile version in `mobile/pubspec.yaml`.
|
||||
|
||||
The mobile lane publishes a `mobile-v<version>` tag that is consumed
|
||||
**manually**, cross-repo, as the `sprout_ref` input to the internal
|
||||
`buzz-releases` Buildkite pipeline (iOS dogfood → Block Comp Portal, App Store →
|
||||
TestFlight — see [Internal Releases](#internal-releases)). The OSS lane is
|
||||
tag-only **by design**: OSS `block/buzz` CI cannot trigger CI in the private
|
||||
`buzz-releases` repo (infosec), so a human cuts the internal build from the tag
|
||||
rather than auto-dispatching across that boundary.
|
||||
The lanes version independently. Desktop reads its manifests, relay reads its
|
||||
crate manifest, and mobile derives both source and marketing version from the
|
||||
exact candidate tag. The mobile handoff to the private `buzz-releases` pipeline
|
||||
remains manual because OSS CI cannot trigger private CI.
|
||||
|
||||
## Quick Start
|
||||
|
||||
@@ -27,126 +20,97 @@ rather than auto-dispatching across that boundary.
|
||||
# Desktop release (next patch version)
|
||||
just release-desktop
|
||||
|
||||
# Desktop patch / minor / explicit
|
||||
just release-desktop patch
|
||||
# Desktop explicit version
|
||||
just release-desktop 0.4.0
|
||||
just release-desktop 1.0.0
|
||||
|
||||
# Relay release (same argument forms)
|
||||
# Relay release
|
||||
just release-relay
|
||||
just release-relay 0.4.0
|
||||
|
||||
# Mobile release (same argument forms)
|
||||
just release-mobile
|
||||
just release-mobile 0.4.0
|
||||
# Publish the next mobile candidate from the exact current remote main commit
|
||||
scripts/mobile-release.sh candidate 0.5.0
|
||||
```
|
||||
|
||||
`just release-desktop` creates a `version-bump/<version>` PR; `just
|
||||
release-relay` creates a `relay-release/<version>` PR; `just release-mobile`
|
||||
creates a `mobile-release/<version>` PR. Each bumps its own version manifest,
|
||||
regenerates lockfiles, and appends a changelog entry. Merge the PR to trigger
|
||||
the build automatically (the mobile tag is instead the `sprout_ref` a human
|
||||
feeds the internal build — see above).
|
||||
|
||||
Re-running any of these recipes with the same version is safe — it detects the
|
||||
existing branch and PR, resets to current `main`, regenerates the changelog
|
||||
with any new commits, and updates the PR in place.
|
||||
Desktop and relay releases use metadata PRs. Mobile does not. Each
|
||||
`mobile-vX.Y.Z-rc.N` tag is an immutable candidate and the artifact of record.
|
||||
There is no mobile release branch, stable mobile tag alias, finalization step,
|
||||
or mobile GitHub Release.
|
||||
|
||||
---
|
||||
|
||||
## How It Works
|
||||
|
||||
All three lanes share one engine; they differ only in which version manifest
|
||||
they bump, which branch prefix they use, and what the merge triggers.
|
||||
|
||||
The merge workflow creates tags with a short-lived installation token from the
|
||||
dedicated `buzz-release-bot` GitHub App. Release-tag rules allow that App to
|
||||
create matching tags and prevent other actors from creating, moving, or
|
||||
deleting them. The workflow's default `GITHUB_TOKEN` is read-only.
|
||||
|
||||
### Desktop
|
||||
|
||||
1. **`just release-desktop`** runs locally on `main` — computes the next
|
||||
version, creates (or reuses) a `version-bump/<version>` branch, bumps the
|
||||
desktop manifests, regenerates lockfiles, generates a changelog
|
||||
entry in `CHANGELOG.md`, commits, pushes, and opens (or updates) a PR.
|
||||
2. **Merge the PR** — the `auto-tag-on-release-pr-merge` workflow detects the
|
||||
`version-bump/*` branch merge and pushes a `v<version>` tag.
|
||||
3. **Tag triggers `release.yml`** — builds, signs, notarizes, and publishes the
|
||||
desktop app for macOS and Linux.
|
||||
1. **`just release-desktop`** runs locally on `main`, creates or updates a
|
||||
`version-bump/<version>` PR, bumps the desktop manifests, regenerates
|
||||
lockfiles, and updates `CHANGELOG.md`.
|
||||
2. **Merge the PR.** `auto-tag-on-release-pr-merge` pushes `v<version>`.
|
||||
3. **The tag triggers `release.yml`.** It builds, signs, notarizes, and
|
||||
publishes the desktop app for macOS and Linux.
|
||||
|
||||
### Relay
|
||||
|
||||
1. **`just release-relay`** runs locally on `main` — computes the next relay
|
||||
version, creates (or reuses) a `relay-release/<version>` branch, bumps
|
||||
`crates/buzz-relay/Cargo.toml`, regenerates `Cargo.lock`, generates a
|
||||
changelog entry in `crates/buzz-relay/CHANGELOG.md`, commits, pushes, and
|
||||
opens (or updates) a PR.
|
||||
2. **Merge the PR** — the `auto-tag-on-release-pr-merge` workflow detects the
|
||||
`relay-release/*` branch merge and pushes a `relay-v<version>` tag.
|
||||
3. **Tag triggers `docker.yml`** — the `relay-v<version>` push triggers
|
||||
`docker.yml`, which builds the multi-arch relay
|
||||
image and publishes `ghcr.io/block/buzz:<version>` (plus `:<major>.<minor>`,
|
||||
`:<major>`, and `:latest` for stable releases). Prereleases
|
||||
(`relay-v<version>-rc.1`) publish only the prerelease tag and do **not**
|
||||
move `:latest`. GitHub runs the tag trigger because the tag is created by
|
||||
the dedicated GitHub App rather than the workflow's `GITHUB_TOKEN`.
|
||||
1. **`just release-relay`** runs locally on `main`, creates or updates a
|
||||
`relay-release/<version>` PR, bumps `crates/buzz-relay/Cargo.toml`,
|
||||
regenerates `Cargo.lock`, and updates the relay changelog.
|
||||
2. **Merge the PR.** `auto-tag-on-release-pr-merge` pushes
|
||||
`relay-v<version>`.
|
||||
3. **The tag triggers `docker.yml`.** Stable releases update the version
|
||||
aliases and `latest`; prereleases do not.
|
||||
|
||||
Every push to `main` continues to build and publish `:main` + `:sha-<7>` tags
|
||||
(the rolling development image). The `:latest` tag tracks the latest **stable**
|
||||
relay release only — it does not move on main pushes or prereleases.
|
||||
Every push to `main` continues to publish the rolling relay `:main` and
|
||||
`:sha-<7>` tags.
|
||||
|
||||
### Mobile
|
||||
|
||||
1. **`just release-mobile`** runs locally on `main` — computes the next mobile
|
||||
version, creates (or reuses) a `mobile-release/<version>` branch, bumps
|
||||
`mobile/pubspec.yaml` (preserving the `+build` number), regenerates
|
||||
`mobile/pubspec.lock`, generates a changelog entry in `mobile/CHANGELOG.md`,
|
||||
commits, pushes, and opens (or updates) a PR.
|
||||
2. **Merge the PR** — the `auto-tag-on-release-pr-merge` workflow detects the
|
||||
`mobile-release/*` branch merge and pushes a `mobile-v<version>` tag.
|
||||
3. **The tag is consumed manually, cross-repo** — nothing in OSS `block/buzz`
|
||||
builds on the tag (OSS CI must not trigger CI in the private `buzz-releases`
|
||||
repo — infosec). A human feeds the `mobile-v<version>` tag as the
|
||||
`sprout_ref` input to the internal `buzz-releases` Buildkite pipeline, which
|
||||
builds and ships iOS to Block Comp Portal (dogfood) and TestFlight (App
|
||||
Store, opt-in). See [Internal Releases](#internal-releases).
|
||||
1. **Publish a candidate.** From a clean checkout whose `origin` is the
|
||||
canonical `block/buzz` repository, run
|
||||
`scripts/mobile-release.sh candidate X.Y.Z`. The script resolves and fetches
|
||||
the exact current `origin/main` commit, derives the next number from exact
|
||||
remote tags for that marketing version, and publishes an annotated
|
||||
`mobile-vX.Y.Z-rc.N` tag there through the dedicated `buzz-release-bot`
|
||||
GitHub App. It never uses the operator's checked-out commit and never moves
|
||||
an existing candidate.
|
||||
2. **Build the exact tag.** Enter the candidate tag as `mobile_ref` in the
|
||||
private Buzz mobile Buildkite pipeline. OSS CI deliberately cannot trigger
|
||||
that private pipeline. The tag supplies both source commit and release
|
||||
version. Flutter receives clean marketing version `X.Y.Z`; Buildkite's
|
||||
monotonically increasing build number supplies the platform build number.
|
||||
3. **Promote tested artifacts.** Promote the already-built signed artifact for
|
||||
each platform through its store workflow. Record the exact tag with the
|
||||
build or rollout record. No source ref is changed and no final build is cut.
|
||||
|
||||
The iOS and Android artifacts for one marketing version may come from different
|
||||
RC tags. For example, iOS can ship `mobile-v0.5.0-rc.2` while Android ships
|
||||
`mobile-v0.5.0-rc.3`. Each platform's exact candidate tag is its source record.
|
||||
There is intentionally no single selected or final candidate for the marketing
|
||||
version.
|
||||
|
||||
The simplification trades away a separate stabilization line. Unrelated commits
|
||||
that reach `main` become part of every later candidate, and there is no retained
|
||||
hotfix branch or branch-ancestry history. Add a dedicated hotfix flow later if a
|
||||
release actually needs isolation from `main`.
|
||||
|
||||
`mobile/pubspec.yaml` keeps `0.0.0+1` only as a valid, visibly non-release
|
||||
fallback for local development and validation builds. Release jobs always
|
||||
inject both version fields. `mobile/CHANGELOG.md` is retained as historical
|
||||
release data. It is not a release ledger for this flow.
|
||||
|
||||
---
|
||||
|
||||
## Release Types
|
||||
## Version Sources
|
||||
|
||||
The argument forms below apply to `release-desktop`, `release-relay`, and
|
||||
`release-mobile`:
|
||||
| Lane | Release version authority |
|
||||
|------|---------------------------|
|
||||
| Desktop | `desktop/package.json` and synchronized desktop manifests |
|
||||
| Relay | `crates/buzz-relay/Cargo.toml` |
|
||||
| Mobile | Exact `mobile-vX.Y.Z-rc.N` remote tag |
|
||||
|
||||
| Command | Version | Example |
|
||||
|---------|---------|---------|
|
||||
| `just release-desktop` | Next patch | `0.3.0` → `0.3.1` |
|
||||
| `just release-desktop patch` | Next patch | `0.3.0` → `0.3.1` |
|
||||
| `just release-desktop 0.4.0` | Explicit minor | `0.3.1` → `0.4.0` |
|
||||
| `just release-desktop 1.0.0` | Explicit | `1.0.0` |
|
||||
|
||||
---
|
||||
|
||||
## Version Files
|
||||
|
||||
`just bump-desktop-version <version>` (desktop lane) updates these files:
|
||||
|
||||
| File | Field |
|
||||
|------|-------|
|
||||
| `desktop/package.json` | `"version"` |
|
||||
| `desktop/src-tauri/tauri.conf.json` | `"version"` |
|
||||
| `desktop/src-tauri/Cargo.toml` | `version` (under `[package]`) |
|
||||
|
||||
It also regenerates `pnpm-lock.yaml` and `desktop/src-tauri/Cargo.lock`.
|
||||
|
||||
`just bump-relay-version <version>` (relay lane) updates
|
||||
`crates/buzz-relay/Cargo.toml` (`version` under `[package]`) and regenerates the
|
||||
workspace `Cargo.lock`.
|
||||
|
||||
`just bump-mobile-version <version>` (mobile lane) updates
|
||||
`mobile/pubspec.yaml` (`version:`, preserving the `+build` number) and
|
||||
regenerates `mobile/pubspec.lock`.
|
||||
`just bump-desktop-version <version>` updates the desktop manifests and
|
||||
regenerates their lockfiles. `just bump-relay-version <version>` updates the
|
||||
relay crate and regenerates `Cargo.lock`. Mobile has no bump recipe or
|
||||
release-metadata PR.
|
||||
|
||||
---
|
||||
|
||||
@@ -184,42 +148,46 @@ existing immutable `v<version>` tag. Select that tag in the ref picker and
|
||||
provide the matching semver version without the `v` prefix. It cannot build
|
||||
from `main` or another caller-selected source ref.
|
||||
|
||||
Mobile intentionally has no branch or arbitrary-ref fallback. The private
|
||||
Buildkite pipeline accepts only an exact candidate tag.
|
||||
|
||||
---
|
||||
|
||||
## Internal Releases
|
||||
|
||||
After the OSS release ships, trigger an internal build via the
|
||||
[sprout-releases Buildkite pipeline](https://buildkite.com/runway/sprout-releases).
|
||||
See the [buzz-releases README](https://github.com/squareup/buzz-releases#cutting-a-release)
|
||||
for the full step-by-step instructions and input field reference.
|
||||
For mobile, trigger the private
|
||||
[Release Mobile pipeline](https://buildkite.com/runway/buzz-mobile-releases) with
|
||||
an exact RC tag for the platform build being cut. For desktop, use
|
||||
[Release Desktop](https://buildkite.com/runway/sprout-releases). See the
|
||||
[buzz-releases README](https://github.com/squareup/buzz-releases#cutting-a-release)
|
||||
for the private pipeline contract.
|
||||
|
||||
---
|
||||
|
||||
## What Gets Published
|
||||
|
||||
Each release produces two GitHub releases:
|
||||
Desktop publishes two GitHub releases:
|
||||
|
||||
1. **`v<version>`** — the user-facing release with the `.dmg` installer
|
||||
(macOS).
|
||||
1. **`v<version>`**: the user-facing release with installers.
|
||||
2. **`buzz-desktop-latest`**: the rolling auto-updater release.
|
||||
|
||||
2. **`buzz-desktop-latest`** — a rolling pre-release for the Tauri
|
||||
auto-updater containing `latest.json` and each platform's signed
|
||||
updater artifact plus its `.sig` signature (`.tar.gz` on macOS,
|
||||
`.AppImage` on Linux, and `_alpha-unsigned.exe` on Windows).
|
||||
Mobile publishes only annotated `mobile-vX.Y.Z-rc.N` git tags. Store artifacts
|
||||
and rollout records retain the exact tag they used. Mobile does not publish a
|
||||
GitHub Release or a stable `mobile-vX.Y.Z` alias.
|
||||
|
||||
---
|
||||
|
||||
## Platform Support
|
||||
|
||||
The release workflow builds **two separate macOS DMGs** — Apple
|
||||
The release workflow builds **two separate macOS DMGs**: Apple
|
||||
Silicon (`darwin-aarch64`, the `release` job) and Intel
|
||||
(`darwin-x86_64`, the `release-macos-x64` job) — plus Linux `.deb` and
|
||||
(`darwin-x86_64`, the `release-macos-x64` job), plus Linux `.deb` and
|
||||
`.AppImage`. Both macOS DMGs are codesigned, notarized, and attached to
|
||||
the same `v<version>` release. Intel users download the `_x64.dmg`.
|
||||
|
||||
The Linux AppImage is post-processed by `desktop/scripts/fix-appimage.sh`,
|
||||
which strips infra libraries over-bundled by linuxdeploy (they crash on
|
||||
Mesa 25+ / GLib 2.88 distros — see
|
||||
Mesa 25+ / GLib 2.88 distros; see
|
||||
[tauri-apps/tauri#15665](https://github.com/tauri-apps/tauri/issues/15665))
|
||||
and re-signs the artifact. As a result the AppImage relies on the
|
||||
host's Wayland/GStreamer/graphics stack and requires GLib >= 2.72
|
||||
@@ -231,8 +199,16 @@ host's Wayland/GStreamer/graphics stack and requires GLib >= 2.72
|
||||
## Prerequisites
|
||||
|
||||
- **Write access** to the `block/buzz` GitHub repository
|
||||
- **`gh` CLI** authenticated (`gh auth status`)
|
||||
- The following **GitHub Actions secrets** must be configured:
|
||||
- An `origin` remote whose configured URL is the canonical `block/buzz`
|
||||
repository
|
||||
- `gh` CLI version 2.87.0 or newer, authenticated with permission to dispatch
|
||||
the candidate workflow
|
||||
- Release tag ruleset [`14378754`](https://github.com/block/buzz/rules/14378754)
|
||||
active for `mobile-v*`, with creation, update, deletion, and non-fast-forward
|
||||
protections and `buzz-release-bot` as its sole always-bypass actor
|
||||
- The `buzz-release-bot` App credentials configured for GitHub Actions
|
||||
- The following **GitHub Actions secrets** must also be configured for the
|
||||
desktop release lane:
|
||||
|
||||
| Secret | Purpose |
|
||||
|--------|---------|
|
||||
@@ -240,6 +216,14 @@ host's Wayland/GStreamer/graphics stack and requires GLib >= 2.72
|
||||
| `TAURI_SIGNING_PRIVATE_KEY` | Tauri updater private key |
|
||||
| `TAURI_SIGNING_PRIVATE_KEY_PASSWORD` | Password for the private key |
|
||||
|
||||
Mobile candidate publication requires workflow-dispatch access and the existing
|
||||
release App because strict tag protection denies direct human creation. The App
|
||||
must be installed on `block/buzz`, have Contents write and Metadata read, and
|
||||
retain an `always` bypass on the immutable `mobile-v*` tag rules. It does not
|
||||
require GitHub Releases permissions, repository Administration permission, or a
|
||||
mobile release-branch ruleset. The publisher validates both the App token's
|
||||
effective `current_user_can_bypass` value and the exact ruleset bypass actor set.
|
||||
|
||||
---
|
||||
|
||||
## Troubleshooting
|
||||
@@ -250,15 +234,38 @@ Switch to `main` and pull latest before running the release recipe.
|
||||
### `just release-desktop` fails with "working tree is dirty"
|
||||
Commit or stash your changes before running the release recipe.
|
||||
|
||||
### New commits merged after creating the release PR
|
||||
Re-run the release recipe (`just release-desktop`, `just release-relay`, or `just release-mobile`) from an up-to-date `main`. It resets the branch to current `main`, regenerates the changelog and PR body to include the new commits, and force-pushes the updated branch.
|
||||
### New commits land after publishing a mobile candidate
|
||||
|
||||
### Build fails at "Validate version"
|
||||
The version string must be valid semver: `MAJOR.MINOR.PATCH` with an optional pre-release suffix. Do not include a `v` prefix.
|
||||
Run `scripts/mobile-release.sh candidate <version>` again after the intended
|
||||
fix reaches remote `main`. It publishes a new immutable RC tag at the new exact
|
||||
remote commit. Continue referring to each tested or shipped platform artifact by
|
||||
its own exact tag.
|
||||
|
||||
### `scripts/mobile-release.sh candidate` fails because `main` moved during publication
|
||||
|
||||
The App-backed workflow may already have published the requested immutable RC
|
||||
at the prior `main` tip before the operator command detects the race. Do not
|
||||
move or delete that tag, and do not treat it as the candidate for current
|
||||
`main`. Inspect the run URL from the command output, then rerun
|
||||
`scripts/mobile-release.sh candidate <version>` to publish the next RC from the
|
||||
new current `main` tip.
|
||||
|
||||
### A mobile candidate command selects the wrong RC number
|
||||
|
||||
Do not retry by moving or deleting a tag. Inspect the exact remote `mobile-v*`
|
||||
tags and resolve the unexpected state. Candidate numbers are monotonically
|
||||
increasing remote identities.
|
||||
|
||||
### A mobile candidate publication is rejected by repository rules
|
||||
|
||||
Confirm `buzz-release-bot` remains the sole always-bypass actor for the active
|
||||
`mobile-v*` ruleset and that its Actions credentials are available. Do not grant
|
||||
direct human creation or weaken update or deletion protection. Existing
|
||||
candidate tags must remain immutable.
|
||||
|
||||
### Auto-updater reports "no update available"
|
||||
Verify that the `buzz-desktop-latest` release exists and contains a
|
||||
valid `latest.json`. The manifest covers all four platform keys
|
||||
(`darwin-aarch64`, `darwin-x86_64`, `linux-x86_64`,
|
||||
`windows-x86_64`); a missing entry usually means that platform's
|
||||
release job failed — check the workflow run.
|
||||
release job failed. Check the workflow run.
|
||||
|
||||
Reference in New Issue
Block a user