Require launch tag for cmux DEV
This commit is contained in:
parent
7a4d986d85
commit
f617032ad5
5 changed files with 117 additions and 4 deletions
|
|
@ -163,6 +163,8 @@ struct SocketControlSettings {
|
|||
static let legacyEnabledKey = "socketControlEnabled"
|
||||
static let allowSocketPathOverrideKey = "CMUX_ALLOW_SOCKET_OVERRIDE"
|
||||
static let socketPasswordEnvKey = "CMUX_SOCKET_PASSWORD"
|
||||
static let launchTagEnvKey = "CMUX_TAG"
|
||||
static let baseDebugBundleIdentifier = "com.cmuxterm.app.debug"
|
||||
|
||||
private static func normalizeMode(_ raw: String) -> String {
|
||||
raw
|
||||
|
|
@ -211,6 +213,53 @@ struct SocketControlSettings {
|
|||
#endif
|
||||
}
|
||||
|
||||
static func launchTag(
|
||||
environment: [String: String] = ProcessInfo.processInfo.environment
|
||||
) -> String? {
|
||||
guard let raw = environment[launchTagEnvKey] else { return nil }
|
||||
let trimmed = raw.trimmingCharacters(in: .whitespacesAndNewlines)
|
||||
return trimmed.isEmpty ? nil : trimmed
|
||||
}
|
||||
|
||||
static func shouldBlockUntaggedDebugLaunch(
|
||||
environment: [String: String] = ProcessInfo.processInfo.environment,
|
||||
bundleIdentifier: String? = Bundle.main.bundleIdentifier,
|
||||
isDebugBuild: Bool = SocketControlSettings.isDebugBuild
|
||||
) -> Bool {
|
||||
guard isDebugBuild else { return false }
|
||||
if isRunningUnderXCTest(environment: environment) {
|
||||
return false
|
||||
}
|
||||
|
||||
guard let bundleIdentifier = bundleIdentifier?.trimmingCharacters(in: .whitespacesAndNewlines),
|
||||
!bundleIdentifier.isEmpty else {
|
||||
return false
|
||||
}
|
||||
|
||||
if bundleIdentifier.hasPrefix("\(baseDebugBundleIdentifier).") {
|
||||
return false
|
||||
}
|
||||
|
||||
guard bundleIdentifier == baseDebugBundleIdentifier else {
|
||||
return false
|
||||
}
|
||||
|
||||
return launchTag(environment: environment) == nil
|
||||
}
|
||||
|
||||
static func isRunningUnderXCTest(environment: [String: String]) -> Bool {
|
||||
let indicators = [
|
||||
"XCTestConfigurationFilePath",
|
||||
"XCTestBundlePath",
|
||||
"XCTestSessionIdentifier",
|
||||
"XCInjectBundleInto",
|
||||
]
|
||||
return indicators.contains { key in
|
||||
guard let value = environment[key] else { return false }
|
||||
return !value.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty
|
||||
}
|
||||
}
|
||||
|
||||
static func socketPath(
|
||||
environment: [String: String] = ProcessInfo.processInfo.environment,
|
||||
bundleIdentifier: String? = Bundle.main.bundleIdentifier,
|
||||
|
|
|
|||
|
|
@ -35,6 +35,10 @@ struct cmuxApp: App {
|
|||
@NSApplicationDelegateAdaptor(AppDelegate.self) private var appDelegate
|
||||
|
||||
init() {
|
||||
if SocketControlSettings.shouldBlockUntaggedDebugLaunch() {
|
||||
Self.terminateForMissingLaunchTag()
|
||||
}
|
||||
|
||||
Self.configureGhosttyEnvironment()
|
||||
|
||||
let startupAppearance = AppearanceSettings.resolvedMode()
|
||||
|
|
@ -58,6 +62,14 @@ struct cmuxApp: App {
|
|||
appDelegate.configure(tabManager: tabManager, notificationStore: notificationStore, sidebarState: sidebarState)
|
||||
}
|
||||
|
||||
private static func terminateForMissingLaunchTag() -> Never {
|
||||
let message = "error: refusing to launch untagged cmux DEV; start with ./scripts/reload.sh --tag <name> (or set CMUX_TAG for test harnesses)"
|
||||
fputs("\(message)\n", stderr)
|
||||
fflush(stderr)
|
||||
NSLog("%@", message)
|
||||
Darwin.exit(64)
|
||||
}
|
||||
|
||||
private static func configureGhosttyEnvironment() {
|
||||
let fileManager = FileManager.default
|
||||
let ghosttyAppResources = "/Applications/Ghostty.app/Contents/Resources/ghostty"
|
||||
|
|
|
|||
|
|
@ -691,6 +691,56 @@ final class SocketControlSettingsTests: XCTestCase {
|
|||
"/tmp/cmux-staging.sock"
|
||||
)
|
||||
}
|
||||
|
||||
func testUntaggedDebugBundleBlockedWithoutLaunchTag() {
|
||||
XCTAssertTrue(
|
||||
SocketControlSettings.shouldBlockUntaggedDebugLaunch(
|
||||
environment: [:],
|
||||
bundleIdentifier: "com.cmuxterm.app.debug",
|
||||
isDebugBuild: true
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
func testUntaggedDebugBundleAllowedWithLaunchTag() {
|
||||
XCTAssertFalse(
|
||||
SocketControlSettings.shouldBlockUntaggedDebugLaunch(
|
||||
environment: ["CMUX_TAG": "tests-v1"],
|
||||
bundleIdentifier: "com.cmuxterm.app.debug",
|
||||
isDebugBuild: true
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
func testTaggedDebugBundleAllowedWithoutLaunchTag() {
|
||||
XCTAssertFalse(
|
||||
SocketControlSettings.shouldBlockUntaggedDebugLaunch(
|
||||
environment: [:],
|
||||
bundleIdentifier: "com.cmuxterm.app.debug.tests-v1",
|
||||
isDebugBuild: true
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
func testReleaseBuildIgnoresLaunchTagGate() {
|
||||
XCTAssertFalse(
|
||||
SocketControlSettings.shouldBlockUntaggedDebugLaunch(
|
||||
environment: [:],
|
||||
bundleIdentifier: "com.cmuxterm.app.debug",
|
||||
isDebugBuild: false
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
func testXCTestLaunchIgnoresLaunchTagGate() {
|
||||
XCTAssertFalse(
|
||||
SocketControlSettings.shouldBlockUntaggedDebugLaunch(
|
||||
environment: ["XCTestConfigurationFilePath": "/tmp/fake.xctestconfiguration"],
|
||||
bundleIdentifier: "com.cmuxterm.app.debug",
|
||||
isDebugBuild: true
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
final class PostHogAnalyticsPropertiesTests: XCTestCase {
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ cd "$(dirname "$0")/.."
|
|||
|
||||
DERIVED_DATA_PATH="$HOME/Library/Developer/Xcode/DerivedData/cmux-tests-v1"
|
||||
APP="$DERIVED_DATA_PATH/Build/Products/Debug/cmux DEV.app"
|
||||
RUN_TAG="tests-v1"
|
||||
|
||||
echo "== build =="
|
||||
# Work around stale explicit-module cache artifacts (notably Sentry headers) that can
|
||||
|
|
@ -51,7 +52,7 @@ launch_and_wait() {
|
|||
defaults write com.cmuxterm.app.debug socketControlMode -string full >/dev/null 2>&1 || true
|
||||
|
||||
# Launch directly with UI test mode enabled so startup follows deterministic test codepaths.
|
||||
CMUX_UI_TEST_MODE=1 "$APP/Contents/MacOS/cmux DEV" >/dev/null 2>&1 &
|
||||
CMUX_TAG="$RUN_TAG" CMUX_UI_TEST_MODE=1 "$APP/Contents/MacOS/cmux DEV" >/dev/null 2>&1 &
|
||||
|
||||
SOCK=""
|
||||
for _ in {1..120}; do
|
||||
|
|
@ -70,7 +71,7 @@ launch_and_wait() {
|
|||
export CMUX_SOCKET="$SOCK"
|
||||
|
||||
# Ensure LaunchServices has a visible/main window attached for rendering checks.
|
||||
open "$APP" >/dev/null 2>&1 || true
|
||||
CMUX_TAG="$RUN_TAG" open "$APP" >/dev/null 2>&1 || true
|
||||
sleep 0.5
|
||||
|
||||
echo "== wait ready =="
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ cd "$(dirname "$0")/.."
|
|||
|
||||
DERIVED_DATA_PATH="$HOME/Library/Developer/Xcode/DerivedData/cmux-tests-v2"
|
||||
APP="$DERIVED_DATA_PATH/Build/Products/Debug/cmux DEV.app"
|
||||
RUN_TAG="tests-v2"
|
||||
|
||||
echo "== build =="
|
||||
# Work around stale explicit-module cache artifacts (notably Sentry headers) that can
|
||||
|
|
@ -51,7 +52,7 @@ launch_and_wait() {
|
|||
defaults write com.cmuxterm.app.debug socketControlMode -string full >/dev/null 2>&1 || true
|
||||
|
||||
# Launch directly with UI test mode enabled so startup follows deterministic test codepaths.
|
||||
CMUX_UI_TEST_MODE=1 "$APP/Contents/MacOS/cmux DEV" >/dev/null 2>&1 &
|
||||
CMUX_TAG="$RUN_TAG" CMUX_UI_TEST_MODE=1 "$APP/Contents/MacOS/cmux DEV" >/dev/null 2>&1 &
|
||||
|
||||
SOCK=""
|
||||
for _ in {1..120}; do
|
||||
|
|
@ -70,7 +71,7 @@ launch_and_wait() {
|
|||
export CMUX_SOCKET="$SOCK"
|
||||
|
||||
# Ensure LaunchServices has a visible/main window attached for rendering checks.
|
||||
open "$APP" >/dev/null 2>&1 || true
|
||||
CMUX_TAG="$RUN_TAG" open "$APP" >/dev/null 2>&1 || true
|
||||
sleep 0.5
|
||||
|
||||
echo "== wait ready =="
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue