Skip to content

Commit 2e26d85

Browse files
committed
feat: Inspect path to find a gitdo project
1 parent b0d69da commit 2e26d85

File tree

4 files changed

+127
-12
lines changed

4 files changed

+127
-12
lines changed

.gitracker/tasks.json

Lines changed: 0 additions & 9 deletions
This file was deleted.

src/gitdo/storage.py

Lines changed: 31 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,12 +14,41 @@ def __init__(self, base_path: Path | None = None):
1414
"""Initialize storage.
1515
1616
Args:
17-
base_path: Base path for storage. Defaults to current directory.
17+
base_path: Base path for storage. If None, searches for .gitdo/
18+
by walking up from current directory.
1819
"""
19-
self.base_path = base_path or Path.cwd()
20+
if base_path is None:
21+
self.base_path = self._find_gitdo_root() or Path.cwd()
22+
else:
23+
self.base_path = base_path
2024
self.storage_dir = self.base_path / ".gitdo"
2125
self.tasks_file = self.storage_dir / "tasks.json"
2226

27+
@staticmethod
28+
def _find_gitdo_root(start_path: Path | None = None) -> Path | None:
29+
"""Find .gitdo/ folder by walking up directory tree.
30+
31+
Args:
32+
start_path: Starting directory. Defaults to current directory.
33+
34+
Returns:
35+
Path containing .gitdo/ folder, or None if not found.
36+
"""
37+
current = start_path or Path.cwd()
38+
current = current.resolve()
39+
40+
# Walk up the directory tree until we find .gitdo or reach root
41+
while True:
42+
gitdo_path = current / ".gitdo"
43+
if gitdo_path.exists() and gitdo_path.is_dir():
44+
return current
45+
46+
parent = current.parent
47+
# Check if we've reached the filesystem root
48+
if parent == current:
49+
return None
50+
current = parent
51+
2352
def init(self) -> None:
2453
"""Initialize .gitdo folder and files."""
2554
self.storage_dir.mkdir(exist_ok=True)

tests/test_storage.py

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,11 @@
11
"""Tests for storage functionality."""
22

33
import json
4+
import os
5+
from pathlib import Path
46

57
from gitdo.models import TaskStatus
8+
from gitdo.storage import Storage
69

710

811
def test_storage_init(storage):
@@ -113,3 +116,95 @@ def test_tasks_json_format(initialized_storage):
113116
assert len(data) == 1
114117
assert data[0]["id"] == task.id
115118
assert data[0]["title"] == "Test task"
119+
120+
121+
def test_find_gitdo_root_in_current_dir(temp_dir):
122+
"""Test finding .gitdo/ in current directory."""
123+
gitdo_dir = temp_dir / ".gitdo"
124+
gitdo_dir.mkdir()
125+
126+
found_root = Storage._find_gitdo_root(temp_dir)
127+
assert found_root == temp_dir.resolve()
128+
129+
130+
def test_find_gitdo_root_in_parent_dir(temp_dir):
131+
"""Test finding .gitdo/ in parent directory."""
132+
gitdo_dir = temp_dir / ".gitdo"
133+
gitdo_dir.mkdir()
134+
135+
# Create a subdirectory
136+
subdir = temp_dir / "subdir"
137+
subdir.mkdir()
138+
139+
found_root = Storage._find_gitdo_root(subdir)
140+
assert found_root == temp_dir.resolve()
141+
142+
143+
def test_find_gitdo_root_multiple_levels_up(temp_dir):
144+
"""Test finding .gitdo/ multiple levels up the directory tree."""
145+
gitdo_dir = temp_dir / ".gitdo"
146+
gitdo_dir.mkdir()
147+
148+
# Create nested subdirectories
149+
deep_dir = temp_dir / "level1" / "level2" / "level3"
150+
deep_dir.mkdir(parents=True)
151+
152+
found_root = Storage._find_gitdo_root(deep_dir)
153+
assert found_root == temp_dir.resolve()
154+
155+
156+
def test_find_gitdo_root_not_found(temp_dir):
157+
"""Test when .gitdo/ is not found anywhere."""
158+
# Don't create .gitdo/ folder
159+
found_root = Storage._find_gitdo_root(temp_dir)
160+
assert found_root is None
161+
162+
163+
def test_storage_uses_walk_up_logic(temp_dir):
164+
"""Test that Storage initialization uses walk-up logic."""
165+
# Create .gitdo/ in parent directory
166+
gitdo_dir = temp_dir / ".gitdo"
167+
gitdo_dir.mkdir()
168+
(gitdo_dir / "tasks.json").write_text("[]")
169+
170+
# Create a subdirectory and change to it
171+
subdir = temp_dir / "subdir"
172+
subdir.mkdir()
173+
174+
original_cwd = os.getcwd()
175+
try:
176+
os.chdir(subdir)
177+
# Create storage without base_path - should find parent's .gitdo/
178+
storage = Storage()
179+
assert storage.base_path == temp_dir.resolve()
180+
assert storage.storage_dir == gitdo_dir.resolve()
181+
finally:
182+
os.chdir(original_cwd)
183+
184+
185+
def test_storage_falls_back_to_cwd_when_not_found(temp_dir):
186+
"""Test that Storage falls back to current directory when .gitdo/ not found."""
187+
# Don't create .gitdo/ folder anywhere
188+
original_cwd = os.getcwd()
189+
try:
190+
os.chdir(temp_dir)
191+
storage = Storage()
192+
assert storage.base_path == temp_dir.resolve()
193+
finally:
194+
os.chdir(original_cwd)
195+
196+
197+
def test_storage_explicit_base_path_skips_walk_up(temp_dir):
198+
"""Test that explicit base_path parameter skips walk-up logic."""
199+
# Create .gitdo/ in one directory
200+
gitdo_dir = temp_dir / ".gitdo"
201+
gitdo_dir.mkdir()
202+
203+
# Create another directory without .gitdo/
204+
other_dir = temp_dir / "other"
205+
other_dir.mkdir()
206+
207+
# Explicitly set base_path - should not use walk-up logic
208+
storage = Storage(base_path=other_dir)
209+
assert storage.base_path == other_dir
210+
assert storage.storage_dir == other_dir / ".gitdo"

uv.lock

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)