Skip to content

Commit 0e9ceef

Browse files
authored
fix: resolve and bundle daemon binary on macOS (#514)
1 parent e90a723 commit 0e9ceef

4 files changed

Lines changed: 104 additions & 9 deletions

File tree

.github/workflows/release.yml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,12 @@ jobs:
9090
TAURI_SIGNING_PRIVATE_KEY="$(cat "$HOME/.tauri/codexmonitor.key")"
9191
npm run tauri -- build --bundles app
9292
93+
- name: Build daemon binaries
94+
run: |
95+
set -euo pipefail
96+
cd src-tauri
97+
cargo build --release --bin codex_monitor_daemon --bin codex_monitor_daemonctl
98+
9399
- name: Bundle OpenSSL and re-sign
94100
run: |
95101
set -euo pipefail

scripts/build_run_ios_device.sh

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -205,11 +205,17 @@ fi
205205

206206
if [[ "$SKIP_BUILD" -eq 0 ]]; then
207207
sync_ios_icons
208+
BUILD_CMD=("$NPM_BIN" run tauri -- ios build -d -t "$TARGET")
209+
if [[ ${#TAURI_CONFIG_ARGS[@]} -gt 0 ]]; then
210+
BUILD_CMD+=("${TAURI_CONFIG_ARGS[@]}")
211+
fi
208212
if [[ "$OPEN_XCODE" -eq 1 ]]; then
209-
"$NPM_BIN" run tauri -- ios build -d -t "$TARGET" "${TAURI_CONFIG_ARGS[@]}" --open
213+
BUILD_CMD+=(--open)
214+
"${BUILD_CMD[@]}"
210215
exit 0
211216
fi
212-
"$NPM_BIN" run tauri -- ios build -d -t "$TARGET" "${TAURI_CONFIG_ARGS[@]}" --ci
217+
BUILD_CMD+=(--ci)
218+
"${BUILD_CMD[@]}"
213219
fi
214220

215221
APP_PATH="src-tauri/gen/apple/build/arm64/Codex Monitor.app"

scripts/macos-fix-openssl.sh

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,29 @@ frameworks_dir="${app_path}/Contents/Frameworks"
4747
bin_path="${app_path}/Contents/MacOS/codex-monitor"
4848
daemon_path="${app_path}/Contents/MacOS/codex_monitor_daemon"
4949
daemonctl_path="${app_path}/Contents/MacOS/codex_monitor_daemonctl"
50+
daemon_source="${DAEMON_BINARY_PATH:-src-tauri/target/release/codex_monitor_daemon}"
51+
daemonctl_source="${DAEMONCTL_BINARY_PATH:-src-tauri/target/release/codex_monitor_daemonctl}"
52+
53+
copy_if_missing() {
54+
local source_path="$1"
55+
local destination_path="$2"
56+
local label="$3"
57+
58+
if [[ -f "${destination_path}" ]]; then
59+
return
60+
fi
61+
62+
if [[ -f "${source_path}" ]]; then
63+
cp -f "${source_path}" "${destination_path}"
64+
chmod +x "${destination_path}"
65+
echo "Bundled ${label} binary from ${source_path}"
66+
else
67+
echo "Warning: ${label} binary not found in app or at ${source_path}"
68+
fi
69+
}
70+
71+
copy_if_missing "${daemon_source}" "${daemon_path}" "daemon"
72+
copy_if_missing "${daemonctl_source}" "${daemonctl_path}" "daemonctl"
5073

5174
if [[ ! -f "${libssl}" || ! -f "${libcrypto}" ]]; then
5275
echo "OpenSSL dylibs not found at ${openssl_prefix}/lib"

src-tauri/src/daemon_binary.rs

Lines changed: 67 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -8,24 +8,84 @@ pub(crate) fn daemon_binary_candidates() -> &'static [&'static str] {
88
}
99
}
1010

11+
fn daemon_search_dirs(executable_dir: &std::path::Path) -> Vec<PathBuf> {
12+
let mut dirs = Vec::new();
13+
14+
let mut push_unique = |path: PathBuf| {
15+
if !dirs.iter().any(|entry| entry == &path) {
16+
dirs.push(path);
17+
}
18+
};
19+
20+
push_unique(executable_dir.to_path_buf());
21+
22+
#[cfg(target_os = "macos")]
23+
{
24+
if let Some(contents_dir) = executable_dir.parent() {
25+
push_unique(contents_dir.join("Resources"));
26+
}
27+
push_unique(PathBuf::from("/opt/homebrew/bin"));
28+
push_unique(PathBuf::from("/usr/local/bin"));
29+
}
30+
31+
#[cfg(target_os = "linux")]
32+
{
33+
push_unique(PathBuf::from("/usr/local/bin"));
34+
push_unique(PathBuf::from("/usr/bin"));
35+
push_unique(PathBuf::from("/usr/sbin"));
36+
}
37+
38+
dirs
39+
}
40+
1141
pub(crate) fn resolve_daemon_binary_path() -> Result<PathBuf, String> {
42+
let mut attempted_paths: Vec<PathBuf> = Vec::new();
1243
let current_exe = std::env::current_exe().map_err(|err| err.to_string())?;
1344
let parent = current_exe
1445
.parent()
1546
.ok_or_else(|| "Unable to resolve executable directory".to_string())?;
1647
let candidate_names = daemon_binary_candidates();
1748

18-
for name in candidate_names {
19-
let candidate = parent.join(name);
20-
if candidate.is_file() {
21-
return Ok(candidate);
49+
if let Ok(explicit_raw) = std::env::var("CODEX_MONITOR_DAEMON_PATH") {
50+
let explicit = explicit_raw.trim();
51+
if !explicit.is_empty() {
52+
let explicit_path = PathBuf::from(explicit);
53+
if explicit_path.is_file() {
54+
return Ok(explicit_path);
55+
}
56+
if explicit_path.is_dir() {
57+
for name in candidate_names {
58+
let candidate = explicit_path.join(name);
59+
if candidate.is_file() {
60+
return Ok(candidate);
61+
}
62+
attempted_paths.push(candidate);
63+
}
64+
} else {
65+
attempted_paths.push(explicit_path);
66+
}
2267
}
2368
}
2469

70+
for search_dir in daemon_search_dirs(parent) {
71+
for name in candidate_names {
72+
let candidate = search_dir.join(name);
73+
if candidate.is_file() {
74+
return Ok(candidate);
75+
}
76+
attempted_paths.push(candidate);
77+
}
78+
}
79+
80+
let attempted = attempted_paths
81+
.iter()
82+
.map(|path| path.display().to_string())
83+
.collect::<Vec<_>>()
84+
.join(", ");
85+
2586
Err(format!(
26-
"Unable to locate daemon binary in {} (tried: {})",
27-
parent.display(),
28-
candidate_names.join(", ")
87+
"Unable to locate daemon binary (tried: {})",
88+
attempted
2989
))
3090
}
3191

0 commit comments

Comments
 (0)