Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 13 additions & 3 deletions macos/OpenMessage/Sources/BackendLauncherCore.swift
Original file line number Diff line number Diff line change
Expand Up @@ -54,18 +54,28 @@ struct BackendLaunchConfiguration: Equatable, Sendable {
dataDirectory: String,
port: Int,
homeDirectory: String = NSHomeDirectory(),
v2Primary: Bool = false,
additionalEnvironment: [String: String] = [:]
) -> BackendLaunchConfiguration {
var environment = additionalEnvironment
environment.merge([
var canonical = [
"OPENMESSAGES_PORT": String(port),
"OPENMESSAGES_DATA_DIR": dataDirectory,
"OPENMESSAGES_LOG_LEVEL": "info",
"OPENMESSAGES_APP_SANDBOX": "1",
"OPENMESSAGES_MACOS_NOTIFICATIONS": "0",
"HOME": homeDirectory,
"PATH": "/opt/homebrew/bin:/usr/local/bin:/usr/bin:/bin",
]) { _, canonical in canonical }
]
if v2Primary {
// Operator lever for the R8 cutover: the backend selects the
// migrated v2 store only when this env var is set, and it refuses
// to start in primary mode if <dataDir>/v2 is missing, so setting
// it on an unmigrated install fails loudly rather than silently
// serving an empty store. Rollback = clear the defaults key.
canonical["OPENMESSAGES_V2_PRIMARY"] = "1"
}
var environment = additionalEnvironment
environment.merge(canonical) { _, canonical in canonical }

let dataDirectoryURL = URL(fileURLWithPath: dataDirectory, isDirectory: true)
return BackendLaunchConfiguration(
Expand Down
7 changes: 6 additions & 1 deletion macos/OpenMessage/Sources/BackendManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -164,10 +164,15 @@ final class BackendManager: ObservableObject {
}
let path = binaryPath
let dir = dataDir
// R8 operator lever: `defaults write com.openmessage.app V2Primary -bool true`
// routes the backend at the migrated v2 store; deleting the key rolls back
// to the legacy store on next launch (the legacy source is never modified).
let v2Primary = UserDefaults.standard.bool(forKey: "V2Primary")
let configuration = BackendLaunchConfiguration.application(
executablePath: path,
dataDirectory: dir,
port: port
port: port,
v2Primary: v2Primary
)
let launcher = BackendLauncherCore(configuration: configuration, processSpawner: processSpawner)
self.launcher = launcher
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,30 @@ final class BackendLaunchConfigurationTests: XCTestCase {
)
}

func testV2PrimaryLeverAddsExactlyOneEnvironmentVariable() {
let base = BackendLaunchConfiguration.application(
executablePath: "/Applications/OpenMessage.app/Contents/Resources/openmessage",
dataDirectory: "/Users/example/Library/Application Support/OpenMessage",
port: 8123,
homeDirectory: "/Users/example"
)
let primary = BackendLaunchConfiguration.application(
executablePath: "/Applications/OpenMessage.app/Contents/Resources/openmessage",
dataDirectory: "/Users/example/Library/Application Support/OpenMessage",
port: 8123,
homeDirectory: "/Users/example",
v2Primary: true
)

// Default stays byte-identical to the shipped contract: the lever off
// must not perturb the environment in any way.
XCTAssertNil(base.environment["OPENMESSAGES_V2_PRIMARY"])
XCTAssertEqual(primary.environment["OPENMESSAGES_V2_PRIMARY"], "1")
var expected = base.environment
expected["OPENMESSAGES_V2_PRIMARY"] = "1"
XCTAssertEqual(primary.environment, expected)
}

func testAdditionalEnvironmentIsExplicitWithoutOverridingCanonicalFields() {
let configuration = BackendLaunchConfiguration.application(
executablePath: "/tmp/openmessage",
Expand Down
Loading