Skip to content

sparkle modernization#1379

Open
robertobissanti wants to merge 27 commits into
MacDownApp:masterfrom
robertobissanti:sparkle2-gatekeeper-modernization
Open

sparkle modernization#1379
robertobissanti wants to merge 27 commits into
MacDownApp:masterfrom
robertobissanti:sparkle2-gatekeeper-modernization

Conversation

@robertobissanti

Copy link
Copy Markdown
  • Migrate Sparkle 1.x to 2.x, bump deployment target to macOS 12
  • Fix build tooling: path quoting, node-sass, stale lockfile
  • Migrate Markdown preview from WebView to WKWebView
  • Fix Auto Layout bug in Rendering preferences pane
  • Fix image drag & drop and preview image width
  • Add CHANGELOG and update README for this modernization pass

- Podfile: use_frameworks!, Sparkle ~> 2.9, MASPreferences ~> 1.4, PAPreferences ~> 0.5
- MPMainController.m: SUUpdater -> Sparkle.h, SPUUpdaterDelegate
- MainMenu.xib: SUUpdater -> SPUStandardUpdaterController
- project.pbxproj: MACOSX_DEPLOYMENT_TARGET 10.8 -> 12.0
- MacDown-Info.plist: note for EdDSA key migration

Part of Gatekeeper/notarization compliance (Sparkle 1.x is incompatible
with hardened runtime + notarization).
- Tools/update_build_number.sh: quote $(pwd -P) so it survives paths
  containing spaces (pre-existing bug, unrelated to the rest of this pass).
- Tools/GitHub-style-generator: migrate node-sass (deprecated, native
  bindings) to sass (Dart Sass); update Makefile flags accordingly.
- Gemfile: drop the dead travis gem, bump the cocoapods constraint.
- Delete the stale Gemfile.lock (pinned Bundler 1.17.3 / CocoaPods 1.10.1,
  both incompatible with current Ruby); regenerate with bundle install.
- Podfile.lock: sync with MASPreferences 1.4.1 / Sparkle 2.9.4.
Full replacement of the deprecated, legacy WebKit WebView with WKWebView
for the preview pane: navigation/policy delegates, JS<->native messaging
via WKScriptMessageHandler (replacing the WebScripting bridge), scroll
sync, zoom, MathJax completion signaling, printing, and word counting.

Also fixes two regressions this migration itself introduced along the way:

- MathJax not rendering: the script tag pointed at the bundled MathJax.js,
  which is only the bootstrap loader (the config/jax/extensions/fonts it
  fetches relative to its own script URL at runtime were never vendored
  locally). Reverted to loading it from the CDN.
- Preview HTML is now loaded via -loadFileURL:allowingReadAccessToURL:
  from a hidden sidecar file next to the document, instead of
  -loadHTMLString:baseURL:, since WKWebView restricts file:// sub-resource
  loads to the loaded page's own directory (this broke custom user styles
  and images referenced from outside the document's folder). Local
  stylesheets/scripts are now embedded inline in the HTML for the same
  reason. Print crash fixed by sizing the print operation's view, which
  WKWebView's printOperationWithPrintInfo: otherwise leaves at zero.

See CHANGELOG.md for the full list.
Graphviz/Mermaid checkboxes used a fixedFrame (absolute position, never
touched by Auto Layout) while the 'Show line numbers' row they're meant
to align with is constraint-driven; any layout drift desynced them.
Converted to proper constraints pinned to that row. Remaining pane
spacing fixed by hand in Interface Builder.
- Dropping an image into the editor used to read the whole file into
  memory and inline it as a base64 data: URI (mislabeling every file as
  image/jpeg regardless of its real type). Now inserts a plain ![](path)
  Markdown reference instead, for any image type.
- Preview images now always render at 100% of the pane's width, via a
  global rule in the base HTML template rather than per-image.
@robertobissanti

Copy link
Copy Markdown
Author

Travis CI check appears stale — this repo already migrated to Dependabot; the xcode10.1 image is unlikely to run

-openOrCreateFileForUrl: handed any resolved, reachable local link target
straight to -[NSWorkspace openURL:], including .app bundles and other
executables reached via an absolute file:// path or "../" traversal in
the previewed (possibly untrusted) Markdown source. A malicious document
could silently launch an arbitrary local application when its (disguised)
link was clicked.

- Check the resolved file's UTI and refuse to open anything conforming to
  public.application/public.executable, rather than pattern-matching the
  URL string (which encoding/traversal tricks could evade).
- Confine auto-created link targets (for links pointing at nonexistent
  files) to the current document's own folder, closing the other half of
  CVE-2019-12138 (creating files outside the document's directory via
  "../").
The old .travis.yml pinned osx_image: xcode10.1, several major
Xcode/macOS releases behind this project's current
MACOSX_DEPLOYMENT_TARGET, and Travis CI's free macOS build queue for open
source projects has been effectively dead for years -- the
continuous-integration/travis-ci required status check on PRs never
completes as a result. Replaced with .github/workflows/tests.yml.

Note: the required-checks list in this repo's branch protection settings
still needs a maintainer/admin to point it at the new workflow; that's
not something a PR can change.
- NSToolbarItem[Group].minSize/maxSize are deprecated (macOS 12): let the
  system measure the item via Auto Layout constraints on the segmented
  control's width/height instead of setting an explicit size.
- NSDragPboard is a deprecated pasteboard *name* constant that was being
  misused as a drag type/UTI in -registerForDraggedTypes:. Register the
  actual type the view's drag handlers care about (NSPasteboardTypeFileURL)
  instead.
Addresses the remaining warnings surfaced by a full build log (buildIssue_70666.txt):

- MPDocument.m: modernize the CVE-2019-12138/12173 executable-type check
  from CoreServices' kUTTypeApplication/kUTTypeExecutable/UTTypeConformsTo
  (deprecated macOS 12) to the UniformTypeIdentifiers UTType class; migrate
  NSSavePanel.allowedFileTypes (deprecated macOS 12) to allowedContentTypes
  in prepareSavePanel:/exportHtml:/exportPdf:; migrate
  NSFileHandlingPanelOKButton (deprecated macOS 10.13) to NSModalResponseOK;
  silence -Wdeprecated-declarations specifically around NSAutoPagination,
  since Apple's own deprecation note names no replacement API/constant.
- MPMainController.m, MPRenderer.m, MPUtilities.m: fix K&R-style C function
  declarations without (void) prototypes (deprecated in all C standards).
- MPUtilities.m: MPGetDataMap now uses
  +[NSKeyedUnarchiver unarchivedObjectOfClasses:fromData:error:] with an
  explicit allowed-class set instead of the insecure, deprecated
  +unarchiveObjectWithFile:.
- MPDocumentSplitView.m: colorUsingColorSpaceName: (deprecated macOS 10.14)
  -> colorUsingColorSpace: with an explicit NSColorSpace.
- MPGeneralPreferencesViewController.m: NSOnState (deprecated macOS 10.14)
  -> NSControlStateValueOn.
- macdown-cmd/main.m: fix two more (void) prototypes;
  stringByAddingPercentEscapesUsingEncoding: (deprecated macOS 10.11) ->
  stringByAddingPercentEncodingWithAllowedCharacters:; the deprecated
  launchAppWithBundleIdentifier:options:additionalEventParamDescriptor:
  launchIdentifier: + NSWorkspaceLaunchDefault pair (deprecated macOS 11)
  -> resolve the bundle identifier to a URL and use
  -openApplicationAtURL:configuration:completionHandler:.

Deliberately not touched: the insertText: deprecation (NSTextView+Autocomplete.m,
MPDocument.m) is flagged by Apple as intended only for the input system, not
application code -- replacing it needs a review of the call sites' actual intent
rather than a mechanical rename, so it's left as a known issue for now.
GBCli, handlebars-objc, hoedown, JJPluralForm, LibYAML, M13OrderedDictionary,
MASPreferences and PAPreferences all still ship MACOSX_DEPLOYMENT_TARGET
values as low as 10.6, below Xcode's currently supported minimum (10.13)
and this project's own target (12.0). Xcode warns on every build instead of
building those pod targets against a consistent deployment target. Force
every pod sub-target's deployment target up to 12.0 in a post_install hook,
matching the approach macdown3000 uses for the same dependency set.
- MacDown.xcodeproj: mark 'Update Build Number', 'Fetch Prism Resources',
  and 'Transpile Styles' as alwaysOutOfDate, since none of them declare
  outputs Xcode can use to skip them (and Update Build Number genuinely
  can't -- it depends on git state, not file contents).
- Podfile post_install: patch hoedown's vendored headers to use
  angle-bracketed sibling includes instead of quoted ones, silencing
  CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER now that hoedown builds as
  a framework. Verified end-to-end: ran a real 'pod install' and confirmed
  the hook re-patches Pods/hoedown/src/*.h and hoedown-umbrella.h, and that
  the earlier deployment-target hook applied 12.0 across all pod targets.
- Podfile.lock: checksum bump from the Podfile edits above (no dependency
  version changes).
Markdown pane: 8 'trailing constraint missing' warnings traced and found
to be false positives (widths fully determined via equal-width chains).
Editor pane: 4 'may cause clipping' warnings include at least one real,
pre-existing issue (font-preview field's leading edge pinned two
inconsistent ways depending on label text width) -- needs an Interface
Builder fix with visual verification, not a blind XML edit. Html pane
warnings are the already-documented ambiguous/misplaced residue from
earlier this session.
…rs-objc as unfixable here

- Generalized the post_install quoted-include patch into a reusable
  patch_quoted_framework_includes helper and applied it to
  PAPreferences-framework's umbrella header (same
  CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER warning as hoedown).
  Also live-patched the currently-installed copy so it's fixed without
  waiting on the next 'pod install'.
- Documented (not fixed) two static-analyzer warnings in handlebars-objc's
  generated parser files -- they live in DerivedData, regenerated from the
  pod's own grammar on every clean build, so there's no durable location
  in this repo to patch them from.
pmh_parser.c is generated at build time (gitignored) from
pmh_parser_head.c + pmh_parser_core.c + pmh_parser_foot.c via the
Makefile, so the fixes belong in pmh_parser_head.c (the actual tracked
source), not in the generated file -- editing the generated copy directly
would just get overwritten on the next build.

- get_element_type_names() -> (void) prototype.
- Two 'assign, then use as the ternary's value via a comma operator'
  idioms rewritten as plain if/else, avoiding the comma-operator misuse
  warning without changing behavior.
- pmh_styleparser.c: five more (void) prototype fixes
  (new_attr_value, new_font_styles, new_font_size, new_block,
  new_style_collection).
All 6 remaining call sites (5 in NSTextView+Autocomplete.m, 1 in
MPDocument.m) were plain 'insert at wherever the cursor/selection
currently is' cases, immediately adjacent in every file to other calls
that already use -insertText:replacementRange:. Converted each to the
same 2-arg NSTextInputClient method, passing NSMakeRange(NSNotFound, 0) --
Apple's documented sentinel meaning 'use the current selection, or the
marked (IME composition) range if there is one', which is exactly what
the deprecated 1-arg -insertText: did. This preserves IME/marked-text
behavior correctly, which plain self.selectedRange would not have during
active composition (this file already special-cases marked text
elsewhere, e.g. hasMarkedText in completeMatchingCharacterForText:atLocation:).
inhibit_all_warnings! suppresses warnings per-file (COMPILER_FLAGS on
each PBXBuildFile entry CocoaPods knows about at install time), but
handlebars-objc.yy.m and y.tab.c are produced at build time by an Xcode
Build Rule from .lm/.ym grammar sources, so CocoaPods never gets a chance
to attach a per-file override to them. Confirmed via the generated
xcconfig that the handlebars-objc target's OTHER_CFLAGS carried no
warning suppression at all -- added -Wno-everything there in
post_install, and live-patched the two already-generated xcconfig files
so it takes effect without waiting on the next 'pod install'.
…ained

Reported as a crash on the Underline toolbar action, but the bug wasn't
in the underline code path itself (toggleForMarkupPrefix:suffix: in
NSTextView+Autocomplete.m was untouched and correct) -- it was in how
MPDocument holds its reference to the editor view. 'editor' was the only
IBOutlet on the class declared unsafe_unretained; every sibling outlet
(toolbar, splitView, editorContainer, preview, ...) is weak. Traced via
git blame to the original 2014 project setup, never updated since.

unsafe_unretained is a raw, non-zeroing pointer: if the MPEditorView is
ever deallocated and recreated (window/layout churn), 'editor' keeps
pointing at freed memory, and the next message sent to it -- from any
action, not just Underline -- is a dangling-pointer access, i.e.
EXC_BAD_ACCESS. weak makes the pointer nil itself out instead, turning a
crash into a safe no-op.
…:from:

The actual root cause of the reported crash (backtrace showed objc_retain/
objc_storeStrong right at the callee's entry, not inside its body):
MPToolbarController's -selectedToolbarItemGroupItem: resolved the clicked
segment's action via -methodForSelector: and invoked the IMP through a
hand-cast 'void (*)(id)' function pointer -- one argument, when every
Objective-C method actually receives three (self, _cmd, sender). Calling
with the wrong signature left _cmd/sender as whatever garbage was in
those registers/stack slots; the callee's own sender parameter (__strong
under ARC) being retained against that garbage pointer is what crashed.

Replaced with [NSApp sendAction:to:from:], the same standard AppKit
dispatch mechanism already used a few lines away for the toolbar's
non-grouped buttons (itemButton.target/.action), which is proven to work
correctly against MPDocument as a plain explicit target.
@schuyler

schuyler commented Jul 7, 2026

Copy link
Copy Markdown

@robertobissanti if you'd be interested in contributing any of this code to https://github.com/schuyler/macdown3000 I would be glad to review and incorporate it!

GitHub
A modern, lightweight Markdown editor for macOS. Contribute to schuyler/macdown3000 development by creating an account on GitHub.

@robertobissanti

robertobissanti commented Jul 8, 2026

Copy link
Copy Markdown
Author

Hi @schuyler, thanks for reaching out!
I was actually about to write to you — last night I fixed a few bugs, following the CVEs from your repo too. Here's a rundown of what I've updated relative to upstream MacDown:

Security

Fixed CVE-2019-12138 and CVE-2019-12173 (arbitrary program execution / directory traversal via a crafted preview link), same approach you'd already shipped in macdown3000.

Moving off deprecated/dying platform tech

  1. WebView (WebKit1, long deprecated) → WKWebView for the preview pane.
  2. Sparkle 1.x → 2.x (EdDSA signing instead of DSA, XPC installer, hardened-runtime compatible — needed for any future notarization work).
  3. Travis CI → GitHub Actions (Travis's free macOS OSS queue has been dead for years).
  4. MACOSX_DEPLOYMENT_TARGET 10.8 → 12.0.
  5. CoreServices UTI calls (kUTTypeApplication, UTTypeConformsTo) → UniformTypeIdentifiers/UTType.
  6. Verified true universal binary (arm64 + x86_64), no Rosetta dependency — relevant given Apple's 2027 cutoff for Carbon/legacy-arch apps.
  7. Cleared a long list of AppKit/Foundation deprecations: NSToolbarItem.minSize/maxSize, NSDragPboard, allowedFileTypes, NSFileHandlingPanelOKButton, colorUsingColorSpaceName:, insertText:, unarchiveObjectWithFile:, K&R-style C prototypes across the codebase.
  8. Added a CocoaPods post_install hook to keep dependency deployment targets and framework-header warnings clean going forward (handlebars-objc, hoedown, PAPreferences).

UI fixes

  1. Fixed several Auto Layout / alignment bugs across the Preferences panes (rows overlapping in Rendering and General).
  2. Preview images now always render at 100% of the preview pane's width — done in the HTML rendering template itself rather than as inline per-image styles, so it applies uniformly regardless of how the image was inserted.
  3. Added drag & drop of images directly into the editor area — this was missing before.

Bugs

  1. Fixed two real crashes: an EXC_BAD_ACCESS in the toolbar's grouped formatting actions (hand-cast IMP function pointer calling with the wrong argument count) and a dangling-pointer IBOutlet (unsafe_unretained instead of weak).
  2. Fixed MathJax/local image/custom CSS breaking under WKWebView's stricter file:// sandboxing, plus a print crash.

One heads-up: the PR you're commenting on doesn't include all of the commits listed above — it's a snapshot from earlier. For the full, up-to-date set of changes, you'll want to check out and build my branch directly:
https://github.com/robertobissanti/macdown/tree/sparkle2-gatekeeper-modernization
Happy to open PRs against macdown3000 for whichever of this is useful once you've had a look — let me know what you'd want first. The CVE fix and CI migration are probably the most immediately relevant since you'll likely have hit the same issues.

GitHub
Open source Markdown editor for macOS. Contribute to robertobissanti/macdown development by creating an account on GitHub.

…r.xib

'location' and 'supportText' outlets are leftovers from a rename to
locationTextField/supportTextField that was done in code (MPTerminal
PreferencesViewController.m) but never cleaned up in the xib. Both stale
entries pointed at the same destination views as the correctly-named
outlets that are still wired, so this was harmless -- just a 'Failed to
connect outlet ... missing setter or instance variable' warning logged on
every nib load.
Xcode's Thread Performance Checker flagged a priority-inversion Hang Risk:
main thread (user-interactive QoS, switching Preferences panes) waiting on
a lock inside MASPreferences' -setSelectedViewController: while a
lower-QoS thread held it. The strongest candidate in our own code:
MPHomebrewSubprocessController (backs the Terminal pane's 'brew --prefix'
check, kicked off from -viewWillAppear) used the pre-GCD
-readToEndOfFileInBackgroundAndNotify + NSNotificationCenter pattern,
whose background read/notification delivery run at a QoS the app doesn't
control -- exactly the kind of thing that can cause this if a pane switch
happens while it's mid-flight.

Rewrote around NSTask.terminationHandler instead, explicitly dispatching
the completion callback back to the main queue (callers update
KVO-observed/bound UI properties from it). Capture self strongly in the
terminationHandler deliberately: the controller is created as a bare local
in MPDetectHomebrewPrefixWithCompletionhandler() with no other owner, so
this is what keeps it alive for the task's duration; NSTask releases its
terminationHandler after one invocation, so it's not a lasting cycle.

This is a well-reasoned diagnosis from reading the code, not a profiled
confirmation -- but a legitimate modernization on its own terms either
way.
self.task.terminationHandler = ^{ ...self... } is a genuine structural
cycle (self -> _task -> terminationHandler -> block -> self), correctly
flagged by the compiler -- my earlier comment claiming NSTask releases
terminationHandler after firing was an unverified assumption, not a
documented guarantee.

The strong self-capture is still deliberate and necessary: nothing else
keeps this controller alive between -runWithCompletionHandler: returning
and the task actually exiting. Break the cycle explicitly instead, as the
first thing the block does (task.terminationHandler = nil;), wrapped in a
narrowly-scoped -Warc-retain-cycles suppression with a comment explaining
why it's safe.
…ibution

- Podfile: patch MASPreferencesWindowController.m via post_install to force
  animate:NO on the Preferences window resize triggered by switching
  toolbar tabs. The animated resize hands off to Core Animation/WindowServer
  on a background-QoS thread, which the main thread then blocks on while
  committing setContentView:/recalculateKeyViewLoop -- the exact priority
  inversion Xcode's Thread Performance Checker flagged at
  MASPreferencesWindowController.m:222/311. The earlier
  MPHomebrewSubprocessController rewrite was a legitimate modernization but,
  per a recurrence of the same warning on a later run, was not the actual
  cause; CHANGELOG corrected accordingly.
- Added 'Portions Copyright (c) 2026 Roberto Bissanti.' to the header of
  every tracked source file modified in this fork so far.
- Xcode 'Update to Recommended Settings': bumped LastUpgradeVersion in the
  MacDown scheme, switched workspace BuildSystemType from Original to
  Latest, and related objectVersion/settings bumps in the
  peg-markdown-highlight and version sub-project pbxprojs.
- MPGeneralPreferencesViewController.xib: Auto Layout adjustments made
  directly in Interface Builder.
- Added Gemfile.lock (regenerated bundler lockfile) and an empty
  GitHub-2020.css placeholder under MacDown/Resources/Styles.

These were pending local/Xcode-driven changes not yet committed; grouped
here since they're editor/tooling state rather than a logical code change.
@schuyler

schuyler commented Jul 8, 2026

Copy link
Copy Markdown

@robertobissanti This is extremely cool. We've already addressed a bunch of these issues in MacDown 3000, but some of them are still hanging fire. In particular Sparkle 2 and WKWebView have been on the roadmap for a while. I had some concerns about whether WKWebView might break backwards compatibility in some way, but if the change is straightforward enough, maybe we should just go for it. If you'd be willing to file individual PRs for the changes that could be ported, that would be awesome. Otherwise I might have Claude Code take a crack at it over the weekend. Please let me know? Thanks!

@robertobissanti

Copy link
Copy Markdown
Author

@schuyler I was hoping for this suggestion. As for how to proceed, I’m already working on a three-way comparison between the original MacDown, MacDown3000 and my fork, to work out which files have been modified by both repositories and focus on those when making changes.

I’m already identifying the most significant improvements in MacDown3000 that I haven’t implemented.

In particular after digging into macdown3000's CHANGELOG in detail I identified this points:

  1. Scroll sync and the universal/Apple Silicon build are already solid on my side, so nothing to pull in there.
  2. Editor/UX (autosave, Insert Table, invisible-character toggle, selection word count, customizable toolbar) — I have basically none of this yet.
  3. New preview/editor themes (GitHub Dark Default, GitHub2 Dark, Google Docs, Gmail, GitHub Tomorrow) and custom Prism theme support.
  4. Markdown rendering improvements (Mermaid v11 with live re-render, slug-based heading IDs).
  5. Infrastructure/tests — still evaluating how much of your suite applies once WKWebView lands, since a chunk of it is scroll-sync/rendering-path specific and may need re-verification either way.
  6. Quick Look extension for Markdown files — don't have this at all.
  7. Code signing/notarization automation — probably needed on my end regardless, ties into my own Gatekeeper-compliance goal.
  8. Localizations (ru/ar/hi/uk/he, plus your de/sk fixes) — bringing these in, but near the bottom of my list.
  9. Homebrew Cask distribution I'm leaving out entirely — that's tied to your release identity, not something I'd try to duplicate.

One caveat: some of this (editor/preview fixes especially) is coupled to the current WebView pipeline. A few items might be redundant or need rework once WKWebView is in — I'll check as I go rather than assume everything ports cleanly.

I’ll be in touch as soon as possible.
In the meantime, try downloading my repository and building a version to test it.

Roberto.

P.S. I think we should find a better place to speak about our collab.

@schuyler

schuyler commented Jul 11, 2026

Copy link
Copy Markdown

@robertobissanti Agreed. I'm kind of astonished that Github doesn't offer some kind of messaging feature. My DMs are open on BlueSky: https://bsky.app/profile/schuyler.info ... I'm sure there are other options... I'm open to suggestions.

Bluesky Social
ICE is 👏 not law enforcement 👏 if it is not 👏 accountable to the law. 👏

Trans rights are human rights. 🏳️‍⚧️🏳️‍🌈

Matthew 25:37-40 ∙ CPE 1704 TKS ∙ ring a dong dillo

buttondown.com/erle ∙ gale8.net ∙ atgeo.org

macnotes added a commit to macnotes/macdown that referenced this pull request Jul 11, 2026
macnotes added a commit to macnotes/macdown that referenced this pull request Jul 11, 2026
schuyler added a commit to schuyler/macdown3000 that referenced this pull request Jul 13, 2026
Ports four low-risk fixes triaged from upstream
[MacDownApp/macdown#1379](MacDownApp/macdown#1379)
— Roberto Bissanti's Sparkle-modernization branch (GitHub
[@wltb](https://github.com/wltb)). Each fix was reviewed cold
independently before landing; the full suite is green (**1074 tests, 0
unexpected**).

## Fixes

| Commit | Upstream | What |
|--------|----------|------|
| `6494c90` | `05d5153` | **Fix EXC_BAD_ACCESS crash** —
`MPDocument.editor` IBOutlet was `unsafe_unretained`; now `weak`. |
| `806f6ef` | `2b09e4e` | **Deprecated `-insertText:` →
`-insertText:replacementRange:`** at 6 call sites, with 6 new tests. |
| `20ba916` | `9e62b44` + `c807ff7` | **Modernize
`MPHomebrewSubprocessController`** (`NSTask.terminationHandler`,
main-queue delivery, retain-cycle break) **and fix a real bug** — see
note below. |
| `4300766` | `604664b` | **Remove stale xib outlets** (`location`,
`supportText`) that logged a "Failed to connect outlet" warning on every
nib load. |

## Fix 3 is scope-upgraded (with approval)

Reviewing the Homebrew modernization surfaced a latent bug: `launchPath
= @"brew"` is a bare relative path, and `NSTask` never searches `PATH`,
so `-launch` threw `NSInvalidArgumentException` every time — the
Terminal preferences pane's Homebrew detection has **never actually
worked** (it always reported "not installed"). Beyond the faithful port,
this now resolves Homebrew's absolute path (`/opt/homebrew/bin/brew`,
then `/usr/local/bin/brew`) and adopts the non-deprecated
`executableURL` + `-launchAndReturnError:`. All three completion paths
(not-found, launch-failure, success) deliver the handler on the main
queue, since the caller mutates AppKit UI directly.

This is a **user-visible behavior change**: the Terminal preferences
pane will now correctly detect an installed Homebrew.

## Testing

- Full suite: **1074 tests, 0 unexpected failures** (the 76 failures are
the codebase's pre-existing `XCTExpectFailure` cases).
- New tests: 6 for the `-insertText:replacementRange:` migration; 3 for
the Homebrew controller (success delivers output on the main thread;
not-found delivers nil on the main thread even when invoked off-main;
the terminationHandler retain-cycle break holds) — the latter use a
test-only seam stubbing brew resolution with `/bin/echo` for determinism
without a real Homebrew install.

## Suggested follow-up (not in this PR)

Two test-only categories still redeclare `editor` as `unsafe_unretained`
(`MacDownTests/MPDocumentLifecycleTests.m:27`,
`MacDownTests/MPScrollSyncTests.m:29`), stale since the weak-outlet fix.
Confirmed harmless (ARC uses the primary class's `weak` synthesis and
the tests hold strong locals) but misleading — worth a one-line cleanup.

Ports by [@wltb](https://github.com/wltb) (Roberto Bissanti).

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants