feat: add archive_folder support for preserving previous generations
When archive_folder is set in the project config, artifacts are moved to numbered archive copies (e.g. x.01.jpg, x.02.jpg) instead of being overwritten or deleted. - Build command archives existing artifacts before rebuilding dirty targets - Clean command moves files to archive instead of deleting them - Subfolder structure is preserved in the archive directory - State file is always deleted, never archived
This commit is contained in:
parent
9ace38c806
commit
24cade558a
7 changed files with 272 additions and 8 deletions
|
|
@ -174,6 +174,69 @@ class TestCleanCommand:
|
|||
assert "Cleaned 1 artifact(s)" in result.output
|
||||
assert not (cli_project / "output.txt").exists()
|
||||
|
||||
def test_clean_archives_when_archive_folder_set(self, tmp_path: Path) -> None:
|
||||
config = {
|
||||
"archive_folder": "archive",
|
||||
"targets": {
|
||||
"output.txt": {"prompt": "Generate text"},
|
||||
"image.png": {"prompt": "Generate image"},
|
||||
},
|
||||
}
|
||||
_ = (tmp_path / "project.hokusai.yaml").write_text(
|
||||
yaml.dump(config, default_flow_style=False)
|
||||
)
|
||||
_ = (tmp_path / "output.txt").write_text("generated text")
|
||||
_ = (tmp_path / "image.png").write_bytes(b"\x89PNG")
|
||||
|
||||
with patch("hokusai.cli.Path") as mock_path_cls:
|
||||
mock_path_cls.cwd.return_value = tmp_path
|
||||
result = runner.invoke(app, ["clean"])
|
||||
|
||||
assert result.exit_code == 0
|
||||
assert "Archived 2 artifact(s)" in result.output
|
||||
assert "mv" in result.output
|
||||
assert not (tmp_path / "output.txt").exists()
|
||||
assert not (tmp_path / "image.png").exists()
|
||||
assert (tmp_path / "archive" / "output.01.txt").read_text() == "generated text"
|
||||
assert (tmp_path / "archive" / "image.01.png").read_bytes() == b"\x89PNG"
|
||||
|
||||
def test_clean_archive_preserves_subfolders(self, tmp_path: Path) -> None:
|
||||
config = {
|
||||
"archive_folder": "archive",
|
||||
"targets": {"img/photo.png": {"prompt": "photo"}},
|
||||
}
|
||||
_ = (tmp_path / "project.hokusai.yaml").write_text(
|
||||
yaml.dump(config, default_flow_style=False)
|
||||
)
|
||||
(tmp_path / "img").mkdir()
|
||||
_ = (tmp_path / "img" / "photo.png").write_bytes(b"img")
|
||||
|
||||
with patch("hokusai.cli.Path") as mock_path_cls:
|
||||
mock_path_cls.cwd.return_value = tmp_path
|
||||
result = runner.invoke(app, ["clean"])
|
||||
|
||||
assert result.exit_code == 0
|
||||
assert (tmp_path / "archive" / "img" / "photo.01.png").exists()
|
||||
|
||||
def test_clean_archive_still_deletes_state(self, tmp_path: Path) -> None:
|
||||
config = {
|
||||
"archive_folder": "archive",
|
||||
"targets": {"output.txt": {"prompt": "text"}},
|
||||
}
|
||||
_ = (tmp_path / "project.hokusai.yaml").write_text(
|
||||
yaml.dump(config, default_flow_style=False)
|
||||
)
|
||||
_ = (tmp_path / "output.txt").write_text("data")
|
||||
state_file = ".project.hokusai-state.yaml"
|
||||
_ = (tmp_path / state_file).write_text("targets: {}")
|
||||
|
||||
with patch("hokusai.cli.Path") as mock_path_cls:
|
||||
mock_path_cls.cwd.return_value = tmp_path
|
||||
result = runner.invoke(app, ["clean"])
|
||||
|
||||
assert result.exit_code == 0
|
||||
assert not (tmp_path / state_file).exists()
|
||||
|
||||
|
||||
class TestGraphCommand:
|
||||
"""Test the graph CLI command."""
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue