feat(pipeline): support incremental runs and analysis caching
Reuse existing output directory by default instead of failing Cache successful Gemini analysis results using content hashing Skip unchanged files and retry failed analyses on subsequent runs Update --force flag to explicitly delete and rebuild the output
This commit is contained in:
@@ -64,6 +64,15 @@ class FlakyAnalyzer:
|
||||
return f"# {relative_path.as_posix()}\n\n{len(content)}"
|
||||
|
||||
|
||||
class RecordingAnalyzer:
|
||||
def __init__(self) -> None:
|
||||
self.paths: list[str] = []
|
||||
|
||||
def analyze(self, relative_path: Path, content: str) -> str:
|
||||
self.paths.append(relative_path.as_posix())
|
||||
return f"# {relative_path.as_posix()}\n\n{len(content)}"
|
||||
|
||||
|
||||
class PipelineTests(unittest.TestCase):
|
||||
def test_strip_comment_out_blocks_removes_nested_blocks(self) -> None:
|
||||
source = "<root><ui:CommentOut><x/><ui:CommentOut><y/></ui:CommentOut></ui:CommentOut><z/></root>"
|
||||
@@ -173,6 +182,47 @@ class PipelineTests(unittest.TestCase):
|
||||
self.assertTrue((docs_root / "Scripts" / "Keep.bas.analysis.md").exists())
|
||||
self.assertTrue(any("Analysis failed for Flows/Active.xaml" in item for item in report.warnings))
|
||||
|
||||
def test_pipeline_resume_skips_successfully_cached_analyses(self) -> None:
|
||||
with TemporaryDirectory() as tmp:
|
||||
tmp_path = Path(tmp)
|
||||
project_root = tmp_path / "project"
|
||||
output_root = tmp_path / "workspace"
|
||||
(project_root / "Flows").mkdir(parents=True)
|
||||
(project_root / "Flows" / "Active.xaml").write_text(ACTIVE_XAML, encoding="utf-8")
|
||||
(project_root / "Scripts").mkdir()
|
||||
(project_root / "Scripts" / "Keep.bas").write_text("Sub Keep()\nEnd Sub", encoding="utf-8")
|
||||
(project_root / "main.xaml").write_text(MAIN_XAML, encoding="utf-8")
|
||||
|
||||
first = RecordingAnalyzer()
|
||||
ProjectPipeline(project_root, output_root, "main.xaml", force=True).run(first)
|
||||
self.assertEqual(
|
||||
first.paths,
|
||||
["Flows/Active.xaml", "main.xaml", "Scripts/Keep.bas"],
|
||||
)
|
||||
|
||||
second = RecordingAnalyzer()
|
||||
ProjectPipeline(project_root, output_root, "main.xaml", force=False).run(second)
|
||||
self.assertEqual(second.paths, [])
|
||||
|
||||
def test_pipeline_resume_retries_failed_analysis_and_reanalyzes_changed_files(self) -> None:
|
||||
with TemporaryDirectory() as tmp:
|
||||
tmp_path = Path(tmp)
|
||||
project_root = tmp_path / "project"
|
||||
output_root = tmp_path / "workspace"
|
||||
(project_root / "Flows").mkdir(parents=True)
|
||||
(project_root / "Flows" / "Active.xaml").write_text(ACTIVE_XAML, encoding="utf-8")
|
||||
(project_root / "Scripts").mkdir()
|
||||
(project_root / "Scripts" / "Keep.bas").write_text("Sub Keep()\nEnd Sub", encoding="utf-8")
|
||||
(project_root / "main.xaml").write_text(MAIN_XAML, encoding="utf-8")
|
||||
|
||||
ProjectPipeline(project_root, output_root, "main.xaml", force=True).run(FlakyAnalyzer())
|
||||
|
||||
(project_root / "Scripts" / "Keep.bas").write_text("Sub Keep()\nMsgBox \"updated\"\nEnd Sub", encoding="utf-8")
|
||||
|
||||
retry = RecordingAnalyzer()
|
||||
ProjectPipeline(project_root, output_root, "main.xaml", force=False).run(retry)
|
||||
self.assertEqual(retry.paths, ["Flows/Active.xaml", "Scripts/Keep.bas"])
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
|
||||
Reference in New Issue
Block a user