1# Copyright 2016 The Android Open Source Project 2# 3# Licensed under the Apache License, Version 2.0 (the "License"); 4# you may not use this file except in compliance with the License. 5# You may obtain a copy of the License at 6# 7# http://www.apache.org/licenses/LICENSE-2.0 8# 9# Unless required by applicable law or agreed to in writing, software 10# distributed under the License is distributed on an "AS IS" BASIS, 11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12# See the License for the specific language governing permissions and 13# limitations under the License. 14 15"""Common errors thrown when repo preupload checks fail.""" 16 17import os 18import sys 19from typing import List, NamedTuple, Optional 20 21_path = os.path.realpath(__file__ + '/../..') 22if sys.path[0] != _path: 23 sys.path.insert(0, _path) 24del _path 25 26 27class HookResult(object): 28 """A single hook result.""" 29 30 def __init__(self, hook, project, commit, error, files=(), 31 fixup_cmd: Optional[List[str]] = None): 32 """Initialize. 33 34 Args: 35 hook: The name of the hook. 36 project: The name of the project. 37 commit: The git commit sha. 38 error: A string representation of the hook's result. Empty on 39 success. 40 files: The list of files that were involved in the hook execution. 41 fixup_cmd: A command that can automatically fix errors found in the 42 hook's execution. Can be None if the hook does not support 43 automatic fixups. 44 """ 45 self.hook = hook 46 self.project = project 47 self.commit = commit 48 self.error = error 49 self.files = files 50 self.fixup_cmd = fixup_cmd 51 52 def __bool__(self): 53 """Whether this result is an error.""" 54 return bool(self.error) 55 56 def is_warning(self): 57 """Whether this result is a non-fatal warning.""" 58 return False 59 60 61class HookCommandResult(HookResult): 62 """A single hook result based on a CompletedProcess.""" 63 64 def __init__(self, hook, project, commit, result, files=(), 65 fixup_cmd=None): 66 HookResult.__init__(self, hook, project, commit, 67 result.stderr if result.stderr else result.stdout, 68 files=files, fixup_cmd=fixup_cmd) 69 self.result = result 70 71 def __bool__(self): 72 """Whether this result is an error.""" 73 return self.result.returncode not in (None, 0, 77) 74 75 def is_warning(self): 76 """Whether this result is a non-fatal warning.""" 77 return self.result.returncode == 77 78 79 80class ProjectResults(NamedTuple): 81 """All results for a single project.""" 82 83 project: str 84 workdir: str 85 86 # All the results from running all the hooks. 87 results: List[HookResult] = [] 88 89 # Whether there were any non-hook related errors. For example, trying to 90 # parse the project configuration. 91 internal_failure: bool = False 92 93 def add_results(self, results: Optional[List[HookResult]]) -> None: 94 """Add |results| to our results.""" 95 if results: 96 self.results.extend(results) 97 98 @property 99 def fixups(self): 100 """Yield results that have a fixup available.""" 101 yield from (x for x in self.results if x and x.fixup_cmd) 102 103 def __bool__(self): 104 """Whether there are any errors in this set of results.""" 105 return self.internal_failure or any(self.results) 106