mirror of
https://github.com/khodges42/nightShift.git
synced 2026-06-14 10:08:37 +00:00
fix apply patch when user has no git
This commit is contained in:
parent
ec9181eb64
commit
809ec92e0e
|
|
@ -177,6 +177,7 @@ pipeline:
|
|||
type: patch_apply
|
||||
mode: apply
|
||||
output: patch-apply-output.txt
|
||||
on_fail: implement
|
||||
|
||||
- id: test
|
||||
type: command
|
||||
|
|
|
|||
|
|
@ -161,6 +161,7 @@ pipeline:
|
|||
type: patch_apply
|
||||
mode: apply
|
||||
output: patch-apply-output.txt
|
||||
on_fail: implement
|
||||
|
||||
- id: test
|
||||
type: command
|
||||
|
|
|
|||
|
|
@ -167,6 +167,7 @@ In `nightshift.yaml`:
|
|||
type: patch_apply
|
||||
mode: dry_run
|
||||
output: patch-apply-output.txt
|
||||
on_fail: implement
|
||||
```
|
||||
|
||||
Run one task:
|
||||
|
|
@ -199,6 +200,7 @@ If the dry run looks good, switch to apply mode:
|
|||
type: patch_apply
|
||||
mode: apply
|
||||
output: patch-apply-output.txt
|
||||
on_fail: implement
|
||||
```
|
||||
|
||||
Run again:
|
||||
|
|
|
|||
|
|
@ -67,6 +67,7 @@ pipeline:
|
|||
type: patch_apply
|
||||
mode: apply
|
||||
output: patch-apply-output.txt
|
||||
on_fail: implement
|
||||
|
||||
- id: test
|
||||
type: command
|
||||
|
|
|
|||
|
|
@ -81,6 +81,8 @@ def validate_patch(
|
|||
|
||||
for path_text in files:
|
||||
_validate_patch_path(path_text, root, scoped_roots, forbidden_paths)
|
||||
_validate_hunk_lines(patch)
|
||||
_validate_file_states(patch, root)
|
||||
return PatchValidationResult(files=tuple(sorted(files)), changed_lines=changed_lines)
|
||||
|
||||
|
||||
|
|
@ -174,6 +176,57 @@ def _patch_files(patch: str) -> set[str]:
|
|||
return {path for path in files if path}
|
||||
|
||||
|
||||
def _validate_hunk_lines(patch: str) -> None:
|
||||
in_hunk = False
|
||||
for line_number, line in enumerate(patch.splitlines(), start=1):
|
||||
if line.startswith("diff --git "):
|
||||
in_hunk = False
|
||||
continue
|
||||
if line.startswith("@@"):
|
||||
in_hunk = True
|
||||
continue
|
||||
if not in_hunk:
|
||||
continue
|
||||
if line.startswith(("+", "-", " ", "\\")):
|
||||
continue
|
||||
raise PipelineError(
|
||||
"Patch validation failed: malformed hunk line "
|
||||
f"{line_number}; expected ' ', '+', '-', or '\\'."
|
||||
)
|
||||
|
||||
|
||||
def _validate_file_states(patch: str, root: Path) -> None:
|
||||
current_path: str | None = None
|
||||
current_is_new = False
|
||||
current_is_deleted = False
|
||||
|
||||
def flush() -> None:
|
||||
if not current_path:
|
||||
return
|
||||
target = root / current_path
|
||||
if current_is_new and target.exists():
|
||||
raise PipelineError(
|
||||
f"Patch validation failed: patch creates existing file `{current_path}`."
|
||||
)
|
||||
if current_is_deleted and not target.exists():
|
||||
raise PipelineError(
|
||||
f"Patch validation failed: patch deletes missing file `{current_path}`."
|
||||
)
|
||||
|
||||
for line in patch.splitlines():
|
||||
if line.startswith("diff --git "):
|
||||
flush()
|
||||
parts = line.split()
|
||||
current_path = _strip_prefix(parts[3]) if len(parts) >= 4 else None
|
||||
current_is_new = False
|
||||
current_is_deleted = False
|
||||
elif line.startswith("new file mode "):
|
||||
current_is_new = True
|
||||
elif line.startswith("deleted file mode "):
|
||||
current_is_deleted = True
|
||||
flush()
|
||||
|
||||
|
||||
def _changed_line_count(patch: str) -> int:
|
||||
count = 0
|
||||
for line in patch.splitlines():
|
||||
|
|
|
|||
|
|
@ -52,6 +52,50 @@ class PatchTests(unittest.TestCase):
|
|||
with self.assertRaisesRegex(PipelineError, "forbidden path"):
|
||||
validate_patch(patch, root, safety)
|
||||
|
||||
def test_validate_patch_rejects_malformed_hunk_line(self) -> None:
|
||||
with tempfile.TemporaryDirectory() as directory:
|
||||
root = Path(directory)
|
||||
(root / "src").mkdir()
|
||||
safety = SafetyConfig(
|
||||
require_clean_worktree=False,
|
||||
scoped_paths=("src",),
|
||||
allowed_commands=(),
|
||||
forbidden_commands=(),
|
||||
)
|
||||
patch = """diff --git a/src/app.py b/src/app.py
|
||||
--- a/src/app.py
|
||||
+++ b/src/app.py
|
||||
@@ -1 +1,2 @@
|
||||
-old
|
||||
+new
|
||||
bare line
|
||||
"""
|
||||
|
||||
with self.assertRaisesRegex(PipelineError, "malformed hunk line"):
|
||||
validate_patch(patch, root, safety)
|
||||
|
||||
def test_validate_patch_rejects_new_file_when_target_exists(self) -> None:
|
||||
with tempfile.TemporaryDirectory() as directory:
|
||||
root = Path(directory)
|
||||
(root / "src").mkdir()
|
||||
(root / "src" / "app.py").write_text("old\n", encoding="utf-8")
|
||||
safety = SafetyConfig(
|
||||
require_clean_worktree=False,
|
||||
scoped_paths=("src",),
|
||||
allowed_commands=(),
|
||||
forbidden_commands=(),
|
||||
)
|
||||
patch = """diff --git a/src/app.py b/src/app.py
|
||||
new file mode 100644
|
||||
--- /dev/null
|
||||
+++ b/src/app.py
|
||||
@@ -0,0 +1 @@
|
||||
+new
|
||||
"""
|
||||
|
||||
with self.assertRaisesRegex(PipelineError, "creates existing file"):
|
||||
validate_patch(patch, root, safety)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user