From 62e813f58fdd0a98915d409c2d34d373e37a8e9e Mon Sep 17 00:00:00 2001 From: hl-valdemar Date: Sun, 11 Oct 2026 19:33:15 +0200 Subject: [PATCH] fix: restart development launcher after rebuilding --- Makefile | 2 +- README.md | 2 +- Scripts/run.swift | 34 ++++++++++++++++++++++++++++++++++ 3 files changed, 36 insertions(+), 2 deletions(-) create mode 100644 Scripts/run.swift diff --git a/Makefile b/Makefile index 489308d..7224196 100644 --- a/Makefile +++ b/Makefile @@ -10,7 +10,7 @@ build: codesign --force --sign - $(APP) run: build - open $(APP) + swift Scripts/run.swift "$(APP)" test: swift test diff --git a/README.md b/README.md index 89ca78c..11478c4 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@ Requires macOS 14 or later and a Swift 6 toolchain (Xcode 16 or later). make run ``` -This builds a release executable, assembles an ad-hoc-signed application at `.build/Conjure.app`, and opens it through Launch Services. You can also open that app directly in Finder. The local signature is for development, not notarized distribution. +This builds a release executable, assembles an ad-hoc-signed application at `.build/Conjure.app`, gracefully quits any running instance of that exact development bundle, and launches a fresh process through Launch Services. This ensures rebuilt code is loaded; simply opening an already-running app in Finder does not reload its executable. Other copies of Conjure are left running. The local signature is for development, not notarized distribution. ```sh make build # Build the application without opening it. diff --git a/Scripts/run.swift b/Scripts/run.swift new file mode 100644 index 0000000..9638610 --- /dev/null +++ b/Scripts/run.swift @@ -0,0 +1,34 @@ +import AppKit + +func fail(_ message: String) -> Never { + FileHandle.standardError.write(Data("\(message)\n".utf8)) + exit(1) +} + +guard CommandLine.arguments.count == 2 else { + fail("Usage: swift Scripts/run.swift ") +} +let target = URL(fileURLWithPath: CommandLine.arguments[1]).standardizedFileURL.resolvingSymlinksInPath() +let running = NSRunningApplication.runningApplications(withBundleIdentifier: "dev.conjure.launcher") + .filter { $0.bundleURL?.resolvingSymlinksInPath().path == target.path } +for app in running { + guard app.terminate() else { fail("Could not quit the existing Conjure instance.") } +} +for _ in 0..<100 { + if running.allSatisfy(\.isTerminated) { break } + // AppKit refreshes running-application state on the main run loop. + RunLoop.main.run(until: Date(timeIntervalSinceNow: 0.05)) +} +guard running.allSatisfy(\.isTerminated) else { + fail("Conjure did not quit; the rebuilt app was not launched. Quit it and try again.") +} + +let configuration = NSWorkspace.OpenConfiguration() +configuration.createsNewApplicationInstance = true +NSWorkspace.shared.openApplication(at: target, configuration: configuration) { app, error in + if let error { fail("Could not launch Conjure: \(error.localizedDescription)") } + guard let app else { fail("Launch Services did not return a running application.") } + print("Opened rebuilt Conjure (PID \(app.processIdentifier)).") + exit(0) +} +RunLoop.main.run()