Add isolated dev build (make run-app-dev)#510
Conversation
make run-app-dev)|
I don't know about this one, tbh 😅 do we really need this? |
|
Fair question 😄 — the itch this scratches: testing anything that touches persisted state (settings migrations, sidebar buckets, layout snapshots, worktree clone flows) means risking your real There's also a stability angle: I daily-drive Supacode for agent orchestration, and I've noticed running terminal sessions get flaky when two instances of the same app are open (haven't debugged it yet). Keeping in-flight changes in a separate app means the prod build I actually rely on to do the work stays untouched. On cost, I tried to keep it cheap and contained:
That said, if it's not worth the maintenance surface to you, I'm happy to trim. Options:
Whatever you prefer — no attachment here. |
187cbb3 to
83af553
Compare
|
Thanks for the pull request. It doesn't meet the contribution policy yet, so I've labeled it
Nothing needs to be redone. A pull request left |
83af553 to
d23034d
Compare
|
@sbertix to bring this PR into compliance with the policies, I opened an issue for this feature and added more details on why it is necessary. |
Build and launch "Supacode Dev" as a dedicated Xcode target alongside the prod app: own bundle id (app.supabit.supacode.dev), name, blueprint icon, and supacode-dev:// scheme. State lives under ~/.supacode-dev and Sparkle is disabled, so it can be tested without touching a real install. Dev behaviour is decided at runtime from the .dev bundle id (SupacodePaths.isDevelopmentBuild) rather than a compile flag, which would force a non-standard Xcode configuration and break SPM module resolution for the CLI target. Both builds accept supacode:// so the embedded CLI keeps working over the socket.
…rminal zmx exports ZMX_SESSION=<name> into every shell it spawns (it marks which session a process is running inside). Its `attach` command checks that env var first: when ZMX_SESSION is set, it ignores the requested session id and tries to *switch* to the env-named session instead. Launching Supacode from inside a Supacode terminal (the common case when running `make run-app-dev` / `make dev` from your daily-driver install) leaks the parent surface's ZMX_SESSION into the new app process. Every surface the new app spawns then runs `zmx attach supa-<new-id>`, but zmx reroutes it to the inherited parent session `supa-<old-id>`, which it can't find — so the surface dies with `error: session "supa-…" does not exist`. The two mismatched UUIDs in that error (requested vs. inherited) are the tell. Drop the inherited ZMX_SESSION once at app launch, before any surface spawns. zmx re-injects the correct per-session value into each shell it spawns, so removing the stale inherited one is safe and surfaces still get their own id. Safe for prod: nothing in Supacode reads ZMX_SESSION; on the normal launch path (Dock/Finder) the var isn't set so this is a no-op; the only path where it's inherited is launch-from-inside-a-terminal, where prod has the same latent bug this also fixes.
pkill immediately followed by open raced: the old Dev app was still mid-termination, so open tried to reactivate the dying instance and LaunchServices returned procNotFound (-600). open -n forces a new instance instead.
The dev app now binds its CLI socket under /tmp/supacode-dev-<uid> instead of sharing /tmp/supacode-<uid> with prod (gated on the same runtime SupacodePaths.isDevelopmentBuild check as the rest of the dev isolation). The CLI binary is shared between the two app targets, so it derives dev-ness at runtime from its own executable path — the bundle id of the enclosing .app — and resolves only its own build's socket directory. The cold-launch fallback now opens the app bundle the CLI is embedded in rather than 'open -a Supacode', so the dev CLI cold-launches the dev app. With both apps running, 'supacode' outside a terminal session no longer fails with 'Multiple Supacode instances found'. The supacode-cli target moves from a buildable folder to an explicit source glob so supacodeTests can compile SocketDiscovery.swift directly (a command line tool has no importable module).
pkill by product name ('Supacode Dev.app/Contents/MacOS/') or by target
name ('watchexec.*run-app-dev') matches processes launched from any
worktree of this repo, so a relaunch or a new watcher in one worktree
killed the dev app / watcher belonging to another.
Anchor the relaunch kill on the worktree's own built-products path, and
put absolute --watch paths on the watchexec command line so the watcher
kill can anchor on the worktree directory.
09fbac7 to
a14f655
Compare
|
A variation of this, if not exactly this, is definitely coming. That said, to align expectations further: although I can confidently say this is eventually being merged, please do not expect this to be prioritized any further at this point ("it's not gonna happen today"). |
|
Closing this because it has been labeled |
Closes #635
Summary
Dogfooding an in-development build today means running it against your real
~/.supacode— the dev build and the daily-driver install share the bundle id, data directory, zmx sessions, deeplink routing, Sparkle updater, and CLI socket directory, so the safest workflow is "quit the app you use all day and hope nothing corrupts" (see #635 for the full failure-mode audit).This adds Supacode Dev, an isolated copy that can run alongside a real install:
app.supabit.supacode.dev), name/icon "Supacode Dev", andsupacode-dev://URL scheme — separate UserDefaults domain and LaunchServices/notification/deeplink identity.~/.supacode-dev(settings, sidebar, layouts, worktree clones), so persisted files and restored zmx sessions never collide with prod./tmp/supacode-dev-<uid>instead of sharing/tmp/supacode-<uid>. The bundled CLI (shared binary, no separate product) derives dev-ness at runtime from its own executable path — the bundle id of the enclosing.app— resolves only its own build's socket directory, and cold-launches the app bundle it is embedded in. Each build'ssupacodealways reaches its own app, and running both apps no longer breaks the CLI with "Multiple Supacode instances found".make run-app-dev(build + launch),make build-app-dev,make dev(watch, rebuild, relaunch via watchexec),make log-stream-dev.Implementation notes:
supacode-dev) on the standardDebugconfiguration, produced by the samesupacodeAppTargetfactory as prod. A custom build configuration breaks thesupacode-clitarget (Tuist generates external SPM projects with onlyDebug/Release), and a command-linePRODUCT_NAMEoverride collides the shared frameworks — both were tried. Frameworks, GhosttyKit, and the CLI stay single shared targets, so only the app sources compile twice, and the prod target is byte-for-byte unchanged (Info.plistreads name/icon/scheme via$(VAR:default=…))..devbundle id suffix (SupacodePaths.isDevelopmentBuild), not a compile flag, which would force the broken custom configuration.supacode-dev://for OS routing but accepts both schemes, so the CLI'ssupacode://payloads work against either build.Type of change
ready)How was this tested?
Ran the full local gates on this branch after rebasing onto
main, plus new unit tests for the CLI socket-directory resolution (SocketDiscoveryTests: enclosing-bundle detection, dev/prod directory choice, bare-binary and symlinked-CLI fallbacks). Verified manually that the dev app and a prod install run side by side with independent state and that each build's CLI reaches its own app.make checkpasses (format + lint)make testpassesChecklist
Closes #above.ready.