Skip to content

fix(dream-macos): auto-detect install dir from script location when DREAM_HOME unset#916

Merged
Lightheartdevs merged 1 commit intoLight-Heart-Labs:mainfrom
yasinBursali:fix/dream-macos-script-dir-autodetect
Apr 15, 2026
Merged

fix(dream-macos): auto-detect install dir from script location when DREAM_HOME unset#916
Lightheartdevs merged 1 commit intoLight-Heart-Labs:mainfrom
yasinBursali:fix/dream-macos-script-dir-autodetect

Conversation

@yasinBursali
Copy link
Copy Markdown
Contributor

@yasinBursali yasinBursali commented Apr 12, 2026

Merge order: Merge before #935 — both modify dream-macos.sh.

What

bash /path/to/install/dream-macos.sh status now works from any non-default install without requiring DREAM_HOME to be exported, by auto-detecting the install from the script's own location. Also fixes a latent off-by-one in installers/macos/lib/constants.sh's path-utils.sh lookup that rendered the original resolve_install_dir() dead code in both source-tree and installed layouts.

Why

External-drive installs, custom Homebrew prefixes, CI automation, cron jobs, and any launchd plist that invokes the CLI by absolute path without inheriting the user shell's env all hit "Dream Server not found at $HOME/dream-server" even when the script itself lives in a populated install. The workaround (always export DREAM_HOME=<install> in .zshrc) is not discoverable from the error message.

Second-order: while verifying the fix, tracing showed MACOS_SCRIPT_DIR in installers/macos/lib/constants.sh resolved to the wrong depth in both layouts. The if [[ -f $MACOS_SCRIPT_DIR/installers/lib/path-utils.sh ]]; then . $MACOS_SCRIPT_DIR/installers/lib/path-utils.sh; fi block was dead in both source-tree (dream-server/installers/installers/lib/path-utils.sh, extra installers/) and installed (/Volumes/X/installers/lib/path-utils.sh, one too many dirs up) layouts. resolve_install_dir() was never actually called from dream-macos.sh; the ELSE fallback was the only live code path. Without fixing this, the new DREAM_SCRIPT_HINT tier would be dead on both layouts too.

How

  1. installers/lib/path-utils.sh — new resolution tier in resolve_install_dir() between DREAM_HOME and DS_INSTALL_DIR. Fires only when DREAM_SCRIPT_HINT is set AND ${DREAM_SCRIPT_HINT}/.env exists. Sentinel prevents false positives from PATH symlinks or scratch copies.

  2. installers/macos/dream-macos.sh — exports DREAM_SCRIPT_HINT="$SCRIPT_DIR" before sourcing constants.sh, unsets after. Updated test_install() error message to point users at the new behavior.

  3. installers/macos/lib/constants.sh — replaces the broken single-path path-utils.sh lookup with a two-candidate for-loop: ../../lib/path-utils.sh matches the source tree, ../installers/lib/path-utils.sh matches the installed layout. Each candidate matches exactly one layout; both verified with realpath. Drops the unused MACOS_SCRIPT_DIR variable.

  4. tests/bats-tests/path-utils.bats — two new tests for the DREAM_SCRIPT_HINT tier (sentinel present / sentinel absent) plus setup/teardown that clears all four env vars for test isolation.

Testing

Automated

  • bash -n all modified shell files: PASS
  • make lint: PASS
  • make test: PASS (82/82 tier-map, 17/17 AMD/Lemonade contracts, all installer contracts, all preflight fixtures)
  • shellcheck on the 3 modified shell files: PASS (only preexisting SC2034 info findings on constants.sh, identical set present on main)
  • pre-commit (gitleaks, private-key, large-files): PASS
  • Two-candidate path loop in constants.sh verified empirically with realpath against both layouts — each candidate matches exactly one.

Runtime (against a macOS install at a non-$HOME volume)

Before fix:
```
$ env -u DREAM_HOME -u INSTALL_DIR -u DS_INSTALL_DIR bash /Volumes/X/dream-server-test/dream-macos.sh status
[XX] Dream Server not found at /Users//dream-server.
```
After fix:
```
$ env -u DREAM_HOME -u INSTALL_DIR -u DS_INSTALL_DIR bash /Volumes/X/dream-server-test/dream-macos.sh status

Dream Server Status (macOS)

Chip: Apple M4
RAM: 24 GB (unified memory)
[OK] llama-server (native Metal): running PID 13757 (healthy)

Health Checks

[OK] LLM API: healthy
[OK] Chat UI: healthy
[OK] Dashboard: healthy
[OK] OpenCode (IDE): healthy
```

Happy-path regression check — `DREAM_HOME` still takes precedence when set:
```
$ DREAM_HOME=/Volumes/X/dream-server-test bash /Volumes/X/dream-server-test/dream-macos.sh status

identical full status output — no regression

```

Sentinel guard sanity — hint pointing at a dir without `.env` falls through to default:
```
$ DREAM_SCRIPT_HINT=/tmp resolve_install_dir

returns $HOME/dream-server (correctly falls through)

```

Manual (per platform for reviewer)

  • macOS: unset DREAM_HOME INSTALL_DIR DS_INSTALL_DIR; bash /path/to/install/dream-macos.sh status from any install path should produce status output, not "not found".
  • Linux: no-op. installers/macos/lib/constants.sh is macOS-only. installers/lib/path-utils.sh is shared, but the new tier is guarded by DREAM_SCRIPT_HINT which only dream-macos.sh sets; Linux's dream-cli and install-core.sh never set it, so the new branch is structurally dead there. Verified via make test passing unchanged.
  • Windows/WSL2: no-op. None of the 4 files are sourced by the Windows installer or dream.ps1.

Platform Impact

  • macOS: affected — bug fix + latent dead-code path resolved. Runtime-verified.
  • Linux: not affected — new code path is dead on Linux (env var never set). make test unchanged.
  • Windows: not affected — no sourced files.

Known Considerations

  • The new BATS tests assert exact-string match on \$BATS_TEST_TMPDIR/dream-install-hint. On macOS developer machines with Homebrew coreutils installed, grealpath canonicalizes /var/folders/.../private/var/folders/..., which would break the assertion locally. CI runs BATS on Linux (/tmp/... has no symlink layer) so it stays green. Flagged non-blocking for any reviewer attempting local Mac runs.
  • DREAM_SCRIPT_HINT is an ephemeral shell var (export → source chain → unset), never written to .env or .env.schema.json. No schema entry needed.

Fork issue

Closes yasinBursali#339

…REAM_HOME unset

Running `bash /path/to/install/dream-macos.sh status` from a non-default
install without DREAM_HOME set would error with
`Dream Server not found at $HOME/dream-server`, even though the script
itself was physically inside the install. External-drive installs,
custom Homebrew prefixes, CI automation and any launchd/cron invocation
by absolute path all hit this.

Resolve it by teaching `resolve_install_dir()` a new `DREAM_SCRIPT_HINT`
tier between the DREAM_HOME and DS_INSTALL_DIR branches, guarded by a
`.env` sentinel file at the hinted root so that PATH symlinks
(e.g. /usr/local/bin) or scratch copies never false-positive.
`dream-macos.sh` exports `DREAM_SCRIPT_HINT="$SCRIPT_DIR"` before sourcing
`constants.sh`, then unsets it after all sources complete so the hint
does not leak into docker-compose/curl subprocesses spawned later.

While verifying the fix end-to-end, I discovered that
`installers/macos/lib/constants.sh` had a latent off-by-one in its
`path-utils.sh` lookup: `MACOS_SCRIPT_DIR="$(cd ...)/../.."` resolved to
the wrong depth in both source-tree and installed layouts, and the
lookup path then re-appended `installers/lib/path-utils.sh` producing
e.g. `dream-server/installers/installers/lib/path-utils.sh` (source) or
`/Volumes/X/installers/lib/path-utils.sh` (installed) - neither exists.
The `if` branch never fired; `resolve_install_dir()` was never actually
called from `dream-macos.sh`, and the ELSE fallback
`DS_INSTALL_DIR="${DREAM_HOME:-$HOME/dream-server}"` was the only live
code path. Without fixing this, the new `DREAM_SCRIPT_HINT` tier would
have been dead on both layouts. Replace the broken single-path lookup
with a two-candidate loop (`../../lib/path-utils.sh` for source tree,
`../installers/lib/path-utils.sh` for installed) and drop the
`MACOS_SCRIPT_DIR` variable (no other consumers after grep).

BATS coverage in `tests/bats-tests/path-utils.bats` gets two new cases
for the new tier (hint + sentinel present, hint + sentinel absent) and
a `setup`/`teardown` that clears `DREAM_SCRIPT_HINT` alongside the
existing env vars so the file stays isolation-safe.

Closes #339
@yasinBursali yasinBursali marked this pull request as ready for review April 13, 2026 17:44
@Lightheartdevs Lightheartdevs merged commit e6c997b into Light-Heart-Labs:main Apr 15, 2026
33 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

2 participants