Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 23 additions & 1 deletion lib/watch.sh
Original file line number Diff line number Diff line change
Expand Up @@ -219,6 +219,23 @@ with open(sys.argv[1], 'w') as f:
_detect_install_mode() {
local devflow_path
devflow_path="$(command -v devflow 2>/dev/null || echo "")"

# PATH-independent fallback. cron and launchd fire with a minimal PATH that usually
# lacks ~/.local/bin, so `command -v devflow` is empty and mode wrongly resolves to
# "none" - and _auto_reinstall_check then silently no-ops (the exact reason the launchd
# agent could not auto-update). Resolve the installed launcher directly instead.
# Inspect the BINDIR launcher (not the repo source): a copy install has a real file
# there, a `make link` install has a symlink there - that distinction IS the mode.
if [[ -z "$devflow_path" ]]; then
local cand
for cand in \
"$([[ "${DEVFLOW_ROOT:-}" == */share/devflow ]] && echo "${DEVFLOW_ROOT%/share/devflow}/bin/devflow")" \
"${HOME}/.local/bin/devflow" \
"/usr/local/bin/devflow" \
"/opt/homebrew/bin/devflow"; do
[[ -n "$cand" && -e "$cand" ]] && { devflow_path="$cand"; break; }
done
fi
[[ -z "$devflow_path" ]] && echo "none" && return

# Resolve the real path for Homebrew detection
Expand Down Expand Up @@ -550,9 +567,10 @@ _watch_launchd_plist() { echo "${HOME}/Library/LaunchAgents/$(_watch_launchd_lab
# _watch_install_launchd <project_dir> <devflow_bin> — write + (re)load the LaunchAgent.
_watch_install_launchd() {
local project_dir="$1" devflow_bin="$2"
local label plist uid cal m
local label plist uid cal m bindir
label="$(_watch_launchd_label "$project_dir")"
plist="$(_watch_launchd_plist "$project_dir")"
bindir="$(dirname "$devflow_bin")" # so the agent's minimal PATH still finds devflow
mkdir -p "${HOME}/Library/LaunchAgents" "${HOME}/.devflow"

cal=""
Expand All @@ -577,6 +595,10 @@ _watch_install_launchd() {
<key>StartCalendarInterval</key>
<array>
${cal} </array>
<key>EnvironmentVariables</key>
<dict>
<key>PATH</key><string>${bindir}:/opt/homebrew/bin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin</string>
</dict>
<key>RunAtLoad</key><true/>
<key>WorkingDirectory</key><string>${project_dir}</string>
<key>StandardOutPath</key><string>${HOME}/.devflow/watch.log</string>
Expand Down
45 changes: 43 additions & 2 deletions tests/unit/watch.bats
Original file line number Diff line number Diff line change
Expand Up @@ -233,9 +233,12 @@ EOF
assert_output "brew"
}

@test "detect_install_mode returns none when devflow not found" {
@test "detect_install_mode returns none when devflow not found anywhere" {
# none now requires BOTH: not on PATH AND no launcher at any known location
# (the pathless fallback resolves ~/.local/bin etc.), so use a clean empty HOME.
export HOME="${BATS_TEST_TMPDIR}/empty-home"; mkdir -p "$HOME"
rm -f "${MOCK_DIR}/devflow"
PATH="${MOCK_DIR}" run _detect_install_mode
DEVFLOW_ROOT="" PATH="${MOCK_DIR}" run _detect_install_mode
assert_success
assert_output "none"
}
Expand Down Expand Up @@ -363,3 +366,41 @@ EOF
assert_output --partial "LaunchAgent removed"
[ ! -f "$plist" ] || fail "plist not removed"
}

# ── _detect_install_mode under a minimal PATH (launchd/cron) ────

@test "detect_install_mode resolves WITHOUT PATH: copy install (regular file at BINDIR)" {
export HOME="${BATS_TEST_TMPDIR}/dh-copy"; mkdir -p "$HOME/.local/bin"
printf '#!/bin/bash\n' > "$HOME/.local/bin/devflow"; chmod +x "$HOME/.local/bin/devflow"
PATH="/usr/bin:/bin" run _detect_install_mode
assert_success
assert_output "install"
}

@test "detect_install_mode resolves WITHOUT PATH: link (symlink at BINDIR)" {
export HOME="${BATS_TEST_TMPDIR}/dh-link"; mkdir -p "$HOME/.local/bin"
printf '#!/bin/bash\n' > "$HOME/real-df"; chmod +x "$HOME/real-df"
ln -sf "$HOME/real-df" "$HOME/.local/bin/devflow"
PATH="/usr/bin:/bin" run _detect_install_mode
assert_success
assert_output "link"
}

@test "detect_install_mode resolves WITHOUT PATH via DEVFLOW_ROOT for a custom prefix" {
export HOME="${BATS_TEST_TMPDIR}/dh-none"; mkdir -p "$HOME" # no ~/.local/bin/devflow
local pfx="${BATS_TEST_TMPDIR}/opt/df"; mkdir -p "$pfx/bin" "$pfx/share/devflow"
printf '#!/bin/bash\n' > "$pfx/bin/devflow"; chmod +x "$pfx/bin/devflow"
DEVFLOW_ROOT="$pfx/share/devflow" PATH="/usr/bin:/bin" run _detect_install_mode
assert_success
assert_output "install"
}

@test "launchd plist injects PATH so the agent's minimal env can find devflow" {
export HOME="${BATS_TEST_TMPDIR}/lah-path"; mkdir -p "$HOME"
printf '#!/bin/bash\nexit 0\n' > "${MOCK_DIR}/launchctl"; chmod +x "${MOCK_DIR}/launchctl"
_watch_install_launchd "/tmp/pp" "/Users/x/.local/bin/devflow"
local plist; plist="$(_watch_launchd_plist /tmp/pp)"
run cat "$plist"
assert_output --partial "<key>EnvironmentVariables</key>"
assert_output --partial "<key>PATH</key><string>/Users/x/.local/bin:"
}
Loading