Skip to main content

max / makenotwork

mnw-cli: plan OTA publish subcommand to replace ota-publish.sh Designs a typed Rust implementation of MNW/server/deploy/ota-publish.sh across two layers — a synckit-client::ota module wrapping the server's existing OTA endpoints, and a `mnw ota publish` subcommand that discovers artifacts in dist/, reads Tauri signatures, and uploads. ~4 hours estimated. Post-beta. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Author: Max J. <87768334+MaxJMath@users.noreply.github.com> · 2026-05-15 17:23 UTC
Commit: ec941f20995b1ec8536f755a1ca86537a6468fda
Parent: efef6c1
1 file changed, +86 insertions, -0 deletions
@@ -13,6 +13,92 @@ Done: Phases 1-8, Git proxy A-D, UX audit (8/8), all remaining features. Deploye
13 13
14 14 ---
15 15
16 + ## OTA Publish Subcommand (Post-Beta)
17 +
18 + Replace `MNW/server/deploy/ota-publish.sh` with a typed Rust implementation. The bash script shells out to `curl` + `python3 -c "import json"` per artifact, fragile and not testable. Three apps (GO, BB, AF) will each publish ~9 artifacts per release; that's a lot of glue to maintain across repos.
19 +
20 + ### Goal
21 +
22 + One command:
23 + ```
24 + mnw ota publish --app goingson --version 0.3.1 [--dist ./dist] [--notes "..."]
25 + ```
26 + Discovers artifacts in the dist directory, reads Tauri signatures from `latest.json`, authenticates, and publishes every platform/arch artifact for the given version.
27 +
28 + ### Architecture
29 +
30 + **Layer 1 — `synckit-client::ota` module** (`MNW/shared/synckit-client/src/client/ota.rs`)
31 +
32 + Typed client wrapping the server's existing OTA endpoints. ~200 LOC + tests.
33 +
34 + ```rust
35 + pub struct OtaClient { /* reuses synckit auth + http */ }
36 +
37 + pub struct ReleaseManifest {
38 + pub app_slug: String,
39 + pub version: String,
40 + pub notes: Option<String>,
41 + pub artifacts: Vec<ArtifactEntry>,
42 + }
43 +
44 + pub struct ArtifactEntry {
45 + pub target: Target, // Linux | Darwin | Windows
46 + pub arch: Arch, // X86_64 | Aarch64
47 + pub path: PathBuf,
48 + pub signature: Option<String>, // Tauri minisign
49 + }
50 +
51 + impl OtaClient {
52 + pub async fn create_release(&self, app_id, version, notes, signature) -> Result<ReleaseId>;
53 + pub async fn register_artifact(&self, release_id, target, arch, size) -> Result<UploadUrl>;
54 + pub async fn upload(&self, upload_url, bytes) -> Result<()>;
55 + pub async fn verify_updater(&self, slug, target, arch) -> Result<bool>;
56 + /// High-level: takes a manifest, does the full publish loop with retry/resume.
57 + pub async fn publish(&self, manifest: ReleaseManifest) -> Result<PublishReport>;
58 + }
59 + ```
60 +
61 + Tests mock the HTTP layer (existing pattern in `client/sync.rs`). Cross-reference: `synckit-client/docs/todo.md`.
62 +
63 + **Layer 2 — `mnw-cli ota publish` subcommand** (`MNW/mnw-cli/src/commands.rs` + new `src/ota.rs`)
64 +
65 + ~150 LOC. Responsibilities:
66 +
67 + - Argument parsing (`--app`, `--version`, `--dist`, `--notes`, `--dry-run`)
68 + - **Artifact discovery** by convention:
69 + - `dist/{App}_{version}_x64.msi`, `_x64-setup.exe`, `_aarch64.dmg`, `_x86_64.AppImage`, `_aarch64.AppImage`, `_amd64.deb`, etc.
70 + - Or read from a `release.toml` manifest in the app repo (preferred for explicitness)
71 + - **Signature extraction** from Tauri's `latest.json` per platform (when present)
72 + - **Auth** via OS keyring (already used elsewhere in mnw-cli) with env-var fallback (`MNW_OTA_EMAIL/PASSWORD/API_KEY`)
73 + - **Progress UI** — line per artifact: `goingson 0.3.1 linux/x86_64 [====> ] 42 MB / 78 MB`
74 + - **Dry-run mode** — list what would be published, no network calls
75 + - **Idempotency** — re-running after a partial failure should skip already-uploaded artifacts (server returns 409 on duplicate artifact registration; treat as success)
76 +
77 + **Layer 3 — Per-app integration** (optional)
78 +
79 + Each app's `release.sh` (or a `cargo xtask release`) calls `mnw ota publish --app <slug> --version $(grep version Cargo.toml)`. No per-app Rust code needed.
80 +
81 + ### Cutover plan
82 +
83 + 1. Build `synckit-client::ota` with full test coverage. Server endpoints already exist — no server changes needed.
84 + 2. Build `mnw-cli ota publish` subcommand.
85 + 3. Verify against staging: publish a test release for `goingson` 0.3.1, check it appears in updater endpoint.
86 + 4. Delete `MNW/server/deploy/ota-publish.sh`.
87 + 5. Update each app's `docs/deploy.md` to reference `mnw ota publish`.
88 +
89 + ### Trigger / priority
90 +
91 + **Not blocking soft launch** — testers download from `~/Dist` directly during beta; no OTA updates planned in the first week. Build this when GO needs its first post-launch update, which is also when bugs surfaced by testers need to ship to them quickly.
92 +
93 + ### Estimate
94 +
95 + ~1 focused day total:
96 + - `synckit-client::ota` module + tests: ~4 hours
97 + - `mnw-cli ota publish` subcommand + discovery + progress UI: ~3 hours
98 + - Cutover + per-app docs update: ~1 hour
99 +
100 + ---
101 +
16 102 ## Key Paths
17 103 ```
18 104 mnw-cli/src/