Skip to content
Open
Show file tree
Hide file tree
Changes from 5 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
9 changes: 9 additions & 0 deletions cluv/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
import subprocess
import sys
import typing
from pathlib import Path
from typing import Callable

import rich
Expand Down Expand Up @@ -215,6 +216,14 @@ def add_init_args(subparsers: Subparsers) -> argparse.ArgumentParser:
help="Initialize the current project across clusters.",
formatter_class=rich_argparse.RichHelpFormatter,
)
init_parser.add_argument(
"path",
nargs="?",
default=None,
metavar="<path>",
type=Path,
help="Path to initialize the project in. Creates the directory if it doesn't exist. Defaults to the current directory.",
)
init_parser.set_defaults(func=init)
return init_parser

Expand Down
6 changes: 5 additions & 1 deletion cluv/cli/init.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
REPO_ROOT = Path(__file__).resolve().parents[2]


def init() -> None:
def init(path: Path | None = None) -> None:
"""Initialize a new project with cluv.

This does the following:
Expand All @@ -30,6 +30,10 @@ def init() -> None:
console.rule("[bold cyan]cluv init[/bold cyan]")
console.print()

if path is not None:
path.mkdir(parents=True, exist_ok=True)
os.chdir(path)

# Check if the current directory is under the home directory. If not, raise an error and exit.
check_home_dir()

Expand Down
59 changes: 59 additions & 0 deletions tests/test_init.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
"""Unit tests for cluv/cli/init.py check functions."""

import importlib
import shutil
import subprocess
import textwrap
from pathlib import Path

Expand All @@ -14,6 +16,7 @@
check_home_dir,
check_job_script,
check_symlink_to_scratch,
init,
)
from cluv.config import load_cluv_config

Expand Down Expand Up @@ -211,3 +214,59 @@ def test_replace_results_dir_from_legacy_template(
assert 'results_path="outputs"' in generated_legacy_script_content
assert "Using $results_path" in generated_legacy_script_content
assert "results_dir" not in generated_legacy_script_content


class TestInitIntegration:
"""Integration tests for the init() function that run the full init flow locally."""

@pytest.mark.skipif(shutil.which("uv") is None, reason="uv is not installed")
def test_init_with_path_creates_project(
self, tmp_path: Path, monkeypatch: pytest.MonkeyPatch
) -> None:
"""init(path=<name>) creates and initializes a project directory end-to-end."""
fake_home = tmp_path / "home"
fake_home.mkdir()
new_project = fake_home / "my_project"

monkeypatch.setattr(Path, "home", lambda: fake_home)
monkeypatch.delenv("SCRATCH", raising=False)
monkeypatch.chdir(tmp_path) # ensures cwd is restored after the test

# Pre-init a git repo so that check_git() doesn't raise
subprocess.run(["git", "init", str(new_project)], check=True, capture_output=True)
Comment thread
lebrice marked this conversation as resolved.
Outdated

init(path=new_project)

assert new_project.is_dir()
pyproject_path = new_project / "pyproject.toml"
assert pyproject_path.exists()

config = load_cluv_config(pyproject_path)
assert config.results_path is not None

assert (new_project / "scripts").is_dir()
assert (new_project / JOB_SCRIPT_PATH).exists()

@pytest.mark.skipif(shutil.which("uv") is None, reason="uv is not installed")
def test_init_without_path_uses_cwd(
self, tmp_path: Path, monkeypatch: pytest.MonkeyPatch
) -> None:
"""init() without a path argument runs in the current directory."""
fake_home = tmp_path / "home"
project_dir = fake_home / "my_project"
project_dir.mkdir(parents=True)

monkeypatch.setattr(Path, "home", lambda: fake_home)
monkeypatch.delenv("SCRATCH", raising=False)
monkeypatch.chdir(project_dir)

subprocess.run(["git", "init", str(project_dir)], check=True, capture_output=True)

init()

pyproject_path = project_dir / "pyproject.toml"
assert pyproject_path.exists()

config = load_cluv_config(pyproject_path)
assert config.results_path is not None