Skip to content

Commit d31975e

Browse files
JRemitzclaude
andcommitted
fix: resolve --profile relative to active config directory
Profiles are now resolved relative to the parent of REELN_CONFIG (when set) instead of the platform default config dir. This ensures profiles stored alongside a custom config file are found correctly. Also gives --profile and --path CLI arguments strict priority over REELN_CONFIG / REELN_PROFILE env vars. Co-Authored-By: Claude <noreply@anthropic.com>
1 parent 0cd351b commit d31975e

4 files changed

Lines changed: 44 additions & 5 deletions

File tree

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/).
77

88
## [Unreleased]
99

10+
### Fixed
11+
- `--profile` now resolves relative to the active config directory (parent of `REELN_CONFIG`) instead of the platform default, so profiles stored alongside a custom config file are found correctly
12+
- `--profile` and `--path` CLI arguments now take strict priority over `REELN_CONFIG` and `REELN_PROFILE` environment variables
13+
1014
## [0.0.31] - 2026-03-13
1115

1216
### Added

reeln/core/config.py

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,19 @@ def data_dir() -> Path:
6868
return Path.home() / ".local" / "share" / _APP_NAME
6969

7070

71+
def _config_base_dir() -> Path:
72+
"""Return the directory where config files live.
73+
74+
Uses the parent directory of ``REELN_CONFIG`` when set, otherwise
75+
falls back to ``config_dir()``. This ensures ``--profile`` resolves
76+
relative to the active config location.
77+
"""
78+
env_config = os.environ.get("REELN_CONFIG")
79+
if env_config:
80+
return Path(env_config).expanduser().parent
81+
return config_dir()
82+
83+
7184
def default_config_path(profile: str | None = None) -> Path:
7285
"""Return the path to the config file, optionally for a named profile."""
7386
if profile:
@@ -375,15 +388,18 @@ def resolve_config_path(
375388
if path is not None:
376389
return path
377390
if profile is not None:
378-
return default_config_path(profile)
391+
# Resolve profile relative to the active config directory
392+
base_dir = _config_base_dir()
393+
return base_dir / f"config.{profile}.json"
379394

380395
# Fall through to env vars
381396
env_config = os.environ.get("REELN_CONFIG")
382397
if env_config:
383398
return Path(env_config).expanduser()
384399
env_profile = os.environ.get("REELN_PROFILE")
385400
if env_profile:
386-
return default_config_path(env_profile)
401+
base_dir = _config_base_dir()
402+
return base_dir / f"config.{env_profile}.json"
387403

388404
return default_config_path()
389405

tests/integration/test_cli_lifecycle.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -334,7 +334,7 @@ def test_segment_with_no_videos(self, tmp_path: Path) -> None:
334334
],
335335
)
336336
assert result.exit_code == 1
337-
assert "No video files" in result.output
337+
assert "No files matching" in result.output
338338

339339
def test_highlights_with_no_segments(self, tmp_path: Path) -> None:
340340
"""No segment highlight files → "No segment highlight files" error."""

tests/unit/core/test_config.py

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
import pytest
1010

1111
from reeln.core.config import (
12+
_config_base_dir,
1213
apply_env_overrides,
1314
config_dir,
1415
config_to_dict,
@@ -106,6 +107,24 @@ def test_default_config_path_with_profile() -> None:
106107
assert result == Path("/cfg/reeln/config.tournament.json")
107108

108109

110+
# ---------------------------------------------------------------------------
111+
# _config_base_dir
112+
# ---------------------------------------------------------------------------
113+
114+
115+
def test_config_base_dir_with_reeln_config(monkeypatch: pytest.MonkeyPatch) -> None:
116+
"""When REELN_CONFIG is set, _config_base_dir returns its parent directory."""
117+
monkeypatch.setenv("REELN_CONFIG", "/custom/path/config.json")
118+
assert _config_base_dir() == Path("/custom/path")
119+
120+
121+
def test_config_base_dir_without_reeln_config(monkeypatch: pytest.MonkeyPatch) -> None:
122+
"""When REELN_CONFIG is not set, _config_base_dir falls back to config_dir()."""
123+
monkeypatch.delenv("REELN_CONFIG", raising=False)
124+
with patch("reeln.core.config.config_dir", return_value=Path("/default/cfg")):
125+
assert _config_base_dir() == Path("/default/cfg")
126+
127+
109128
# ---------------------------------------------------------------------------
110129
# Defaults
111130
# ---------------------------------------------------------------------------
@@ -803,7 +822,7 @@ def test_load_config_reeln_profile_env(tmp_path: Path, monkeypatch: pytest.Monke
803822
profile_file = tmp_path / "config.tourney.json"
804823
profile_file.write_text(json.dumps({"config_version": 1, "sport": "lacrosse"}))
805824
monkeypatch.setenv("REELN_PROFILE", "tourney")
806-
with patch("reeln.core.config.default_config_path", return_value=profile_file):
825+
with patch("reeln.core.config._config_base_dir", return_value=tmp_path):
807826
cfg = load_config()
808827
assert cfg.sport == "lacrosse"
809828

@@ -814,7 +833,7 @@ def test_load_config_explicit_profile_overrides_env(tmp_path: Path, monkeypatch:
814833
monkeypatch.setenv("REELN_PROFILE", "ignored")
815834
profile_file = tmp_path / "config.explicit.json"
816835
profile_file.write_text(json.dumps({"config_version": 1, "sport": "rugby"}))
817-
with patch("reeln.core.config.default_config_path", return_value=profile_file):
836+
with patch("reeln.core.config._config_base_dir", return_value=tmp_path):
818837
cfg = load_config(profile="explicit")
819838
assert cfg.sport == "rugby"
820839

0 commit comments

Comments
 (0)