From a5d609e2611b077c0bc908af9de8328e8a7ef59d Mon Sep 17 00:00:00 2001 From: "Ryan Johnson (ntninja)" Date: Sun, 5 Jul 2026 01:00:49 +0000 Subject: [PATCH] =?UTF-8?q?feat(release):=20v0.4.0-alpha.1=20=E2=80=94=20o?= =?UTF-8?q?ne=20product=20version,=20live=20desktop=20builds,=20macOS=20le?= =?UTF-8?q?g?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The alpha release scheme (RD-approved): - ONE version everywhere: [workspace.package].version = 0.4.0-alpha.1, inherited by every spine crate; the standalone src-tauri crate, tauri.conf.json, and the console package.json are kept in lock-step by the new 'cargo xtask version' command (prints current with no args; refuses malformed or partial bumps). Scheme: 0.x milestones, -alpha.N/-beta.N pre-releases; CONTRACT_VERSION stays the independent wire-compat integer. (Also: the workspace repository URL still pointed at GitLab — now GitHub.) - GET /about serves { name, version, contract_version } and the console renders a fixed build-stamp footer from it (field-support: a bug report quotes the version of the rig it came from). Contract-pinned. - The DESKTOP builds carry the 'live' feature: the product's whole job is talking to real timers, and the alpha field test runs against real RH hardware. The TLS stack is vendored, so the portable stays one file. - macOS (Apple Silicon) joins the release matrix beside Linux and Windows. Co-Authored-By: Claude Fable 5 --- .github/workflows/release-builds.yml | 5 ++ Cargo.lock | 18 ++-- Cargo.toml | 6 +- crates/adapters/Cargo.toml | 2 +- crates/app/Cargo.toml | 2 +- crates/engine/Cargo.toml | 2 +- crates/events/Cargo.toml | 2 +- crates/projection/Cargo.toml | 2 +- crates/server/Cargo.toml | 2 +- crates/server/src/app.rs | 14 ++++ crates/storage/Cargo.toml | 2 +- crates/testkit/Cargo.toml | 2 +- frontend/apps/rd-console/package.json | 2 +- frontend/apps/rd-console/src/App.svelte | 27 ++++++ .../apps/rd-console/tests/AudioHost.svelte | 1 - frontend/contract/events.contract.ts | 12 +++ src-tauri/Cargo.toml | 14 +++- src-tauri/tauri.conf.json | 2 +- xtask/Cargo.toml | 2 +- xtask/src/main.rs | 82 ++++++++++++++++++- 20 files changed, 174 insertions(+), 27 deletions(-) diff --git a/.github/workflows/release-builds.yml b/.github/workflows/release-builds.yml index 1fc22ea..88b2be5 100644 --- a/.github/workflows/release-builds.yml +++ b/.github/workflows/release-builds.yml @@ -35,6 +35,11 @@ jobs: - os: windows-latest portable-artifact: gridfpv-windows-portable portable-path: src-tauri/target/release/gridfpv-desktop.exe + - os: macos-latest + # Apple Silicon (macos-latest = arm64) — covers every modern Mac; an Intel + # (x86_64) leg is a matrix add if one ever turns up at the field. + portable-artifact: gridfpv-macos-portable + portable-path: src-tauri/target/release/gridfpv-desktop steps: - uses: actions/checkout@v7 diff --git a/Cargo.lock b/Cargo.lock index b5714cc..493a068 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -438,7 +438,7 @@ dependencies = [ [[package]] name = "gridfpv-adapters" -version = "0.1.0" +version = "0.4.0-alpha.1" dependencies = [ "gridfpv-events", "gridfpv-projection", @@ -452,7 +452,7 @@ dependencies = [ [[package]] name = "gridfpv-app" -version = "0.1.0" +version = "0.4.0-alpha.1" dependencies = [ "axum", "gridfpv-adapters", @@ -473,7 +473,7 @@ dependencies = [ [[package]] name = "gridfpv-engine" -version = "0.1.0" +version = "0.4.0-alpha.1" dependencies = [ "gridfpv-adapters", "gridfpv-events", @@ -486,7 +486,7 @@ dependencies = [ [[package]] name = "gridfpv-events" -version = "0.1.0" +version = "0.4.0-alpha.1" dependencies = [ "serde", "serde_json", @@ -495,7 +495,7 @@ dependencies = [ [[package]] name = "gridfpv-projection" -version = "0.1.0" +version = "0.4.0-alpha.1" dependencies = [ "gridfpv-events", "serde", @@ -505,7 +505,7 @@ dependencies = [ [[package]] name = "gridfpv-server" -version = "0.1.0" +version = "0.4.0-alpha.1" dependencies = [ "axum", "futures-util", @@ -527,7 +527,7 @@ dependencies = [ [[package]] name = "gridfpv-storage" -version = "0.1.0" +version = "0.4.0-alpha.1" dependencies = [ "gridfpv-events", "rusqlite", @@ -538,7 +538,7 @@ dependencies = [ [[package]] name = "gridfpv-testkit" -version = "0.1.0" +version = "0.4.0-alpha.1" [[package]] name = "h2" @@ -2236,7 +2236,7 @@ checksum = "1ffae5123b2d3fc086436f8834ae3ab053a283cfac8fe0a0b8eaae044768a4c4" [[package]] name = "xtask" -version = "0.1.0" +version = "0.4.0-alpha.1" dependencies = [ "gridfpv-testkit", "serde_json", diff --git a/Cargo.toml b/Cargo.toml index c3d0faf..ffa6c24 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -23,9 +23,13 @@ exclude = ["src-tauri"] # Shared metadata + dependency versions for every crate in the spine. # Crates inherit these with `field.workspace = true` so versions are set once. [workspace.package] +# The ONE product version (v0.4.0-alpha.1 scheme: 0.x milestones, -alpha/-beta pre-releases; +# bump with `cargo xtask version `). Crates inherit it; tauri.conf.json and the +# console package.json are kept in lock-step by the same xtask command. +version = "0.4.0-alpha.1" edition = "2024" license = "AGPL-3.0-or-later" -repository = "https://gitlab.com/gridfpv/gridfpv" +repository = "https://github.com/GridFPV/gridfpv" rust-version = "1.85" [workspace.dependencies] diff --git a/crates/adapters/Cargo.toml b/crates/adapters/Cargo.toml index 0f5f172..c591880 100644 --- a/crates/adapters/Cargo.toml +++ b/crates/adapters/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "gridfpv-adapters" -version = "0.1.0" +version.workspace = true edition.workspace = true license.workspace = true repository.workspace = true diff --git a/crates/app/Cargo.toml b/crates/app/Cargo.toml index ae9fbfc..36c17b1 100644 --- a/crates/app/Cargo.toml +++ b/crates/app/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "gridfpv-app" -version = "0.1.0" +version.workspace = true edition.workspace = true license.workspace = true repository.workspace = true diff --git a/crates/engine/Cargo.toml b/crates/engine/Cargo.toml index 0c7ab8a..c8cb0e7 100644 --- a/crates/engine/Cargo.toml +++ b/crates/engine/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "gridfpv-engine" -version = "0.1.0" +version.workspace = true edition.workspace = true license.workspace = true repository.workspace = true diff --git a/crates/events/Cargo.toml b/crates/events/Cargo.toml index d735192..58135f3 100644 --- a/crates/events/Cargo.toml +++ b/crates/events/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "gridfpv-events" -version = "0.1.0" +version.workspace = true edition.workspace = true license.workspace = true repository.workspace = true diff --git a/crates/projection/Cargo.toml b/crates/projection/Cargo.toml index d661537..e3b2415 100644 --- a/crates/projection/Cargo.toml +++ b/crates/projection/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "gridfpv-projection" -version = "0.1.0" +version.workspace = true edition.workspace = true license.workspace = true repository.workspace = true diff --git a/crates/server/Cargo.toml b/crates/server/Cargo.toml index 12f3829..991e857 100644 --- a/crates/server/Cargo.toml +++ b/crates/server/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "gridfpv-server" -version = "0.1.0" +version.workspace = true edition.workspace = true license.workspace = true repository.workspace = true diff --git a/crates/server/src/app.rs b/crates/server/src/app.rs index a0be296..5680788 100644 --- a/crates/server/src/app.rs +++ b/crates/server/src/app.rs @@ -408,6 +408,20 @@ pub fn router(registry: EventRegistry) -> Router { // event id to its own `AppState`/log through the registry. let read = Router::new() .route("/health", get(|| async { "ok" })) + // The product identity (alpha field-support): WHICH build is this rig running? The + // console footer reads it, and a bug report from the field should quote it. The + // version is the ONE workspace version (v0.4.0-alpha.1 scheme, `cargo xtask version`); + // the contract version is the independent wire-compat integer. + .route( + "/about", + get(|| async { + Json(serde_json::json!({ + "name": "GridFPV", + "version": env!("CARGO_PKG_VERSION"), + "contract_version": crate::CONTRACT_VERSION, + })) + }), + ) // The Director's wall clock, in epoch microseconds (open, no auth). The console measures its // offset from this (round-trip-corrected) so the start countdown + race clock read off // *server* time, not the RD device's clock — which can differ by ~1s on a separate laptop and diff --git a/crates/storage/Cargo.toml b/crates/storage/Cargo.toml index ed2ecf0..7db0bb5 100644 --- a/crates/storage/Cargo.toml +++ b/crates/storage/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "gridfpv-storage" -version = "0.1.0" +version.workspace = true edition.workspace = true license.workspace = true repository.workspace = true diff --git a/crates/testkit/Cargo.toml b/crates/testkit/Cargo.toml index 5b2e82e..3590c6b 100644 --- a/crates/testkit/Cargo.toml +++ b/crates/testkit/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "gridfpv-testkit" -version = "0.1.0" +version.workspace = true edition.workspace = true license.workspace = true repository.workspace = true diff --git a/frontend/apps/rd-console/package.json b/frontend/apps/rd-console/package.json index bed8703..a81b25b 100644 --- a/frontend/apps/rd-console/package.json +++ b/frontend/apps/rd-console/package.json @@ -1,6 +1,6 @@ { "name": "@gridfpv/rd-console", - "version": "0.0.0", + "version": "0.4.0-alpha.1", "private": true, "description": "GridFPV RD console surface — control-oriented, dense, authenticated. Minimal shell; filled by #51+.", "license": "AGPL-3.0-or-later", diff --git a/frontend/apps/rd-console/src/App.svelte b/frontend/apps/rd-console/src/App.svelte index 33294af..0858c16 100644 --- a/frontend/apps/rd-console/src/App.svelte +++ b/frontend/apps/rd-console/src/App.svelte @@ -230,6 +230,17 @@ } // ── Settings (the RD token, set/cleared up front) ────────────────────────── + // The running Director's identity for the footer build-stamp (alpha field-support: a bug + // report should quote the version). Same-origin fetch — the console is always served BY the + // Director; a failed read leaves the footer blank rather than lying. + let about = $state<{ version: string } | undefined>(undefined); + $effect(() => { + fetch('/about') + .then((r) => (r.ok ? r.json() : undefined)) + .then((a) => (about = a)) + .catch(() => {}); + }); + // v1 keeps NO manual settings surface: the control token is requested automatically by // TokenDialog when a privileged action needs one (loopback needs none at all), and the // setup wizard runs once on event creation (everything it sets stays editable on the @@ -375,9 +386,25 @@ +{#if about} +
GridFPV v{about.version}
+{/if} +