bundle git via dugite-native (D-59)
macOS sandbox blocks Homebrew git execution (symlinks resolve to Cellar paths that SBPL cannot match). Bundled dugite-native — the same relocatable Git distribution GitHub Desktop ships. make dugite-fetch downloads the platform tarball to native/dugite/. Toolchain checks the bundled binary first, sets GIT_EXEC_PATH and GIT_TEMPLATE_DIR automatically. native/dugite/ is gitignored (57 MB compressed). Decision D-59 in tooling.md, dependency entry in licenses.yaml (GPL-2.0 for the git binary, MIT for dugite-native build scripts). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.6
parent
770048fbd2
commit
b0d9fab0c6
@@ -109,6 +109,18 @@ dependencies:
|
|||||||
the core PTY wrapper for libc bindings (socketpair, recvmsg with
|
the core PTY wrapper for libc bindings (socketpair, recvmsg with
|
||||||
SCM_RIGHTS, ioctl). Dart-team maintained; zero transitive deps.
|
SCM_RIGHTS, ioctl). Dart-team maintained; zero transitive deps.
|
||||||
|
|
||||||
|
- name: dugite-native (Git)
|
||||||
|
kind: native-binary
|
||||||
|
version: "2.53.0"
|
||||||
|
homepage: https://github.com/desktop/dugite-native
|
||||||
|
license: GPL-2.0 (Git), MIT (dugite-native build scripts)
|
||||||
|
purpose: >-
|
||||||
|
Self-contained, relocatable Git distribution for macOS/Linux/Windows.
|
||||||
|
Bundled so clide can run git inside the macOS app sandbox without
|
||||||
|
depending on Homebrew or Xcode Command Line Tools. Same approach
|
||||||
|
used by GitHub Desktop and Tower. Binary not committed — downloaded
|
||||||
|
at build time via `make dugite-fetch`.
|
||||||
|
|
||||||
- name: tree-sitter
|
- name: tree-sitter
|
||||||
kind: native-shared-lib
|
kind: native-shared-lib
|
||||||
version: "0.26.8"
|
version: "0.26.8"
|
||||||
|
|||||||
@@ -42,4 +42,13 @@ Toolchain, supply chain, CI, ignore strategy.
|
|||||||
- **Cross-reference:** [D-31](#d-31-prefer-zero-deps-exact-pin), [D-42](#d-42-dependencies-documented-in-licensesyaml).
|
- **Cross-reference:** [D-31](#d-31-prefer-zero-deps-exact-pin), [D-42](#d-42-dependencies-documented-in-licensesyaml).
|
||||||
- **Raised by:** 2026-04-23 format engine evaluation.
|
- **Raised by:** 2026-04-23 format engine evaluation.
|
||||||
|
|
||||||
|
### D-59: Bundled git via dugite-native
|
||||||
|
- **Date:** 2026-04-25
|
||||||
|
- **Decision:** Ship a self-contained Git binary from [dugite-native](https://github.com/desktop/dugite-native) (the same distribution GitHub Desktop bundles). Downloaded at build time via `make dugite-fetch`, stored under `native/dugite/`, gitignored. The `Toolchain` class resolves to the bundled binary first, falling back to system git on PATH.
|
||||||
|
- **Rationale:** The macOS app sandbox blocks execution of Homebrew-installed git (symlinks resolve to Cellar paths that SBPL cannot match without freezing rendering). `/usr/bin/git` is an xcrun shim that refuses to run inside a sandbox. Bundling dugite-native makes clide self-contained — no dependency on Homebrew, Xcode CLT, or system git. The approach is proven: GitHub Desktop, Tower, and other git GUI apps all bundle their own git for the same reason.
|
||||||
|
- **Alternatives rejected:** (R) libgit2 via FFI — missing porcelain commands (pull/push/rebase), no hooks, would require rewriting GitClient. (R) Build git from source — dugite-native already does this with better infra. (R) SBPL exceptions for Homebrew — `(subpath "/opt/homebrew")` for process-exec freezes Flutter rendering on macOS 26.
|
||||||
|
- **Cost:** ~57 MB download (~199 MB unpacked, stripped at build time). Must track dugite-native releases for security updates. GPL-2.0 (git binary) applies to the bundled artefact, not to clide's MIT code.
|
||||||
|
- **Cross-reference:** [D-31](#d-31-prefer-zero-deps-exact-pin), [D-42](#d-42-dependencies-documented-in-licensesyaml).
|
||||||
|
- **Raised by:** 2026-04-25 macOS sandbox investigation.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|||||||
@@ -10,4 +10,59 @@ class AppDelegate: FlutterAppDelegate {
|
|||||||
override func applicationSupportsSecureRestorableState(_ app: NSApplication) -> Bool {
|
override func applicationSupportsSecureRestorableState(_ app: NSApplication) -> Bool {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override func applicationDidFinishLaunching(_ notification: Notification) {
|
||||||
|
guard let window = NSApp.mainWindow ?? NSApp.windows.first,
|
||||||
|
let flutterVC = window.contentViewController as? FlutterViewController else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
let channel = FlutterMethodChannel(
|
||||||
|
name: "clide/window",
|
||||||
|
binaryMessenger: flutterVC.engine.binaryMessenger)
|
||||||
|
|
||||||
|
channel.setMethodCallHandler { (call, result) in
|
||||||
|
guard let window = NSApp.mainWindow ?? NSApp.windows.first else {
|
||||||
|
result(FlutterMethodNotImplemented)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
switch call.method {
|
||||||
|
case "pickDirectory":
|
||||||
|
let panel = NSOpenPanel()
|
||||||
|
panel.canChooseFiles = false
|
||||||
|
panel.canChooseDirectories = true
|
||||||
|
panel.allowsMultipleSelection = false
|
||||||
|
panel.canCreateDirectories = false
|
||||||
|
panel.prompt = "Open"
|
||||||
|
panel.message = "Select a project folder"
|
||||||
|
panel.beginSheetModal(for: window) { response in
|
||||||
|
if response == .OK, let url = panel.url {
|
||||||
|
result(url.path)
|
||||||
|
} else {
|
||||||
|
result(nil)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case "startDrag":
|
||||||
|
if let event = NSApp.currentEvent {
|
||||||
|
window.performDrag(with: event)
|
||||||
|
}
|
||||||
|
result(nil)
|
||||||
|
case "minimize":
|
||||||
|
window.miniaturize(nil)
|
||||||
|
result(nil)
|
||||||
|
case "maximize":
|
||||||
|
window.zoom(nil)
|
||||||
|
result(nil)
|
||||||
|
case "close":
|
||||||
|
window.close()
|
||||||
|
result(nil)
|
||||||
|
case "isMaximized":
|
||||||
|
result(window.isZoomed)
|
||||||
|
case "startResize":
|
||||||
|
result(nil)
|
||||||
|
default:
|
||||||
|
result(FlutterMethodNotImplemented)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user