diff --git a/tasktypes/vendoring.py b/tasktypes/vendoring.py index be929c7..d3a3c29 100644 --- a/tasktypes/vendoring.py +++ b/tasktypes/vendoring.py @@ -25,6 +25,13 @@ def _patch_exception_message(e): return str(e) +def _is_patch_conflict(e): + # `./mach vendor --patch-mode only` applies the patches and then runs the + # post-patch actions. Only a patch that didn't apply is something the AI can + # fix; mach logs this message for that case and not for a failed action. + return "Could not apply" in _patch_exception_message(e) + + class VendorTaskRunner(BaseTaskRunner): def __init__(self, provider_dictionary, config_dictionary): self.jobType = JOBTYPE.VENDORING @@ -193,6 +200,13 @@ def _process_new_job(self, library, task, new_version, timestamp, most_recent_jo except Exception as e: msg = _patch_exception_message(e) + if not _is_patch_conflict(e): + # Something other than a patch conflict failed (e.g. a + # post-patch action), which the AI can't resolve. + self.dbProvider.update_job_status(created_job, JOBSTATUS.DONE, JOBOUTCOME.COULD_NOT_PATCH) + self.bugzillaProvider.comment_on_bug(created_job.bugzilla_id, CommentTemplates.COULD_NOT_GENERAL_ERROR("apply the mozilla patches (this wasn't a patch conflict, so Updatebot didn't ask its AI assistant to resolve it)", errormessage=msg), needinfo=library.maintainer_bz) + return + # The mozilla patches didn't apply cleanly. Ask the AI to try to # resolve the conflicts (updating the .patch files / moz.yaml). potential_commit_message = "Bug %s - Update %s local patches to apply cleanly" % ( diff --git a/tests/functionality_all_platforms.py b/tests/functionality_all_platforms.py index 9f45184..e39948f 100755 --- a/tests/functionality_all_platforms.py +++ b/tests/functionality_all_platforms.py @@ -368,7 +368,7 @@ def testFailsDuringPatching(self): lambda: 50, # get_filed_bug_id_func, lambda b: {}, # filed_bug_ids_func AssertFalse, # treeherder_response - command_callbacks={'patch': lambda: raise_(Exception("No patching!"))} + command_callbacks={'patch': lambda: raise_(Exception("Could not apply mozilla.patch, possible reasons:"))} ) try: u.run(library_filter=library_filter) @@ -380,10 +380,35 @@ def testFailsDuringPatching(self): self.assertEqual(JOBSTATUS.DONE, j.status, "Expected status JOBSTATUS.DONE, got status %s" % (j.status.name)) self.assertEqual(JOBOUTCOME.COULD_NOT_PATCH, j.outcome, "Expected outcome JOBOUTCOME.COULD_NOT_PATCH, got outcome %s" % (j.outcome.name)) self.assertEqual(expected_values.get_filed_bug_id_func(), j.bugzilla_id) + self.assertEqual(u.aiProvider.resolve_call_count, 1, "A patch conflict should have been given to the AI") finally: self._cleanup(u, expected_values) + # Create -> patching fails, but not because a patch didn't apply (e.g. a + # post-patch action failed) -> the AI isn't asked -> COULD_NOT_PATCH + @logEntryExitHeaderLine + def testPatchFailureNotAConflictSkipsAI(self): + library_filter = 'png' + (u, expected_values, _check_jobs) = self._setup( + library_filter, + lambda b: ["try_rev|2021-02-09 15:30:04 -0500|2021-02-12 17:40:01 +0000"], + lambda: 56, # get_filed_bug_id_func + lambda b: {}, # filed_bug_ids_func + AssertFalse, # treeherder_response + command_callbacks={'patch': lambda: raise_(Exception("Importing local patches...\nERROR: Could not find revision field in moz.yaml."))}, + ai_config={'ai_outcome': 'trivial success', 'ai_details': ['Should not be asked.']}, + ) + try: + u.run(library_filter=library_filter) + self.assertEqual(u.aiProvider.resolve_call_count, 0, "The AI should not be asked about a failure that isn't a patch conflict") + lib = [lib for lib in u.libraryProvider.get_libraries(u.config_dictionary['General']['gecko-path']) if library_filter in lib.name][0] + j = u.dbProvider.get_job(lib, expected_values.library_new_version_id()) + self.assertEqual(JOBSTATUS.DONE, j.status) + self.assertEqual(JOBOUTCOME.COULD_NOT_PATCH, j.outcome) + finally: + self._cleanup(u, expected_values) + # Create -> patch fails -> AI resolves the conflict -> re-apply succeeds -> proceeds @logEntryExitHeaderLine def testPatchConflictResolvedByAI(self): @@ -392,7 +417,7 @@ def testPatchConflictResolvedByAI(self): def patch_callback(): patch_calls[0] += 1 if patch_calls[0] == 1: - raise_(Exception("patch does not apply cleanly")) + raise_(Exception("Could not apply mozilla.patch, possible reasons:")) return "" arc_calls = [0] @@ -438,7 +463,7 @@ def testPatchConflictResolvedByAIUncertain(self): def patch_callback(): patch_calls[0] += 1 if patch_calls[0] == 1: - raise_(Exception("patch does not apply cleanly")) + raise_(Exception("Could not apply mozilla.patch, possible reasons:")) return "" arc_calls = [0] @@ -480,7 +505,7 @@ def testPatchConflictAIResolvedButReapplyStillFails(self): lambda: 54, # get_filed_bug_id_func lambda b: {}, # filed_bug_ids_func AssertFalse, # treeherder_response - command_callbacks={'patch': lambda: raise_(Exception("patch still does not apply"))}, + command_callbacks={'patch': lambda: raise_(Exception("Could not apply mozilla.patch, possible reasons:"))}, ai_config={'ai_outcome': 'trivial success', 'ai_details': ['I think I fixed it.']}, ) try: