From 049232099b8ddc14cd07c6918c94b04107366ba6 Mon Sep 17 00:00:00 2001 From: Josh Knapp Date: Thu, 27 Aug 2026 10:34:56 -0700 Subject: [PATCH] Dedupe the preview-build predicate, fix two comment inaccuracies MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Final review pass gave this a clean bill of health overall but named three small things: - get_app_version() and check_for_updates() each read option_env!("TRIPLE_C_BUILD_SUFFIX") independently with slightly different idioms — if one were ever edited alone, the About panel and the update check could silently disagree about whether this is a preview build. Extracted preview_build_suffix() as the single place that reads and classifies it. - pick_update's doc comment described the unparseable-tag case as a `-preview.` suffix; the actual tag build-app-preview.yml creates is `preview-` (no version, no dot) — already correct in the neighboring GitHubRelease::prerelease comment, just not here. - That same prerelease comment claimed defence against a preview release leaking through backfill-releases.yml, but a preview's tag already fails semver parsing on its own — this field's actual job is the case parsing can't catch: a normally-tagged release someone flags prerelease on Gitea (a hotfix candidate, an RC) that a backfill would otherwise mirror as-is. Co-Authored-By: Claude Sonnet 5 Claude-Session: https://claude.ai/code/session_01FGjXq6fqtAFHdbhk4f3PfZ --- app/src-tauri/src/commands/update_commands.rs | 27 +++++++++++++------ app/src-tauri/src/models/update_info.rs | 11 ++++---- 2 files changed, 25 insertions(+), 13 deletions(-) diff --git a/app/src-tauri/src/commands/update_commands.rs b/app/src-tauri/src/commands/update_commands.rs index 3f99e64..c3d571a 100644 --- a/app/src-tauri/src/commands/update_commands.rs +++ b/app/src-tauri/src/commands/update_commands.rs @@ -16,7 +16,7 @@ const REGISTRY_API_BASE: &str = const GHCR_TOKEN_URL: &str = "https://ghcr.io/token?scope=repository:shadowdao/triple-c-sandbox:pull"; -/// `CARGO_PKG_VERSION`, plus a build-time suffix when one was baked in. +/// The build-time preview suffix, if one was baked in and isn't blank. /// /// The bundle version itself (`tauri.conf.json`, `Cargo.toml`, `package.json`) /// is never given a `-preview.` suffix — `build-app-preview.yml` strips @@ -26,7 +26,17 @@ const GHCR_TOKEN_URL: &str = /// is the workaround: set as a build-time env var in the preview workflow /// only, so `option_env!` bakes it into the binary without the bundle version /// ever seeing it. A production build sets nothing, so `option_env!` reads -/// `None` and this is a no-op — see triple-c#32. +/// `None` here — see triple-c#32. +/// +/// The single source of truth for "is this a preview build": both +/// `get_app_version()` (what the About panel shows) and `check_for_updates()` +/// (whether a same-numbered release counts as an update — see `pick_update`) +/// read this rather than each calling `option_env!` themselves, so the two +/// can never silently disagree about which build this is. +fn preview_build_suffix() -> Option<&'static str> { + option_env!("TRIPLE_C_BUILD_SUFFIX").filter(|s| !s.is_empty()) +} + fn format_app_version(base: &str, build_suffix: Option<&str>) -> String { match build_suffix { Some(suffix) if !suffix.is_empty() => format!("{}-{}", base, suffix), @@ -36,7 +46,7 @@ fn format_app_version(base: &str, build_suffix: Option<&str>) -> String { #[tauri::command] pub fn get_app_version() -> String { - format_app_version(env!("CARGO_PKG_VERSION"), option_env!("TRIPLE_C_BUILD_SUFFIX")) + format_app_version(env!("CARGO_PKG_VERSION"), preview_build_suffix()) } #[tauri::command] @@ -79,7 +89,7 @@ pub async fn check_for_updates() -> Result, String> { // relaxes that one comparison to `>=` so "there is a real release at my // own number" reads as an update, without touching the production case // — see `pick_update`. - let is_preview_build = option_env!("TRIPLE_C_BUILD_SUFFIX").is_some_and(|s| !s.is_empty()); + let is_preview_build = preview_build_suffix().is_some(); match pick_update(&releases, current_semver, platform_extensions, is_preview_build) { Some(release) => { @@ -121,10 +131,11 @@ pub async fn check_for_updates() -> Result, String> { /// Three filters, all of which must pass: not a prerelease (see the long /// comment on `GitHubRelease::prerelease`), at least one asset for this /// platform, and a tag that parses as semver *and* beats what is running. A -/// tag that does not parse — a `-preview.` suffix, most realistically — -/// is skipped rather than erroring, the same as it always has been; nothing -/// here changes what an update tag is expected to look like, only what -/// channel it is allowed to come from. +/// tag that does not parse — `preview-` (the shape +/// `build-app-preview.yml` actually creates release tags with), most +/// realistically — is skipped rather than erroring, the same as it always +/// has been; nothing here changes what an update tag is expected to look +/// like, only what channel it is allowed to come from. /// /// `is_preview_build` relaxes "beats" from `>` to `>=`. A preview build's /// `current_semver` is the bare number it was compiled with, which is by diff --git a/app/src-tauri/src/models/update_info.rs b/app/src-tauri/src/models/update_info.rs index 478bf3b..17069d8 100644 --- a/app/src-tauri/src/models/update_info.rs +++ b/app/src-tauri/src/models/update_info.rs @@ -36,11 +36,12 @@ pub struct GitHubRelease { /// /// `build-app.yml`'s own mirror never publishes a prerelease, but /// `.gitea/workflows/backfill-releases.yml` forwards every Gitea release - /// unfiltered, `prerelease` included — so if it were ever dispatched - /// while a preview release existed, this field is what stops - /// `check_for_updates` from offering it (the `preview-` tag shape - /// already fails semver parsing independently, but this is real - /// defence-in-depth, not a no-op). + /// unfiltered, `prerelease` included. A preview release's `preview-` + /// tag already fails semver parsing on its own, so this field is not what + /// stops *that* case — it is what stops the case tag-parsing can't catch: + /// a normally-tagged release (`v0.4.13`) that someone marks as a + /// prerelease on Gitea (a hotfix candidate, an RC) and a backfill then + /// mirrors as-is. Real defence for that case, not a no-op. #[serde(default)] pub prerelease: bool, }