From 9a60b1d2dbb1ca172ef87f094887eb9e95fd1819 Mon Sep 17 00:00:00 2001 From: ddidderr Date: Mon, 12 Oct 2026 21:11:39 +0200 Subject: [PATCH] fix(os): find in-repo resources from release builds run in the workspace Testing with a real user config showed that `target/release/ghostty` and `target/production/ghostty` (also `cargo run --profile production`, which the README recommends) silently ran without resources: the configured theme was not applied, bash shell integration was disabled ("no resources dir set") so tab titles, the prompt cursor and cwd inheritance for new tabs/splits broke, and terminfo was missing. Zig never hits this because `zig build` installs `zig-out/share/ghostty` next to `zig-out/bin`, which the exe-relative sentinel search finds. Cargo has no such tree; the Rust-specific fallback to `/resources` was limited to the dev profile. It now applies to every profile. It only triggers when the exe lives inside the workspace it was built from, after GHOSTTY_RESOURCES_DIR and the share/ sentinel search, so installed binaries are unaffected. Test Plan: - cargo test -p ghostty-os (dev_fallback_finds_workspace_resources now covers release builds and env precedence) - run target/production/ghostty with `theme = Catppuccin Mocha` and `shell-integration = bash`: theme applied, titles show the cwd --- README.md | 6 +++--- crates/ghostty-os/src/resourcesdir.rs | 20 ++++++++++++-------- 2 files changed, 15 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index e1b997b..94733aa 100644 --- a/README.md +++ b/README.md @@ -48,9 +48,9 @@ cargo run -- +list-themes # CLI actions work like Ghostty's cargo run --profile production # optimized build (LTO, no debug assertions) ``` -Dev-profile builds (`cargo run`) find `resources/` (shell integration, -themes, terminfo) automatically; other profiles use the installed -resources or `GHOSTTY_RESOURCES_DIR`. Your existing Ghostty config +Binaries run from this checkout's `target/` (any profile) find `resources/` +(shell integration, themes, terminfo) automatically; installed binaries use +the installed resources or `GHOSTTY_RESOURCES_DIR`. Your existing Ghostty config (`~/.config/ghostty/config[.ghostty]`) is used as-is. ## Installing diff --git a/crates/ghostty-os/src/resourcesdir.rs b/crates/ghostty-os/src/resourcesdir.rs index 1407471..0f84c1c 100644 --- a/crates/ghostty-os/src/resourcesdir.rs +++ b/crates/ghostty-os/src/resourcesdir.rs @@ -143,12 +143,12 @@ fn resources_dir_impl(debug: bool, exe: Option<&Path>, env: Option) -> }; } - // Rust-specific: development builds run out of `/target`, where - // there is no installed `share/` tree. Fall back to the in-repo - // `/resources` directory (it carries the same sentinel, - // `terminfo/x/xterm-ghostty`, see scripts/gen-terminfo.sh) if the binary - // lives inside this workspace. - if debug && let Some(dir) = dev_resources_dir(exe_start) { + // Rust-specific: builds of any profile run out of `/target`, + // where there is no `share/` tree (Zig installs one into `zig-out/share` + // next to `zig-out/bin`). Fall back to the in-repo `/resources` + // directory (it carries the same sentinel, `terminfo/x/xterm-ghostty`, see + // scripts/gen-terminfo.sh) if the binary lives inside this workspace. + if let Some(dir) = dev_resources_dir(exe_start) { return ResourcesDir { app_path: Some(enc(&dir)), host_path: None, @@ -261,9 +261,13 @@ mod tests { // Outside the workspace there is no fallback. let r = resources_dir_impl(true, Some(Path::new("/nonexistent/bin/x")), None); assert_eq!(r.app().map(EncodedOs::as_encoded), None); - // Release builds never use it. + // Release builds run from the workspace use it too... + let exe = workspace_root().join("target/production/ghostty"); let r = resources_dir_impl(false, Some(&exe), None); - assert_eq!(r.app().map(EncodedOs::as_encoded), None); + assert!(r.app().is_some_and(|d| d.ends_with_plain("/resources"))); + // ...but GHOSTTY_RESOURCES_DIR still takes precedence there. + let r = resources_dir_impl(false, Some(&exe), Some("/env/res".into())); + assert_eq!(r.app_os().unwrap(), Path::new("/env/res")); } #[test]