R2D2-MERIDIAN/RELEASING.md
Joshua Belke c6dcd136b1 refactor: move clients under REMAPPING deployable grammar
Land desktop, web, mobile, and admin-web at REMAPPING/meridian-* with
rewritten build/CI paths, the IHLC scaffold, and an updated STELLAR snapshot
so the CTO-required one-directory-per-deployable layout is the live tree.

Signed-off-by: Joshua Belke <joshua@innovationhub-act.org>
2026-08-19 14:01:39 -04:00

13 KiB
Raw Permalink Blame History

Releasing Meridian

Meridian has three independent release lanes. Desktop and relay use release PRs. Mobile uses immutable release-candidate tags cut directly from remote main:

Lane Entry point Artifact
Desktop just release-desktop Signed desktop app (macOS/Linux)
Relay just release-relay registry.r2d2.office.ilab.zone/meridian container image
Mobile scripts/mobile-release.sh candidate X.Y.Z Exact mobile-vX.Y.Z-rc.N source identity

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 meridian-releases pipeline remains manual because OSS CI cannot trigger private CI.

Quick Start

# Desktop release (next patch version)
just release-desktop

# Desktop explicit version
just release-desktop 0.4.0

# Relay release
just release-relay
just release-relay 0.4.0

# Publish the next mobile candidate from the exact current remote main commit
scripts/mobile-release.sh candidate 0.5.0

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

Desktop

The first Meridian desktop release is 0.6.0. Do not cut 0.0.2. The manifests read 0.0.1 after the rebrand reset, so the next number looks like 0.0.2 — but this repo carries 148 tags inherited from the pre-rebrand project, and the desktop's series is the bare v* one (every other component is prefixed: relay-v, chart-v, push-chart-v, mobile-v). In that series v0.0.2 through v0.0.22 are all taken, v0.1.x and v0.2.x are empty, v0.3.x–v0.5.x are consumed, and the maximum is v0.5.2. Tags are immutable here, so a taken name is not a naming annoyance — it is a release that cannot be cut.

The floor is 0.6.0 rather than the also-free 0.1.0 because all 103 bare tags are reachable from main (git describe --tags --abbrev=0 returns v0.5.2). Anything resolving "latest" by version sort — docker.yml and scripts/test-signed-canary-contract.sh both do — would rank inherited v0.5.2 above a new v0.1.0, so the first real release would be invisible to exactly the tooling that selects releases, and would look fine when cut. Starting above the inherited maximum keeps that ordering honest. Council call recorded on meridian-xr5g; reversal evidence is a consumer found pinning bare v0.5.x.

  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. The manifest bump belongs to this commit — the running artifact must carry the version its tag names.
  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, creates or updates a relay-release/<version> PR, bumps crates/meridian-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. Each release also publishes an optimized, symbol-bearing image under matching debug- tags (for example, debug-0.3.0 and debug-latest) for native profiling. The ordinary tags remain stripped and are the default for deployments that do not need it.

Every push to main continues to publish the rolling relay :main and :sha-<7> tags, plus matching :debug-main and :debug-sha-<7> variants.

Mobile

  1. Publish a candidate. From a clean checkout whose origin is the canonical RAID/R2D2-MERIDIAN 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 meridian-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 Meridian 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.

REMAPPING/meridian-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. REMAPPING/meridian-mobile/CHANGELOG.md is retained as historical release data. It is not a release ledger for this flow.


Version Sources

Lane Release version authority
Desktop REMAPPING/meridian-desktop/package.json and synchronized desktop manifests
Relay crates/meridian-relay/Cargo.toml
Mobile Exact mobile-vX.Y.Z-rc.N remote tag

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.


Signed macOS Canary

Use the manual Signed macOS Canary workflow when you need an Apple Silicon build of current main for explicit testing without publishing a release:

gh workflow run signed-macos-canary.yml --repo RAID/R2D2-MERIDIAN --ref main

The workflow derives a -test.<run-number> version, signs and notarizes the DMG, verifies it with Gatekeeper, and uploads it as a short-lived Actions artifact with seven-day retention. Because this is a public repository, any signed-in GitHub user can download that artifact while it exists; it is unpublished, not private. The workflow has no release permissions, does not create or move tags, and cannot update meridian-desktop-latest or latest.json.

Download the artifact from the completed run:

gh run download <run-id> --repo RAID/R2D2-MERIDIAN --name <artifact-name>

The workflow intentionally accepts only main. Use the normal release process for distributable builds or builds from an immutable release tag.


Manual Release Retry

The Release workflow's manual dispatch is only a retry mechanism for an 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.


Signed platform builds

Not yet wired for this deployment. The signed macOS/iOS build path that produced these artifacts ran on a Block-internal Buildkite pipeline (sprout-releases) that a self-hosted Meridian cannot reach. The candidate tag flow above still applies and still produces the version strings; what is missing is the signing pipeline that consumes them.

Until that is replaced, desktop and mobile releases are unsigned local builds. Define the replacement here before publishing anything externally — do not re-point these steps at a pipeline that does not exist.


What Gets Published

Desktop publishes two GitHub releases:

  1. v<version>: the user-facing release with installers.
  2. meridian-desktop-latest: the rolling auto-updater release.

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 Silicon (darwin-aarch64, the release job) and Intel (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 REMAPPING/meridian-desktop/scripts/fix-appimage.sh, which strips infra libraries over-bundled by linuxdeploy (they crash on Mesa 25+ / GLib 2.88 distros; see tauri-apps/tauri#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 (Ubuntu 22.04 or newer). The release-linux job builds inside a ubuntu:22.04 container for broad GLIBC compatibility.


Prerequisites

  • Write access to the RAID/R2D2-MERIDIAN GitHub repository
  • An origin remote whose configured URL is the canonical RAID/R2D2-MERIDIAN repository
  • gh CLI version 2.87.0 or newer, authenticated with permission to dispatch the candidate workflow
  • Release tag ruleset 14378754 active for mobile-v*, with creation, update, deletion, and non-fast-forward protections and meridian-release-bot as its sole always-bypass actor
  • The meridian-release-bot App credentials configured for GitHub Actions
  • The following GitHub Actions secrets must also be configured for the desktop release lane:
    Secret Purpose
    MERIDIAN_UPDATER_PUBLIC_KEY Tauri updater public key (minisign)
    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 RAID/R2D2-MERIDIAN, 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 the App token's effective current_user_can_bypass value rather than reading the ruleset's hidden bypass actor list.


Troubleshooting

just release-desktop fails with "must be on main branch"

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 land after publishing a mobile candidate

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 meridian-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 meridian-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.