Skip to content

Commit d09e7a8

Browse files
committed
GitSCM: raise PatchConflict from get_diff_from_patches and handle in LandingWorker (bug 2000268) r=zeid
Includes the following changes: * GitSCM: add @detect_patch_conflict to transform SCMExceptions as needed * GitSCM: raise PatchConflict as needed in get_diff_from_patches * BaseWorker: allow handle_new_commit_failures to return something * landing_worker: handle PatchConflict from scm.get_diff_from_patches Pull request: #742
1 parent ee114fc commit d09e7a8

File tree

3 files changed

+41
-13
lines changed

3 files changed

+41
-13
lines changed

src/lando/api/legacy/workers/base.py

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
import subprocess
77
from abc import ABC, abstractmethod
88
from time import sleep
9-
from typing import Callable
9+
from typing import Callable, TypeVar
1010

1111
from celery import Task
1212
from django.db import transaction
@@ -36,6 +36,8 @@
3636

3737
logger = logging.getLogger(__name__)
3838

39+
T = TypeVar("T")
40+
3941

4042
class Worker(ABC):
4143
"""A base class for repository workers."""
@@ -314,15 +316,15 @@ def update_repo(
314316

315317
def handle_new_commit_failures(
316318
self,
317-
create_revision_callable: Callable[[Revision], None],
319+
create_revision_callable: Callable[[Revision], T],
318320
repo: Repo,
319321
job: BaseJob,
320322
scm: AbstractSCM,
321323
revision: Revision,
322-
) -> None:
324+
) -> T:
323325
"""Create revisions with job status handling."""
324326
try:
325-
create_revision_callable(revision)
327+
return create_revision_callable(revision)
326328
except NoDiffStartLine as exc:
327329
message = (
328330
"Lando encountered a malformed patch, please try again. "

src/lando/api/legacy/workers/landing_worker.py

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -178,6 +178,10 @@ def convert_patches_to_diff(self, scm: AbstractSCM, job: LandingJob):
178178
# at this time. In theory this would work for any provided patches in a
179179
# standard format.
180180

181+
def get_diff_from_patches(revision: Revision) -> str:
182+
logger.debug(f"Converting paches to single diff for {revision} ...")
183+
return scm.get_diff_from_patches(revision.patches)
184+
181185
# NOTE: this is only supported for jobs with a single revision at this time.
182186
# See bug 2001185.
183187

@@ -192,7 +196,14 @@ def convert_patches_to_diff(self, scm: AbstractSCM, job: LandingJob):
192196
if not revision.patches:
193197
raise ValueError("Revision is missing patches.")
194198

195-
diff = scm.get_diff_from_patches(revision.patches)
199+
diff = self.handle_new_commit_failures(
200+
get_diff_from_patches,
201+
job.target_repo,
202+
job,
203+
scm,
204+
revision,
205+
)
206+
196207
revision.set_patch(diff)
197208
revision.save()
198209

src/lando/main/scm/git.py

Lines changed: 23 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
from contextlib import contextmanager
99
from datetime import datetime
1010
from pathlib import Path
11-
from typing import Any
11+
from typing import Any, Callable, TypeVar
1212

1313
from typing_extensions import override
1414

@@ -36,6 +36,24 @@
3636
ENV_COMMITTER_EMAIL = "GIT_COMMITTER_EMAIL"
3737

3838

39+
T = TypeVar("T")
40+
41+
42+
def detect_patch_conflict(fn: Callable[..., T]) -> Callable[..., T]:
43+
"""Decorator transforming SCMExceptions to PatchConflict as appropriate."""
44+
45+
def wrapper(*args, **kwargs) -> T:
46+
try:
47+
return fn(*args, **kwargs)
48+
except SCMException as exc:
49+
if "error: patch" in exc.err:
50+
raise PatchConflict(exc.err) from exc
51+
52+
raise exc
53+
54+
return wrapper
55+
56+
3957
class GitSCM(AbstractSCM):
4058
"""An implementation of the AbstractVCS for Git, for use by the Repo and LandingWorkers."""
4159

@@ -122,6 +140,7 @@ def last_commit_for_path(self, path: str) -> str:
122140
return self._git_run(*command, cwd=self.path)
123141

124142
@override
143+
@detect_patch_conflict
125144
def apply_patch(
126145
self, diff: str, commit_description: str, commit_author: str, commit_date: str
127146
):
@@ -152,13 +171,7 @@ def apply_patch(
152171
]
153172

154173
for c in cmds:
155-
try:
156-
self._git_run(*c, cwd=self.path)
157-
except SCMException as exc:
158-
if "error: patch" in exc.err:
159-
raise PatchConflict(exc.err) from exc
160-
161-
raise exc
174+
self._git_run(*c, cwd=self.path)
162175

163176
@override
164177
def apply_patch_git(self, patch_bytes: bytes):
@@ -189,6 +202,7 @@ def apply_patch_git(self, patch_bytes: bytes):
189202
# Re-raise the exception from the failed `git am`.
190203
raise exc
191204

205+
@detect_patch_conflict
192206
def get_diff_from_patches(self, patches: str) -> str:
193207
"""Apply multiple patches and return the diff output."""
194208
# TODO: add error handling so that if something goes wrong here,
@@ -202,6 +216,7 @@ def get_diff_from_patches(self, patches: str) -> str:
202216
patch_file.flush()
203217

204218
self._git_run("apply", "--reject", patch_file.name, cwd=self.path)
219+
205220
self._git_run("add", "-A", "-f", cwd=self.path)
206221
return self._git_run(
207222
"diff", "--staged", "--binary", cwd=self.path, rstrip=False

0 commit comments

Comments
 (0)