Skip to content

Commit 261333b

Browse files
authored
Prevent errors when installing folders that do not contain tome commands
1 parent 0ea5f09 commit 261333b

File tree

2 files changed

+68
-4
lines changed

2 files changed

+68
-4
lines changed

tests/integration/test_install.py

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -407,8 +407,9 @@ def test_tomeignore_file():
407407
)
408408
# TODO: greetings/subgreetings does not filter out subgreetings-commands.py,
409409
# you have to only use the last folder, which is not ideal
410-
save(os.path.join(source, "greetings", "greetings-commands.py"), "")
411-
save(os.path.join(source, "greetings", "subgreetings", "subgreetings-commands.py"), "")
410+
# adding fake command to not raise error for not having any tome commands
411+
save(os.path.join(source, "greetings", "greetings-commands.py"), "@tome_command()")
412+
save(os.path.join(source, "greetings", "subgreetings", "subgreetings-commands.py"), "@tome_command()")
412413
save(os.path.join(source, "assets", "image.png"), "")
413414
save(os.path.join(source, "pepetests", "test_pepe.py"), "")
414415

@@ -423,3 +424,30 @@ def test_tomeignore_file():
423424
assert "subgreetings-commands.py" not in tc.out
424425
assert "image.png" in tc.out
425426
assert "test_pepe.py" in tc.out
427+
428+
429+
def test_validate_folder_install():
430+
client = TestClient()
431+
client.run("new mynamespace:mycommand")
432+
client.run("install mynamespace", assert_error=True)
433+
assert "No valid tome commands were found" in client.out
434+
435+
client.run("install mynamespace -e", assert_error=True)
436+
assert "No valid tome commands were found" in client.out
437+
438+
439+
def test_validate_git_install():
440+
client = TestClient()
441+
git_repo_folder = os.path.join(client.current_folder, "git_repo")
442+
443+
# Create an invalid structure in the git repository: place the command in a nested subdirectory (more than one level deep)
444+
nested_folder = os.path.join(git_repo_folder, "subfolder", "nested")
445+
os.makedirs(nested_folder, exist_ok=True)
446+
with client.chdir(nested_folder):
447+
client.run("new mynamespace:mycommand")
448+
449+
client.init_git_repo(folder=git_repo_folder)
450+
451+
install_source = f"{os.path.join(client.current_folder, git_repo_folder)}/.git"
452+
client.run(f"install '{install_source}'", assert_error=True)
453+
assert "No valid tome commands were found in the cloned repository" in client.out

tome/internal/installer.py

Lines changed: 38 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -157,13 +157,47 @@ def download_and_extract(source, destination):
157157
output.warning(f"Downloaded {destination_file} but did not extract (unsupported type)")
158158

159159

160+
def _has_valid_commands(directory):
161+
"""
162+
Check the first-level subdirectories of the given directory to find files
163+
that appear to contain valid tome commands.
164+
"""
165+
# List only the first-level subdirectories of 'directory'
166+
for subdir_name in os.listdir(directory):
167+
subdir = os.path.join(directory, subdir_name)
168+
if os.path.isdir(subdir):
169+
# Iterate over the files in the first-level subdirectory
170+
for file in os.listdir(subdir):
171+
file_path = os.path.join(subdir, file)
172+
if os.path.isfile(file_path):
173+
ext = os.path.splitext(file)[1]
174+
# Check if the filename starts with "tome_" and has a recognized shell extension
175+
if file.startswith("tome_") and ext in ['.sh', '.bat', '.ps1', '.bash', '.zsh']:
176+
return True
177+
# Alternatively, if it is a Python file, search for the decorator usage
178+
if ext == ".py":
179+
try:
180+
with open(file_path, 'r', encoding='utf-8') as f:
181+
content = f.read()
182+
if "@tome_command(" in content:
183+
return True
184+
except Exception:
185+
# Ignore files that cannot be read
186+
continue
187+
return False
188+
189+
160190
def install_from_source(source, cache_destination_folder, force_requirements, create_env):
161191
if os.path.exists(cache_destination_folder):
162192
rmdir(cache_destination_folder)
163193
if source.type is SourceType.GIT:
164194
commit = clone_git_repo(source, cache_destination_folder)
165195
source.commit = commit
196+
if not _has_valid_commands(cache_destination_folder):
197+
raise TomeException("No valid tome commands were found in the cloned repository.")
166198
elif source.type is SourceType.FOLDER:
199+
if not _has_valid_commands(source.uri):
200+
raise TomeException(f"No valid tome commands were found in the '{source.uri}' folder.")
167201
process_folder(source.uri, cache_destination_folder)
168202
elif source.type is SourceType.FILE:
169203
assert is_compressed_file(source.uri)
@@ -188,14 +222,16 @@ def install_editable(source, cache_base_folder, force_requirements, create_env):
188222
Updates the cache directory with a new source installed in editable mode.
189223
If an editable installations file doesn't exist, it creates a new one.
190224
191-
:param create_env:
192-
:param force_requirements:
225+
:param create_env: Whether to create a virtual environment for the source.
226+
:param force_requirements: Force installation of requirements.
193227
:param source: The source of the scripts to be installed in editable mode.
194228
:param cache_base_folder: The cache directory where the installations file is stored.
195229
"""
196230
output = TomeOutput()
197231
os.makedirs(cache_base_folder, exist_ok=True)
198232

233+
if not _has_valid_commands(source.uri):
234+
raise TomeException(f"No valid tome commands were found in the '{source.uri}' folder.")
199235
_install_requirements(source.uri, force_requirements, create_env)
200236

201237
editables_file = TomePaths(cache_base_folder).editables_path

0 commit comments

Comments
 (0)