Skip to content

Fix gitx CLI crash when GitX is not running (Sparkle Updater.app steals the bundle identifier) - #560

Merged
hannesa2 merged 2 commits into
gitx:masterfrom
voidbranch:fix-sparkle-updater-bundle-id
Jul 23, 2026
Merged

hannesa2 merged 2 commits into
gitx:masterfrom
voidbranch:fix-sparkle-updater-bundle-id

Conversation

@voidbranch

Copy link
Copy Markdown
Contributor

Symptom

Since recent releases, running gitx from the command line crashes whenever GitX.app is not already running:

warning: failed to get scripting definition from /Applications/GitX.app/Contents/Frameworks/Sparkle.framework/Versions/B/Updater.app; it may not be scriptable.
*** Terminating app due to uncaught exception 'NSInvalidArgumentException',
reason: '-[SBApplication open:withOptions:]: unrecognized selector sent to instance ...'

Launching the app first and re-running gitx works, which is the tell-tale for the root cause below.

Root cause

The archive step in BuildPR.yml passes PRODUCT_BUNDLE_IDENTIFIER=net.phere.GitX on the xcodebuild command line (added in da70aa5). A command-line build setting overrides every target in the workspace, including Sparkle's Updater.app built from the submodule. Since the Sparkle 2.9.3 bump (#556 / 68f8324), Updater.app's Info.plist takes its identifier from $(PRODUCT_BUNDLE_IDENTIFIER), so shipped bundles contain two bundles claiming net.phere.GitX:

  • /Applications/GitX.app (the real app), and
  • /Applications/GitX.app/Contents/Frameworks/Sparkle.framework/Versions/B/Updater.app

The CLI resolves the app with [SBApplication applicationWithBundleIdentifier:@"net.phere.GitX"]. When the app is not running, LaunchServices resolves that identifier ambiguously and can return the Updater.app — which has no scripting definition (hence the warning), so the Scripting Bridge proxy only knows the standard suite and open:withOptions: throws unrecognized selector. When the app is already running, SBApplication binds to the running process and everything works.

Fix (two commits)

  1. Drop the command-line override. It is redundant: every target already sets its own PRODUCT_BUNDLE_IDENTIFIER in project.pbxproj (net.phere.GitX for the app, net.phere.gitx-cli for the CLI). A comment in the workflow explains why it must not come back.
  2. Harden the CLI. Since gitx ships at GitX.app/Contents/Resources/gitx, derive the app URL from the tool's own symlink-resolved path and use applicationWithURL: (falling back to the bundle-identifier lookup when running outside a bundle, e.g. from the build directory). This also makes new CLIs immune to the broken bundles installed by existing releases, which will remain on users' disks until they update.

Verified locally: with the identifier override, Updater.app/Contents/Info.plist in the shipped 1.5-HEAD app contains net.phere.GitX (upstream Sparkle uses org.sparkle-project.Sparkle.Updater), and the crash reproduces 100% from a cold start.

Passing PRODUCT_BUNDLE_IDENTIFIER=net.phere.GitX on the xcodebuild
command line overrides the setting for every target in the workspace,
including Sparkle's Updater.app built from the submodule. Since the
Sparkle 2.9.3 bump, Updater.app's Info.plist takes its identifier from
that build setting, so released bundles contain two bundles claiming
net.phere.GitX: the app itself and
GitX.app/Contents/Frameworks/Sparkle.framework/Versions/B/Updater.app.

When the gitx CLI then asks Scripting Bridge for net.phere.GitX while
the app is not running, LaunchServices can resolve to the Updater.app,
which has no scripting definition, and the CLI crashes:

    warning: failed to get scripting definition from
    .../Sparkle.framework/Versions/B/Updater.app; it may not be scriptable.
    *** Terminating app due to uncaught exception 'NSInvalidArgumentException',
    reason: '-[SBApplication open:withOptions:]: unrecognized selector ...'

The override is redundant anyway: every target already sets its own
PRODUCT_BUNDLE_IDENTIFIER in project.pbxproj (net.phere.GitX for the
app, net.phere.gitx-cli for the CLI).
The CLI ships inside the app bundle at GitX.app/Contents/Resources/gitx,
so derive the app's URL from the tool's own (symlink-resolved) path and
target it with -[SBApplication applicationWithURL:]. Resolving by bundle
identifier is fragile: if any other bundle on disk claims net.phere.GitX
(as Sparkle's embedded Updater.app did in releases built with a global
PRODUCT_BUNDLE_IDENTIFIER override), LaunchServices can return a bundle
without our scripting definition and every scripting call crashes with
'-[SBApplication open:withOptions:]: unrecognized selector'.

The bundle-identifier lookup is kept as a fallback for running the tool
outside an app bundle (e.g. straight from the build directory).

This makes the CLI immune to broken bundles already installed by
existing releases, complementing the workflow fix in the previous
commit.
@hannesa2
hannesa2 merged commit 8951252 into gitx:master Jul 23, 2026
2 checks passed
@hannesa2

hannesa2 commented Jul 23, 2026 •

Copy link
Copy Markdown
Contributor

Sorry, I've to revert this. The merge to master shows me this:
image

Next time, I've to make same PR on upstream, to see if it's working

@hannesa2

hannesa2 commented Jul 23, 2026 •

Copy link
Copy Markdown
Contributor

Ahh, it's a general issue. Sorry for the noise

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants