rhiza 0.8.0__tar.gz → 0.8.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {rhiza-0.8.0 → rhiza-0.8.1}/PKG-INFO +1 -1
- {rhiza-0.8.0 → rhiza-0.8.1}/pyproject.toml +1 -1
- {rhiza-0.8.0 → rhiza-0.8.1}/src/rhiza/commands/materialize.py +6 -19
- {rhiza-0.8.0 → rhiza-0.8.1}/src/rhiza/commands/migrate.py +19 -0
- {rhiza-0.8.0 → rhiza-0.8.1}/tests/test_commands/test_materialize.py +302 -34
- {rhiza-0.8.0 → rhiza-0.8.1}/tests/test_commands/test_migrate.py +132 -1
- {rhiza-0.8.0 → rhiza-0.8.1}/uv.lock +1 -1
- {rhiza-0.8.0 → rhiza-0.8.1}/.editorconfig +0 -0
- {rhiza-0.8.0 → rhiza-0.8.1}/.github/dependabot.yml +0 -0
- {rhiza-0.8.0 → rhiza-0.8.1}/.github/rhiza/actions/setup-project/action.yml +0 -0
- {rhiza-0.8.0 → rhiza-0.8.1}/.github/workflows/rhiza_book.yml +0 -0
- {rhiza-0.8.0 → rhiza-0.8.1}/.github/workflows/rhiza_ci.yml +0 -0
- {rhiza-0.8.0 → rhiza-0.8.1}/.github/workflows/rhiza_deptry.yml +0 -0
- {rhiza-0.8.0 → rhiza-0.8.1}/.github/workflows/rhiza_devcontainer.yml +0 -0
- {rhiza-0.8.0 → rhiza-0.8.1}/.github/workflows/rhiza_docker.yml +0 -0
- {rhiza-0.8.0 → rhiza-0.8.1}/.github/workflows/rhiza_marimo.yml +0 -0
- {rhiza-0.8.0 → rhiza-0.8.1}/.github/workflows/rhiza_pre-commit.yml +0 -0
- {rhiza-0.8.0 → rhiza-0.8.1}/.github/workflows/rhiza_release.yml +0 -0
- {rhiza-0.8.0 → rhiza-0.8.1}/.github/workflows/rhiza_sync.yml +0 -0
- {rhiza-0.8.0 → rhiza-0.8.1}/.github/workflows/rhiza_validate.yml +0 -0
- {rhiza-0.8.0 → rhiza-0.8.1}/.gitignore +0 -0
- {rhiza-0.8.0 → rhiza-0.8.1}/.pre-commit-config.yaml +0 -0
- {rhiza-0.8.0 → rhiza-0.8.1}/.rhiza/CONFIG.md +0 -0
- {rhiza-0.8.0 → rhiza-0.8.1}/.rhiza/TOKEN_SETUP.md +0 -0
- {rhiza-0.8.0 → rhiza-0.8.1}/.rhiza/history +0 -0
- {rhiza-0.8.0 → rhiza-0.8.1}/.rhiza/scripts/book.sh +0 -0
- {rhiza-0.8.0 → rhiza-0.8.1}/.rhiza/scripts/bump.sh +0 -0
- {rhiza-0.8.0 → rhiza-0.8.1}/.rhiza/scripts/customisations/build-extras.sh +0 -0
- {rhiza-0.8.0 → rhiza-0.8.1}/.rhiza/scripts/customisations/post-release.sh +0 -0
- {rhiza-0.8.0 → rhiza-0.8.1}/.rhiza/scripts/marimushka.sh +0 -0
- {rhiza-0.8.0 → rhiza-0.8.1}/.rhiza/scripts/release.sh +0 -0
- {rhiza-0.8.0 → rhiza-0.8.1}/.rhiza/scripts/update-readme-help.sh +0 -0
- {rhiza-0.8.0 → rhiza-0.8.1}/.rhiza/template.yml +0 -0
- {rhiza-0.8.0 → rhiza-0.8.1}/.rhiza/utils/version_matrix.py +0 -0
- {rhiza-0.8.0 → rhiza-0.8.1}/.rhiza/utils/version_max.py +0 -0
- {rhiza-0.8.0 → rhiza-0.8.1}/CLI.md +0 -0
- {rhiza-0.8.0 → rhiza-0.8.1}/CODE_OF_CONDUCT.md +0 -0
- {rhiza-0.8.0 → rhiza-0.8.1}/CONTRIBUTING.md +0 -0
- {rhiza-0.8.0 → rhiza-0.8.1}/GETTING_STARTED.md +0 -0
- {rhiza-0.8.0 → rhiza-0.8.1}/LICENSE +0 -0
- {rhiza-0.8.0 → rhiza-0.8.1}/Makefile +0 -0
- {rhiza-0.8.0 → rhiza-0.8.1}/README.md +0 -0
- {rhiza-0.8.0 → rhiza-0.8.1}/USAGE.md +0 -0
- {rhiza-0.8.0 → rhiza-0.8.1}/book/Makefile.book +0 -0
- {rhiza-0.8.0 → rhiza-0.8.1}/book/marimo/.gitkeep +0 -0
- {rhiza-0.8.0 → rhiza-0.8.1}/book/marimo/README.md +0 -0
- {rhiza-0.8.0 → rhiza-0.8.1}/book/marimo/rhiza.py +0 -0
- {rhiza-0.8.0 → rhiza-0.8.1}/book/minibook-templates/custom.html.jinja2 +0 -0
- {rhiza-0.8.0 → rhiza-0.8.1}/book/pdoc-templates/module.html.jinja2 +0 -0
- {rhiza-0.8.0 → rhiza-0.8.1}/presentation/Makefile.presentation +0 -0
- {rhiza-0.8.0 → rhiza-0.8.1}/presentation/README.md +0 -0
- {rhiza-0.8.0 → rhiza-0.8.1}/pytest.ini +0 -0
- {rhiza-0.8.0 → rhiza-0.8.1}/renovate.json +0 -0
- {rhiza-0.8.0 → rhiza-0.8.1}/ruff.toml +0 -0
- {rhiza-0.8.0 → rhiza-0.8.1}/src/rhiza/__init__.py +0 -0
- {rhiza-0.8.0 → rhiza-0.8.1}/src/rhiza/__main__.py +0 -0
- {rhiza-0.8.0 → rhiza-0.8.1}/src/rhiza/_templates/basic/__init__.py.jinja2 +0 -0
- {rhiza-0.8.0 → rhiza-0.8.1}/src/rhiza/_templates/basic/main.py.jinja2 +0 -0
- {rhiza-0.8.0 → rhiza-0.8.1}/src/rhiza/_templates/basic/pyproject.toml.jinja2 +0 -0
- {rhiza-0.8.0 → rhiza-0.8.1}/src/rhiza/cli.py +0 -0
- {rhiza-0.8.0 → rhiza-0.8.1}/src/rhiza/commands/__init__.py +0 -0
- {rhiza-0.8.0 → rhiza-0.8.1}/src/rhiza/commands/init.py +0 -0
- {rhiza-0.8.0 → rhiza-0.8.1}/src/rhiza/commands/uninstall.py +0 -0
- {rhiza-0.8.0 → rhiza-0.8.1}/src/rhiza/commands/validate.py +0 -0
- {rhiza-0.8.0 → rhiza-0.8.1}/src/rhiza/commands/welcome.py +0 -0
- {rhiza-0.8.0 → rhiza-0.8.1}/src/rhiza/models.py +0 -0
- {rhiza-0.8.0 → rhiza-0.8.1}/tests/Makefile.tests +0 -0
- {rhiza-0.8.0 → rhiza-0.8.1}/tests/test_cli_commands.py +0 -0
- {rhiza-0.8.0 → rhiza-0.8.1}/tests/test_commands/test_init.py +0 -0
- {rhiza-0.8.0 → rhiza-0.8.1}/tests/test_commands/test_uninstall.py +0 -0
- {rhiza-0.8.0 → rhiza-0.8.1}/tests/test_commands/test_validate.py +0 -0
- {rhiza-0.8.0 → rhiza-0.8.1}/tests/test_models.py +0 -0
- {rhiza-0.8.0 → rhiza-0.8.1}/tests/test_package.py +0 -0
- {rhiza-0.8.0 → rhiza-0.8.1}/tests/test_rhiza/README.md +0 -0
- {rhiza-0.8.0 → rhiza-0.8.1}/tests/test_rhiza/benchmarks/.gitignore +0 -0
- {rhiza-0.8.0 → rhiza-0.8.1}/tests/test_rhiza/benchmarks/README.md +0 -0
- {rhiza-0.8.0 → rhiza-0.8.1}/tests/test_rhiza/benchmarks/analyze_benchmarks.py +0 -0
- {rhiza-0.8.0 → rhiza-0.8.1}/tests/test_rhiza/conftest.py +0 -0
- {rhiza-0.8.0 → rhiza-0.8.1}/tests/test_rhiza/test_bump_script.py +0 -0
- {rhiza-0.8.0 → rhiza-0.8.1}/tests/test_rhiza/test_docstrings.py +0 -0
- {rhiza-0.8.0 → rhiza-0.8.1}/tests/test_rhiza/test_git_repo_fixture.py +0 -0
- {rhiza-0.8.0 → rhiza-0.8.1}/tests/test_rhiza/test_makefile.py +0 -0
- {rhiza-0.8.0 → rhiza-0.8.1}/tests/test_rhiza/test_marimushka_script.py +0 -0
- {rhiza-0.8.0 → rhiza-0.8.1}/tests/test_rhiza/test_readme.py +0 -0
- {rhiza-0.8.0 → rhiza-0.8.1}/tests/test_rhiza/test_release_script.py +0 -0
- {rhiza-0.8.0 → rhiza-0.8.1}/tests/test_rhiza/test_structure.py +0 -0
- {rhiza-0.8.0 → rhiza-0.8.1}/tests/test_rhiza/test_updatereadme_script.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: rhiza
|
|
3
|
-
Version: 0.8.
|
|
3
|
+
Version: 0.8.1
|
|
4
4
|
Summary: Reusable configuration templates for modern Python projects
|
|
5
5
|
Project-URL: Homepage, https://github.com/jebel-quant/rhiza-cli
|
|
6
6
|
Project-URL: Repository, https://github.com/jebel-quant/rhiza-cli
|
|
@@ -15,7 +15,7 @@ from pathlib import Path
|
|
|
15
15
|
|
|
16
16
|
from loguru import logger
|
|
17
17
|
|
|
18
|
-
from rhiza.commands import
|
|
18
|
+
from rhiza.commands.validate import validate
|
|
19
19
|
from rhiza.models import RhizaTemplate
|
|
20
20
|
|
|
21
21
|
|
|
@@ -116,11 +116,11 @@ def materialize(target: Path, branch: str, target_branch: str | None, force: boo
|
|
|
116
116
|
sys.exit(1)
|
|
117
117
|
|
|
118
118
|
# -----------------------
|
|
119
|
-
#
|
|
119
|
+
# Validate Rhiza configuration
|
|
120
120
|
# -----------------------
|
|
121
|
-
# The
|
|
121
|
+
# The validate function checks if template.yml exists and is valid
|
|
122
122
|
# Returns True if valid, False otherwise
|
|
123
|
-
valid =
|
|
123
|
+
valid = validate(target)
|
|
124
124
|
|
|
125
125
|
if not valid:
|
|
126
126
|
logger.error(f"Rhiza template is invalid in: {target}")
|
|
@@ -128,21 +128,8 @@ def materialize(target: Path, branch: str, target_branch: str | None, force: boo
|
|
|
128
128
|
sys.exit(1)
|
|
129
129
|
|
|
130
130
|
# Load the template configuration from the validated file
|
|
131
|
-
#
|
|
132
|
-
|
|
133
|
-
standard_template_file = target / ".github" / "rhiza" / "template.yml"
|
|
134
|
-
|
|
135
|
-
if migrated_template_file.exists():
|
|
136
|
-
template_file = migrated_template_file
|
|
137
|
-
logger.debug(f"Loading template configuration from migrated location: {template_file}")
|
|
138
|
-
elif standard_template_file.exists():
|
|
139
|
-
template_file = standard_template_file
|
|
140
|
-
logger.debug(f"Loading template configuration from standard location: {template_file}")
|
|
141
|
-
else:
|
|
142
|
-
logger.error("No template.yml file found")
|
|
143
|
-
logger.error("Run 'rhiza init' or 'rhiza migrate' to create one")
|
|
144
|
-
sys.exit(1)
|
|
145
|
-
|
|
131
|
+
# Validation ensures the file exists at .rhiza/template.yml
|
|
132
|
+
template_file = target / ".rhiza" / "template.yml"
|
|
146
133
|
template = RhizaTemplate.from_yaml(template_file)
|
|
147
134
|
|
|
148
135
|
# Extract template configuration settings
|
|
@@ -10,6 +10,8 @@ from pathlib import Path
|
|
|
10
10
|
|
|
11
11
|
from loguru import logger
|
|
12
12
|
|
|
13
|
+
from rhiza.models import RhizaTemplate
|
|
14
|
+
|
|
13
15
|
|
|
14
16
|
def migrate(target: Path) -> None:
|
|
15
17
|
"""Migrate project to use the new .rhiza folder structure.
|
|
@@ -81,6 +83,23 @@ def migrate(target: Path) -> None:
|
|
|
81
83
|
logger.warning("No existing template.yml file found in .github")
|
|
82
84
|
logger.info("You may need to run 'rhiza init' to create a template configuration")
|
|
83
85
|
|
|
86
|
+
# Ensure the .rhiza folder is included in template.yml include list (if template exists)
|
|
87
|
+
template_file = new_template_file
|
|
88
|
+
if template_file.exists():
|
|
89
|
+
# Load existing template configuration
|
|
90
|
+
template = RhizaTemplate.from_yaml(template_file)
|
|
91
|
+
template_include = template.include or []
|
|
92
|
+
if ".rhiza" not in template_include:
|
|
93
|
+
logger.warning("The .rhiza folder is not included in your template.yml")
|
|
94
|
+
template_include.append(".rhiza")
|
|
95
|
+
logger.info("The .rhiza folder is added to your template.yml to ensure it's included in your repository")
|
|
96
|
+
|
|
97
|
+
# Save the updated template.yml
|
|
98
|
+
template.include = template_include
|
|
99
|
+
template.to_yaml(template_file)
|
|
100
|
+
else:
|
|
101
|
+
logger.debug("No template.yml present in .rhiza; skipping include update")
|
|
102
|
+
|
|
84
103
|
# Migrate .rhiza.history to .rhiza/history if it exists
|
|
85
104
|
old_history_file = target / ".rhiza.history"
|
|
86
105
|
new_history_file = rhiza_dir / "history"
|
|
@@ -5,6 +5,7 @@ underlying inject logic and that basic paths and options are handled.
|
|
|
5
5
|
"""
|
|
6
6
|
|
|
7
7
|
import subprocess
|
|
8
|
+
from pathlib import Path
|
|
8
9
|
from unittest.mock import Mock, patch
|
|
9
10
|
|
|
10
11
|
import pytest
|
|
@@ -18,41 +19,20 @@ from rhiza.commands.materialize import materialize
|
|
|
18
19
|
class TestInjectCommand:
|
|
19
20
|
"""Tests for the inject/materialize command."""
|
|
20
21
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
@patch("rhiza.commands.materialize.shutil.copy2")
|
|
24
|
-
@patch("rhiza.commands.materialize.tempfile.mkdtemp")
|
|
25
|
-
def test_inject_creates_default_template_yml(
|
|
26
|
-
self, mock_mkdtemp, mock_copy2, mock_rmtree, mock_subprocess, tmp_path
|
|
27
|
-
):
|
|
28
|
-
"""Test that inject creates a default template.yml when it doesn't exist."""
|
|
22
|
+
def test_inject_fails_without_template_yml(self, tmp_path):
|
|
23
|
+
"""Test that materialize fails when template.yml doesn't exist."""
|
|
29
24
|
# Setup git repo
|
|
30
25
|
git_dir = tmp_path / ".git"
|
|
31
26
|
git_dir.mkdir()
|
|
32
27
|
|
|
33
|
-
#
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
mock_mkdtemp.return_value = str(temp_dir)
|
|
37
|
-
|
|
38
|
-
# Mock subprocess to succeed
|
|
39
|
-
mock_subprocess.return_value = Mock(returncode=0)
|
|
28
|
+
# Create required pyproject.toml (needed for validation to not fail earlier)
|
|
29
|
+
pyproject_file = tmp_path / "pyproject.toml"
|
|
30
|
+
pyproject_file.write_text("[project]\nname = 'test'\n")
|
|
40
31
|
|
|
41
|
-
# Run
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
template_file = tmp_path / ".rhiza" / "template.yml"
|
|
46
|
-
assert template_file.exists()
|
|
47
|
-
|
|
48
|
-
# Verify it contains expected content
|
|
49
|
-
|
|
50
|
-
with open(template_file) as f:
|
|
51
|
-
config = yaml.safe_load(f)
|
|
52
|
-
|
|
53
|
-
assert config["template-repository"] == "jebel-quant/rhiza"
|
|
54
|
-
assert config["template-branch"] == "main"
|
|
55
|
-
assert ".github" in config["include"]
|
|
32
|
+
# Run materialize without creating template.yml first
|
|
33
|
+
# It should fail because template.yml doesn't exist
|
|
34
|
+
with pytest.raises(SystemExit):
|
|
35
|
+
materialize(tmp_path, "main", None, False)
|
|
56
36
|
|
|
57
37
|
@patch("rhiza.commands.materialize.subprocess.run")
|
|
58
38
|
@patch("rhiza.commands.materialize.shutil.rmtree")
|
|
@@ -64,6 +44,10 @@ class TestInjectCommand:
|
|
|
64
44
|
git_dir = tmp_path / ".git"
|
|
65
45
|
git_dir.mkdir()
|
|
66
46
|
|
|
47
|
+
# Create pyproject.toml for validation
|
|
48
|
+
pyproject_file = tmp_path / "pyproject.toml"
|
|
49
|
+
pyproject_file.write_text('[project]\nname = "test"\n')
|
|
50
|
+
|
|
67
51
|
# Create existing template.yml
|
|
68
52
|
rhiza_dir = tmp_path / ".rhiza"
|
|
69
53
|
rhiza_dir.mkdir(parents=True, exist_ok=True)
|
|
@@ -99,6 +83,10 @@ class TestInjectCommand:
|
|
|
99
83
|
git_dir = tmp_path / ".git"
|
|
100
84
|
git_dir.mkdir()
|
|
101
85
|
|
|
86
|
+
# Create pyproject.toml for validation
|
|
87
|
+
pyproject_file = tmp_path / "pyproject.toml"
|
|
88
|
+
pyproject_file.write_text('[project]\nname = "test"\n')
|
|
89
|
+
|
|
102
90
|
# Create template.yml with empty include
|
|
103
91
|
rhiza_dir = tmp_path / ".rhiza"
|
|
104
92
|
rhiza_dir.mkdir(parents=True, exist_ok=True)
|
|
@@ -121,6 +109,10 @@ class TestInjectCommand:
|
|
|
121
109
|
git_dir = tmp_path / ".git"
|
|
122
110
|
git_dir.mkdir()
|
|
123
111
|
|
|
112
|
+
# Create pyproject.toml for validation
|
|
113
|
+
pyproject_file = tmp_path / "pyproject.toml"
|
|
114
|
+
pyproject_file.write_text('[project]\nname = "test"\n')
|
|
115
|
+
|
|
124
116
|
# Create template.yml
|
|
125
117
|
rhiza_dir = tmp_path / ".rhiza"
|
|
126
118
|
rhiza_dir.mkdir(parents=True, exist_ok=True)
|
|
@@ -159,6 +151,10 @@ class TestInjectCommand:
|
|
|
159
151
|
git_dir = tmp_path / ".git"
|
|
160
152
|
git_dir.mkdir()
|
|
161
153
|
|
|
154
|
+
# Create pyproject.toml for validation
|
|
155
|
+
pyproject_file = tmp_path / "pyproject.toml"
|
|
156
|
+
pyproject_file.write_text('[project]\nname = "test"\n')
|
|
157
|
+
|
|
162
158
|
# Create existing file in target
|
|
163
159
|
existing_file = tmp_path / "test.txt"
|
|
164
160
|
existing_file.write_text("existing")
|
|
@@ -201,6 +197,10 @@ class TestInjectCommand:
|
|
|
201
197
|
git_dir = tmp_path / ".git"
|
|
202
198
|
git_dir.mkdir()
|
|
203
199
|
|
|
200
|
+
# Create pyproject.toml for validation
|
|
201
|
+
pyproject_file = tmp_path / "pyproject.toml"
|
|
202
|
+
pyproject_file.write_text('[project]\nname = "test"\n')
|
|
203
|
+
|
|
204
204
|
# Create existing file in target
|
|
205
205
|
existing_file = tmp_path / "test.txt"
|
|
206
206
|
existing_file.write_text("existing")
|
|
@@ -241,6 +241,10 @@ class TestInjectCommand:
|
|
|
241
241
|
git_dir = tmp_path / ".git"
|
|
242
242
|
git_dir.mkdir()
|
|
243
243
|
|
|
244
|
+
# Create pyproject.toml for validation
|
|
245
|
+
pyproject_file = tmp_path / "pyproject.toml"
|
|
246
|
+
pyproject_file.write_text('[project]\nname = "test"\n')
|
|
247
|
+
|
|
244
248
|
# Create template.yml with exclude
|
|
245
249
|
rhiza_dir = tmp_path / ".rhiza"
|
|
246
250
|
rhiza_dir.mkdir(parents=True, exist_ok=True)
|
|
@@ -289,6 +293,10 @@ class TestInjectCommand:
|
|
|
289
293
|
git_dir = tmp_path / ".git"
|
|
290
294
|
git_dir.mkdir()
|
|
291
295
|
|
|
296
|
+
# Create pyproject.toml for validation
|
|
297
|
+
pyproject_file = tmp_path / "pyproject.toml"
|
|
298
|
+
pyproject_file.write_text('[project]\nname = "test"\n')
|
|
299
|
+
|
|
292
300
|
# Create minimal template.yml
|
|
293
301
|
rhiza_dir = tmp_path / ".rhiza"
|
|
294
302
|
rhiza_dir.mkdir(parents=True, exist_ok=True)
|
|
@@ -320,6 +328,10 @@ class TestInjectCommand:
|
|
|
320
328
|
git_dir = tmp_path / ".git"
|
|
321
329
|
git_dir.mkdir()
|
|
322
330
|
|
|
331
|
+
# Create pyproject.toml for validation
|
|
332
|
+
pyproject_file = tmp_path / "pyproject.toml"
|
|
333
|
+
pyproject_file.write_text('[project]\nname = "test"\n')
|
|
334
|
+
|
|
323
335
|
# Create template.yml
|
|
324
336
|
rhiza_dir = tmp_path / ".rhiza"
|
|
325
337
|
rhiza_dir.mkdir(parents=True, exist_ok=True)
|
|
@@ -354,6 +366,10 @@ class TestInjectCommand:
|
|
|
354
366
|
git_dir = tmp_path / ".git"
|
|
355
367
|
git_dir.mkdir()
|
|
356
368
|
|
|
369
|
+
# Create pyproject.toml for validation
|
|
370
|
+
pyproject_file = tmp_path / "pyproject.toml"
|
|
371
|
+
pyproject_file.write_text('[project]\nname = "test"\n')
|
|
372
|
+
|
|
357
373
|
# Create template.yml
|
|
358
374
|
rhiza_dir = tmp_path / ".rhiza"
|
|
359
375
|
rhiza_dir.mkdir(parents=True, exist_ok=True)
|
|
@@ -410,6 +426,10 @@ class TestInjectCommand:
|
|
|
410
426
|
git_dir = tmp_path / ".git"
|
|
411
427
|
git_dir.mkdir()
|
|
412
428
|
|
|
429
|
+
# Create pyproject.toml for validation
|
|
430
|
+
pyproject_file = tmp_path / "pyproject.toml"
|
|
431
|
+
pyproject_file.write_text('[project]\nname = "test"\n')
|
|
432
|
+
|
|
413
433
|
# Create existing file that will be skipped
|
|
414
434
|
existing_file = tmp_path / "existing.txt"
|
|
415
435
|
existing_file.write_text("existing content")
|
|
@@ -460,6 +480,10 @@ class TestInjectCommand:
|
|
|
460
480
|
git_dir = tmp_path / ".git"
|
|
461
481
|
git_dir.mkdir()
|
|
462
482
|
|
|
483
|
+
# Create pyproject.toml for validation
|
|
484
|
+
pyproject_file = tmp_path / "pyproject.toml"
|
|
485
|
+
pyproject_file.write_text('[project]\nname = "test"\n')
|
|
486
|
+
|
|
463
487
|
# Create template.yml with gitlab host
|
|
464
488
|
rhiza_dir = tmp_path / ".rhiza"
|
|
465
489
|
rhiza_dir.mkdir(parents=True, exist_ok=True)
|
|
@@ -509,6 +533,10 @@ class TestInjectCommand:
|
|
|
509
533
|
git_dir = tmp_path / ".git"
|
|
510
534
|
git_dir.mkdir()
|
|
511
535
|
|
|
536
|
+
# Create pyproject.toml for validation
|
|
537
|
+
pyproject_file = tmp_path / "pyproject.toml"
|
|
538
|
+
pyproject_file.write_text('[project]\nname = "test"\n')
|
|
539
|
+
|
|
512
540
|
# Create template.yml with explicit github host
|
|
513
541
|
rhiza_dir = tmp_path / ".rhiza"
|
|
514
542
|
rhiza_dir.mkdir(parents=True, exist_ok=True)
|
|
@@ -550,6 +578,10 @@ class TestInjectCommand:
|
|
|
550
578
|
git_dir = tmp_path / ".git"
|
|
551
579
|
git_dir.mkdir()
|
|
552
580
|
|
|
581
|
+
# Create pyproject.toml for validation
|
|
582
|
+
pyproject_file = tmp_path / "pyproject.toml"
|
|
583
|
+
pyproject_file.write_text('[project]\nname = "test"\n')
|
|
584
|
+
|
|
553
585
|
# Create template.yml with invalid host
|
|
554
586
|
rhiza_dir = tmp_path / ".rhiza"
|
|
555
587
|
rhiza_dir.mkdir(parents=True, exist_ok=True)
|
|
@@ -587,6 +619,10 @@ class TestInjectCommand:
|
|
|
587
619
|
git_dir = tmp_path / ".git"
|
|
588
620
|
git_dir.mkdir()
|
|
589
621
|
|
|
622
|
+
# Create pyproject.toml for validation
|
|
623
|
+
pyproject_file = tmp_path / "pyproject.toml"
|
|
624
|
+
pyproject_file.write_text('[project]\nname = "test"\n')
|
|
625
|
+
|
|
590
626
|
# Create src and tests folders to avoid validation warnings
|
|
591
627
|
(tmp_path / "src").mkdir()
|
|
592
628
|
(tmp_path / "tests").mkdir()
|
|
@@ -630,13 +666,17 @@ class TestInjectCommand:
|
|
|
630
666
|
assert "workflow" in call_args.lower()
|
|
631
667
|
assert "permission" in call_args.lower()
|
|
632
668
|
|
|
633
|
-
@patch("rhiza.commands.materialize.
|
|
634
|
-
def
|
|
669
|
+
@patch("rhiza.commands.materialize.validate")
|
|
670
|
+
def test_materialize_raises_error_when_validate_bypassed_with_empty_include(self, mock_validate, tmp_path):
|
|
635
671
|
"""Test that materialize raises RuntimeError when include_paths is empty after validation."""
|
|
636
672
|
# Setup git repo
|
|
637
673
|
git_dir = tmp_path / ".git"
|
|
638
674
|
git_dir.mkdir()
|
|
639
675
|
|
|
676
|
+
# Create pyproject.toml for validation
|
|
677
|
+
pyproject_file = tmp_path / "pyproject.toml"
|
|
678
|
+
pyproject_file.write_text('[project]\nname = "test"\n')
|
|
679
|
+
|
|
640
680
|
# Create template.yml with empty include (bypassing normal validation)
|
|
641
681
|
rhiza_dir = tmp_path / ".rhiza"
|
|
642
682
|
rhiza_dir.mkdir(parents=True, exist_ok=True)
|
|
@@ -652,8 +692,10 @@ class TestInjectCommand:
|
|
|
652
692
|
f,
|
|
653
693
|
)
|
|
654
694
|
|
|
655
|
-
# Mock
|
|
656
|
-
|
|
695
|
+
# Mock validate to return True to bypass normal validation that would catch empty include lists.
|
|
696
|
+
# This test validates materialize's runtime error handling for the theoretical edge case
|
|
697
|
+
# where validation passes but include_paths is still empty (e.g., validation logic gaps).
|
|
698
|
+
mock_validate.return_value = True
|
|
657
699
|
|
|
658
700
|
# Run materialize and expect RuntimeError
|
|
659
701
|
with pytest.raises(RuntimeError, match="No include paths found"):
|
|
@@ -669,6 +711,10 @@ class TestInjectCommand:
|
|
|
669
711
|
git_dir = tmp_path / ".git"
|
|
670
712
|
git_dir.mkdir()
|
|
671
713
|
|
|
714
|
+
# Create pyproject.toml for validation
|
|
715
|
+
pyproject_file = tmp_path / "pyproject.toml"
|
|
716
|
+
pyproject_file.write_text('[project]\nname = "test"\n')
|
|
717
|
+
|
|
672
718
|
# Create template.yml
|
|
673
719
|
rhiza_dir = tmp_path / ".rhiza"
|
|
674
720
|
rhiza_dir.mkdir(parents=True, exist_ok=True)
|
|
@@ -726,6 +772,10 @@ class TestInjectCommand:
|
|
|
726
772
|
git_dir = tmp_path / ".git"
|
|
727
773
|
git_dir.mkdir()
|
|
728
774
|
|
|
775
|
+
# Create pyproject.toml for validation
|
|
776
|
+
pyproject_file = tmp_path / "pyproject.toml"
|
|
777
|
+
pyproject_file.write_text('[project]\nname = "test"\n')
|
|
778
|
+
|
|
729
779
|
# Create template.yml
|
|
730
780
|
rhiza_dir = tmp_path / ".rhiza"
|
|
731
781
|
rhiza_dir.mkdir(parents=True, exist_ok=True)
|
|
@@ -780,6 +830,10 @@ class TestInjectCommand:
|
|
|
780
830
|
git_dir = tmp_path / ".git"
|
|
781
831
|
git_dir.mkdir()
|
|
782
832
|
|
|
833
|
+
# Create pyproject.toml for validation
|
|
834
|
+
pyproject_file = tmp_path / "pyproject.toml"
|
|
835
|
+
pyproject_file.write_text('[project]\nname = "test"\n')
|
|
836
|
+
|
|
783
837
|
# Create template.yml
|
|
784
838
|
rhiza_dir = tmp_path / ".rhiza"
|
|
785
839
|
rhiza_dir.mkdir(parents=True, exist_ok=True)
|
|
@@ -825,6 +879,10 @@ class TestInjectCommand:
|
|
|
825
879
|
git_dir = tmp_path / ".git"
|
|
826
880
|
git_dir.mkdir()
|
|
827
881
|
|
|
882
|
+
# Create pyproject.toml for validation
|
|
883
|
+
pyproject_file = tmp_path / "pyproject.toml"
|
|
884
|
+
pyproject_file.write_text('[project]\nname = "test"\n')
|
|
885
|
+
|
|
828
886
|
# Create template.yml
|
|
829
887
|
rhiza_dir = tmp_path / ".rhiza"
|
|
830
888
|
rhiza_dir.mkdir(parents=True, exist_ok=True)
|
|
@@ -864,6 +922,10 @@ class TestInjectCommand:
|
|
|
864
922
|
git_dir = tmp_path / ".git"
|
|
865
923
|
git_dir.mkdir()
|
|
866
924
|
|
|
925
|
+
# Create pyproject.toml for validation
|
|
926
|
+
pyproject_file = tmp_path / "pyproject.toml"
|
|
927
|
+
pyproject_file.write_text('[project]\nname = "test"\n')
|
|
928
|
+
|
|
867
929
|
# Create template.yml
|
|
868
930
|
rhiza_dir = tmp_path / ".rhiza"
|
|
869
931
|
rhiza_dir.mkdir(parents=True, exist_ok=True)
|
|
@@ -898,6 +960,10 @@ class TestInjectCommand:
|
|
|
898
960
|
git_dir = tmp_path / ".git"
|
|
899
961
|
git_dir.mkdir()
|
|
900
962
|
|
|
963
|
+
# Create pyproject.toml for validation
|
|
964
|
+
pyproject_file = tmp_path / "pyproject.toml"
|
|
965
|
+
pyproject_file.write_text('[project]\nname = "test"\n')
|
|
966
|
+
|
|
901
967
|
# Create template.yml
|
|
902
968
|
rhiza_dir = tmp_path / ".rhiza"
|
|
903
969
|
rhiza_dir.mkdir(parents=True, exist_ok=True)
|
|
@@ -951,6 +1017,10 @@ class TestInjectCommand:
|
|
|
951
1017
|
git_dir = tmp_path / ".git"
|
|
952
1018
|
git_dir.mkdir()
|
|
953
1019
|
|
|
1020
|
+
# Create pyproject.toml for validation
|
|
1021
|
+
pyproject_file = tmp_path / "pyproject.toml"
|
|
1022
|
+
pyproject_file.write_text('[project]\nname = "test"\n')
|
|
1023
|
+
|
|
954
1024
|
# Create template.yml
|
|
955
1025
|
rhiza_dir = tmp_path / ".rhiza"
|
|
956
1026
|
rhiza_dir.mkdir(parents=True, exist_ok=True)
|
|
@@ -1009,6 +1079,10 @@ class TestInjectCommand:
|
|
|
1009
1079
|
git_dir = tmp_path / ".git"
|
|
1010
1080
|
git_dir.mkdir()
|
|
1011
1081
|
|
|
1082
|
+
# Create pyproject.toml for validation
|
|
1083
|
+
pyproject_file = tmp_path / "pyproject.toml"
|
|
1084
|
+
pyproject_file.write_text('[project]\nname = "test"\n')
|
|
1085
|
+
|
|
1012
1086
|
# Create an old .rhiza/history file with files that will become orphaned
|
|
1013
1087
|
rhiza_dir = tmp_path / ".rhiza"
|
|
1014
1088
|
rhiza_dir.mkdir(parents=True, exist_ok=True)
|
|
@@ -1082,6 +1156,10 @@ class TestInjectCommand:
|
|
|
1082
1156
|
git_dir = tmp_path / ".git"
|
|
1083
1157
|
git_dir.mkdir()
|
|
1084
1158
|
|
|
1159
|
+
# Create pyproject.toml for validation
|
|
1160
|
+
pyproject_file = tmp_path / "pyproject.toml"
|
|
1161
|
+
pyproject_file.write_text('[project]\nname = "test"\n')
|
|
1162
|
+
|
|
1085
1163
|
# Create an old .rhiza/history file with a file that doesn't exist
|
|
1086
1164
|
rhiza_dir = tmp_path / ".rhiza"
|
|
1087
1165
|
rhiza_dir.mkdir(parents=True, exist_ok=True)
|
|
@@ -1143,6 +1221,10 @@ class TestInjectCommand:
|
|
|
1143
1221
|
git_dir = tmp_path / ".git"
|
|
1144
1222
|
git_dir.mkdir()
|
|
1145
1223
|
|
|
1224
|
+
# Create pyproject.toml for validation
|
|
1225
|
+
pyproject_file = tmp_path / "pyproject.toml"
|
|
1226
|
+
pyproject_file.write_text('[project]\nname = "test"\n')
|
|
1227
|
+
|
|
1146
1228
|
# No old .rhiza/history file
|
|
1147
1229
|
|
|
1148
1230
|
# Create template.yml
|
|
@@ -1193,6 +1275,10 @@ class TestInjectCommand:
|
|
|
1193
1275
|
git_dir = tmp_path / ".git"
|
|
1194
1276
|
git_dir.mkdir()
|
|
1195
1277
|
|
|
1278
|
+
# Create pyproject.toml for validation
|
|
1279
|
+
pyproject_file = tmp_path / "pyproject.toml"
|
|
1280
|
+
pyproject_file.write_text('[project]\nname = "test"\n')
|
|
1281
|
+
|
|
1196
1282
|
# Create .rhiza/history with a file that will be orphaned
|
|
1197
1283
|
rhiza_dir = tmp_path / ".rhiza"
|
|
1198
1284
|
rhiza_dir.mkdir(parents=True, exist_ok=True)
|
|
@@ -1235,3 +1321,185 @@ class TestInjectCommand:
|
|
|
1235
1321
|
|
|
1236
1322
|
# Verify the file still exists (deletion failed but was handled)
|
|
1237
1323
|
assert old_file.exists()
|
|
1324
|
+
|
|
1325
|
+
@patch("rhiza.commands.materialize.subprocess.run")
|
|
1326
|
+
@patch("rhiza.commands.materialize.shutil.rmtree")
|
|
1327
|
+
@patch("rhiza.commands.materialize.shutil.copy2")
|
|
1328
|
+
@patch("rhiza.commands.materialize.tempfile.mkdtemp")
|
|
1329
|
+
def test_materialize_with_legacy_history_location(
|
|
1330
|
+
self, mock_mkdtemp, mock_copy2, mock_rmtree, mock_subprocess, tmp_path
|
|
1331
|
+
):
|
|
1332
|
+
"""Test that materialize reads history from legacy .rhiza.history location."""
|
|
1333
|
+
# Setup git repo
|
|
1334
|
+
git_dir = tmp_path / ".git"
|
|
1335
|
+
git_dir.mkdir()
|
|
1336
|
+
|
|
1337
|
+
# Create pyproject.toml for validation
|
|
1338
|
+
pyproject_file = tmp_path / "pyproject.toml"
|
|
1339
|
+
pyproject_file.write_text('[project]\nname = "test"\n')
|
|
1340
|
+
|
|
1341
|
+
# Create old .rhiza.history file (legacy location at root)
|
|
1342
|
+
old_history_file = tmp_path / ".rhiza.history"
|
|
1343
|
+
old_history_file.write_text("old_file.txt\n")
|
|
1344
|
+
|
|
1345
|
+
# Create the file that was in history
|
|
1346
|
+
old_file = tmp_path / "old_file.txt"
|
|
1347
|
+
old_file.write_text("old content")
|
|
1348
|
+
|
|
1349
|
+
# Create template.yml
|
|
1350
|
+
rhiza_dir = tmp_path / ".rhiza"
|
|
1351
|
+
rhiza_dir.mkdir(parents=True, exist_ok=True)
|
|
1352
|
+
template_file = rhiza_dir / "template.yml"
|
|
1353
|
+
|
|
1354
|
+
with open(template_file, "w") as f:
|
|
1355
|
+
yaml.dump(
|
|
1356
|
+
{
|
|
1357
|
+
"template-repository": "jebel-quant/rhiza",
|
|
1358
|
+
"template-branch": "main",
|
|
1359
|
+
"include": ["new_file.txt"],
|
|
1360
|
+
},
|
|
1361
|
+
f,
|
|
1362
|
+
)
|
|
1363
|
+
|
|
1364
|
+
# Mock tempfile
|
|
1365
|
+
temp_dir = tmp_path / "temp"
|
|
1366
|
+
temp_dir.mkdir()
|
|
1367
|
+
new_file = temp_dir / "new_file.txt"
|
|
1368
|
+
new_file.write_text("new content")
|
|
1369
|
+
mock_mkdtemp.return_value = str(temp_dir)
|
|
1370
|
+
|
|
1371
|
+
# Mock subprocess to succeed
|
|
1372
|
+
mock_subprocess.return_value = Mock(returncode=0)
|
|
1373
|
+
|
|
1374
|
+
# Run materialize - should read from legacy location and delete orphaned file
|
|
1375
|
+
materialize(tmp_path, "main", None, False)
|
|
1376
|
+
|
|
1377
|
+
# Verify old file was deleted (it was in old history but not in new template)
|
|
1378
|
+
assert not old_file.exists()
|
|
1379
|
+
|
|
1380
|
+
# Verify new history file was created in new location
|
|
1381
|
+
new_history_file = tmp_path / ".rhiza" / "history"
|
|
1382
|
+
assert new_history_file.exists()
|
|
1383
|
+
|
|
1384
|
+
@patch("rhiza.commands.materialize.subprocess.run")
|
|
1385
|
+
@patch("rhiza.commands.materialize.shutil.rmtree")
|
|
1386
|
+
@patch("rhiza.commands.materialize.shutil.copy2")
|
|
1387
|
+
@patch("rhiza.commands.materialize.tempfile.mkdtemp")
|
|
1388
|
+
def test_materialize_cleans_up_legacy_history_file(
|
|
1389
|
+
self, mock_mkdtemp, mock_copy2, mock_rmtree, mock_subprocess, tmp_path
|
|
1390
|
+
):
|
|
1391
|
+
"""Test that materialize removes old .rhiza.history after migration."""
|
|
1392
|
+
# Setup git repo
|
|
1393
|
+
git_dir = tmp_path / ".git"
|
|
1394
|
+
git_dir.mkdir()
|
|
1395
|
+
|
|
1396
|
+
# Create pyproject.toml for validation
|
|
1397
|
+
pyproject_file = tmp_path / "pyproject.toml"
|
|
1398
|
+
pyproject_file.write_text('[project]\nname = "test"\n')
|
|
1399
|
+
|
|
1400
|
+
# Create both old and new history files
|
|
1401
|
+
old_history_file = tmp_path / ".rhiza.history"
|
|
1402
|
+
old_history_file.write_text("file1.txt\n")
|
|
1403
|
+
|
|
1404
|
+
rhiza_dir = tmp_path / ".rhiza"
|
|
1405
|
+
rhiza_dir.mkdir(parents=True, exist_ok=True)
|
|
1406
|
+
new_history_file = rhiza_dir / "history"
|
|
1407
|
+
new_history_file.write_text("file1.txt\n")
|
|
1408
|
+
|
|
1409
|
+
# Create template.yml
|
|
1410
|
+
template_file = rhiza_dir / "template.yml"
|
|
1411
|
+
|
|
1412
|
+
with open(template_file, "w") as f:
|
|
1413
|
+
yaml.dump(
|
|
1414
|
+
{
|
|
1415
|
+
"template-repository": "jebel-quant/rhiza",
|
|
1416
|
+
"template-branch": "main",
|
|
1417
|
+
"include": ["file1.txt"],
|
|
1418
|
+
},
|
|
1419
|
+
f,
|
|
1420
|
+
)
|
|
1421
|
+
|
|
1422
|
+
# Mock tempfile
|
|
1423
|
+
temp_dir = tmp_path / "temp"
|
|
1424
|
+
temp_dir.mkdir()
|
|
1425
|
+
file1 = temp_dir / "file1.txt"
|
|
1426
|
+
file1.write_text("content1")
|
|
1427
|
+
mock_mkdtemp.return_value = str(temp_dir)
|
|
1428
|
+
|
|
1429
|
+
# Mock subprocess to succeed
|
|
1430
|
+
mock_subprocess.return_value = Mock(returncode=0)
|
|
1431
|
+
|
|
1432
|
+
# Run materialize
|
|
1433
|
+
materialize(tmp_path, "main", None, False)
|
|
1434
|
+
|
|
1435
|
+
# Verify old history file was removed
|
|
1436
|
+
assert not old_history_file.exists()
|
|
1437
|
+
|
|
1438
|
+
# Verify new history file still exists
|
|
1439
|
+
assert new_history_file.exists()
|
|
1440
|
+
|
|
1441
|
+
@patch("rhiza.commands.materialize.subprocess.run")
|
|
1442
|
+
@patch("rhiza.commands.materialize.shutil.rmtree")
|
|
1443
|
+
@patch("rhiza.commands.materialize.shutil.copy2")
|
|
1444
|
+
@patch("rhiza.commands.materialize.tempfile.mkdtemp")
|
|
1445
|
+
def test_materialize_handles_legacy_history_cleanup_failure(
|
|
1446
|
+
self, mock_mkdtemp, mock_copy2, mock_rmtree, mock_subprocess, tmp_path
|
|
1447
|
+
):
|
|
1448
|
+
"""Test that materialize handles failure to remove old .rhiza.history gracefully."""
|
|
1449
|
+
# Setup git repo
|
|
1450
|
+
git_dir = tmp_path / ".git"
|
|
1451
|
+
git_dir.mkdir()
|
|
1452
|
+
|
|
1453
|
+
# Create pyproject.toml for validation
|
|
1454
|
+
pyproject_file = tmp_path / "pyproject.toml"
|
|
1455
|
+
pyproject_file.write_text('[project]\nname = "test"\n')
|
|
1456
|
+
|
|
1457
|
+
# Create both old and new history files
|
|
1458
|
+
old_history_file = tmp_path / ".rhiza.history"
|
|
1459
|
+
old_history_file.write_text("file1.txt\n")
|
|
1460
|
+
|
|
1461
|
+
rhiza_dir = tmp_path / ".rhiza"
|
|
1462
|
+
rhiza_dir.mkdir(parents=True, exist_ok=True)
|
|
1463
|
+
new_history_file = rhiza_dir / "history"
|
|
1464
|
+
new_history_file.write_text("file1.txt\n")
|
|
1465
|
+
|
|
1466
|
+
# Create template.yml
|
|
1467
|
+
template_file = rhiza_dir / "template.yml"
|
|
1468
|
+
|
|
1469
|
+
with open(template_file, "w") as f:
|
|
1470
|
+
yaml.dump(
|
|
1471
|
+
{
|
|
1472
|
+
"template-repository": "jebel-quant/rhiza",
|
|
1473
|
+
"template-branch": "main",
|
|
1474
|
+
"include": ["file1.txt"],
|
|
1475
|
+
},
|
|
1476
|
+
f,
|
|
1477
|
+
)
|
|
1478
|
+
|
|
1479
|
+
# Mock tempfile
|
|
1480
|
+
temp_dir = tmp_path / "temp"
|
|
1481
|
+
temp_dir.mkdir()
|
|
1482
|
+
file1 = temp_dir / "file1.txt"
|
|
1483
|
+
file1.write_text("content1")
|
|
1484
|
+
mock_mkdtemp.return_value = str(temp_dir)
|
|
1485
|
+
|
|
1486
|
+
# Mock subprocess to succeed
|
|
1487
|
+
mock_subprocess.return_value = Mock(returncode=0)
|
|
1488
|
+
|
|
1489
|
+
# Mock unlink to fail for the old history file
|
|
1490
|
+
original_unlink = Path.unlink
|
|
1491
|
+
|
|
1492
|
+
def selective_unlink(self, *args, **kwargs):
|
|
1493
|
+
if self.name == ".rhiza.history":
|
|
1494
|
+
raise PermissionError("Cannot delete old history file")
|
|
1495
|
+
return original_unlink(self, *args, **kwargs)
|
|
1496
|
+
|
|
1497
|
+
with patch.object(Path, "unlink", selective_unlink):
|
|
1498
|
+
# Run materialize - should handle cleanup failure gracefully
|
|
1499
|
+
materialize(tmp_path, "main", None, False)
|
|
1500
|
+
|
|
1501
|
+
# Verify old history file still exists (cleanup failed but was handled)
|
|
1502
|
+
assert old_history_file.exists()
|
|
1503
|
+
|
|
1504
|
+
# Verify new history file was still created
|
|
1505
|
+
assert new_history_file.exists()
|
|
@@ -52,7 +52,8 @@ class TestMigrateCommand:
|
|
|
52
52
|
|
|
53
53
|
assert migrated_content["template-repository"] == "test/repo"
|
|
54
54
|
assert migrated_content["template-branch"] == "main"
|
|
55
|
-
|
|
55
|
+
# After migration, .rhiza should be automatically added to include list
|
|
56
|
+
assert migrated_content["include"] == [".github", "Makefile", ".rhiza"]
|
|
56
57
|
|
|
57
58
|
# Verify old file was removed
|
|
58
59
|
assert not old_template_file.exists()
|
|
@@ -167,6 +168,50 @@ Makefile
|
|
|
167
168
|
new_history_file = rhiza_dir / "history"
|
|
168
169
|
assert not new_history_file.exists()
|
|
169
170
|
|
|
171
|
+
def test_migrate_skips_history_when_both_exist(self, tmp_path):
|
|
172
|
+
"""Test that migrate skips history migration when both old and new exist."""
|
|
173
|
+
# Create existing .rhiza.history
|
|
174
|
+
old_history_file = tmp_path / ".rhiza.history"
|
|
175
|
+
old_content = "# Old history content\nold_file.txt\n"
|
|
176
|
+
old_history_file.write_text(old_content)
|
|
177
|
+
|
|
178
|
+
# Create existing .rhiza/history (already migrated)
|
|
179
|
+
rhiza_dir = tmp_path / ".rhiza"
|
|
180
|
+
rhiza_dir.mkdir(parents=True)
|
|
181
|
+
new_history_file = rhiza_dir / "history"
|
|
182
|
+
new_content = "# New history content\nnew_file.txt\n"
|
|
183
|
+
new_history_file.write_text(new_content)
|
|
184
|
+
|
|
185
|
+
# Run migrate
|
|
186
|
+
migrate(tmp_path)
|
|
187
|
+
|
|
188
|
+
# Verify new history file was NOT overwritten
|
|
189
|
+
assert new_history_file.read_text() == new_content
|
|
190
|
+
|
|
191
|
+
# Verify old file still exists (not removed since target exists)
|
|
192
|
+
assert old_history_file.exists()
|
|
193
|
+
assert old_history_file.read_text() == old_content
|
|
194
|
+
|
|
195
|
+
def test_migrate_handles_existing_rhiza_history(self, tmp_path):
|
|
196
|
+
"""Test that migrate handles when .rhiza/history already exists but .rhiza.history doesn't."""
|
|
197
|
+
# Create existing .rhiza/history (no old file)
|
|
198
|
+
rhiza_dir = tmp_path / ".rhiza"
|
|
199
|
+
rhiza_dir.mkdir(parents=True)
|
|
200
|
+
new_history_file = rhiza_dir / "history"
|
|
201
|
+
existing_content = "# Existing history\nfile.txt\n"
|
|
202
|
+
new_history_file.write_text(existing_content)
|
|
203
|
+
|
|
204
|
+
# Run migrate without creating .rhiza.history
|
|
205
|
+
migrate(tmp_path)
|
|
206
|
+
|
|
207
|
+
# Verify .rhiza/history is unchanged
|
|
208
|
+
assert new_history_file.exists()
|
|
209
|
+
assert new_history_file.read_text() == existing_content
|
|
210
|
+
|
|
211
|
+
# Verify no old file was created
|
|
212
|
+
old_history_file = tmp_path / ".rhiza.history"
|
|
213
|
+
assert not old_history_file.exists()
|
|
214
|
+
|
|
170
215
|
def test_migrate_skips_existing_files(self, tmp_path):
|
|
171
216
|
"""Test that migrate skips existing files in .rhiza."""
|
|
172
217
|
# Create existing .rhiza/template.yml
|
|
@@ -195,6 +240,92 @@ Makefile
|
|
|
195
240
|
|
|
196
241
|
assert content["template-repository"] == "existing/repo"
|
|
197
242
|
|
|
243
|
+
def test_migrate_adds_rhiza_to_include_list(self, tmp_path):
|
|
244
|
+
"""Test that migrate adds .rhiza to include list if not present."""
|
|
245
|
+
# Create existing template.yml in .github/rhiza/ without .rhiza in include
|
|
246
|
+
github_rhiza_dir = tmp_path / ".github" / "rhiza"
|
|
247
|
+
github_rhiza_dir.mkdir(parents=True)
|
|
248
|
+
old_template_file = github_rhiza_dir / "template.yml"
|
|
249
|
+
|
|
250
|
+
template_content = {
|
|
251
|
+
"template-repository": "test/repo",
|
|
252
|
+
"template-branch": "main",
|
|
253
|
+
"include": [".github", "Makefile"],
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
with open(old_template_file, "w") as f:
|
|
257
|
+
yaml.dump(template_content, f)
|
|
258
|
+
|
|
259
|
+
# Run migrate
|
|
260
|
+
migrate(tmp_path)
|
|
261
|
+
|
|
262
|
+
# Verify new template.yml was created
|
|
263
|
+
new_template_file = tmp_path / ".rhiza" / "template.yml"
|
|
264
|
+
assert new_template_file.exists()
|
|
265
|
+
|
|
266
|
+
# Verify .rhiza was added to include list
|
|
267
|
+
with open(new_template_file) as f:
|
|
268
|
+
migrated_content = yaml.safe_load(f)
|
|
269
|
+
|
|
270
|
+
assert ".rhiza" in migrated_content["include"]
|
|
271
|
+
assert migrated_content["include"] == [".github", "Makefile", ".rhiza"]
|
|
272
|
+
|
|
273
|
+
def test_migrate_does_not_duplicate_rhiza_in_include_list(self, tmp_path):
|
|
274
|
+
"""Test that migrate does not duplicate .rhiza if already in include list."""
|
|
275
|
+
# Create existing template.yml in .github/rhiza/ with .rhiza already in include
|
|
276
|
+
github_rhiza_dir = tmp_path / ".github" / "rhiza"
|
|
277
|
+
github_rhiza_dir.mkdir(parents=True)
|
|
278
|
+
old_template_file = github_rhiza_dir / "template.yml"
|
|
279
|
+
|
|
280
|
+
template_content = {
|
|
281
|
+
"template-repository": "test/repo",
|
|
282
|
+
"template-branch": "main",
|
|
283
|
+
"include": [".github", ".rhiza", "Makefile"],
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
with open(old_template_file, "w") as f:
|
|
287
|
+
yaml.dump(template_content, f)
|
|
288
|
+
|
|
289
|
+
# Run migrate
|
|
290
|
+
migrate(tmp_path)
|
|
291
|
+
|
|
292
|
+
# Verify new template.yml was created
|
|
293
|
+
new_template_file = tmp_path / ".rhiza" / "template.yml"
|
|
294
|
+
assert new_template_file.exists()
|
|
295
|
+
|
|
296
|
+
# Verify .rhiza appears only once in include list
|
|
297
|
+
with open(new_template_file) as f:
|
|
298
|
+
migrated_content = yaml.safe_load(f)
|
|
299
|
+
|
|
300
|
+
assert migrated_content["include"].count(".rhiza") == 1
|
|
301
|
+
assert migrated_content["include"] == [".github", ".rhiza", "Makefile"]
|
|
302
|
+
|
|
303
|
+
def test_migrate_adds_rhiza_to_existing_template(self, tmp_path):
|
|
304
|
+
"""Test that migrate adds .rhiza to include list for existing .rhiza/template.yml."""
|
|
305
|
+
# Create existing template.yml directly in .rhiza/ without .rhiza in include
|
|
306
|
+
rhiza_dir = tmp_path / ".rhiza"
|
|
307
|
+
rhiza_dir.mkdir(parents=True)
|
|
308
|
+
template_file = rhiza_dir / "template.yml"
|
|
309
|
+
|
|
310
|
+
template_content = {
|
|
311
|
+
"template-repository": "test/repo",
|
|
312
|
+
"template-branch": "main",
|
|
313
|
+
"include": ["src", "tests"],
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
with open(template_file, "w") as f:
|
|
317
|
+
yaml.dump(template_content, f)
|
|
318
|
+
|
|
319
|
+
# Run migrate
|
|
320
|
+
migrate(tmp_path)
|
|
321
|
+
|
|
322
|
+
# Verify .rhiza was added to include list
|
|
323
|
+
with open(template_file) as f:
|
|
324
|
+
updated_content = yaml.safe_load(f)
|
|
325
|
+
|
|
326
|
+
assert ".rhiza" in updated_content["include"]
|
|
327
|
+
assert updated_content["include"] == ["src", "tests", ".rhiza"]
|
|
328
|
+
|
|
198
329
|
|
|
199
330
|
class TestMigrateCLI:
|
|
200
331
|
"""Tests for the migrate CLI command."""
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|