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__( 31 self, 32 hook, 33 project, 34 commit, 35 error, 36 warning: bool = False, 37 files=(), 38 fixup_cmd: Optional[List[str]] = None, 39 ): 40 """Initialize. 41 42 Args: 43 hook: The name of the hook. 44 project: The name of the project. 45 commit: The git commit sha. 46 error: A string representation of the hook's result. Empty on 47 success. 48 warning: Whether this result is a warning, not an error. 49 files: The list of files that were involved in the hook execution. 50 fixup_cmd: A command that can automatically fix errors found in the 51 hook's execution. Can be None if the hook does not support 52 automatic fixups. 53 """ 54 self.hook = hook 55 self.project = project 56 self.commit = commit 57 self.error = error 58 self._warning = warning 59 self.files = files 60 self.fixup_cmd = fixup_cmd 61 62 def __bool__(self): 63 """Whether this result is an error.""" 64 return bool(self.error) and not self._warning 65 66 def is_warning(self): 67 """Whether this result is a non-fatal warning.""" 68 return self._warning 69 70 71class HookCommandResult(HookResult): 72 """A single hook result based on a CompletedProcess.""" 73 74 def __init__(self, hook, project, commit, result, files=(), 75 fixup_cmd=None): 76 HookResult.__init__(self, hook, project, commit, 77 result.stderr if result.stderr else result.stdout, 78 files=files, fixup_cmd=fixup_cmd) 79 self.result = result 80 81 def __bool__(self): 82 """Whether this result is an error.""" 83 return self.result.returncode not in (None, 0, 77) 84 85 def is_warning(self): 86 """Whether this result is a non-fatal warning.""" 87 return self.result.returncode == 77 88 89 90class ProjectResults(NamedTuple): 91 """All results for a single project.""" 92 93 project: str 94 workdir: str 95 96 # All the results from running all the hooks. 97 results: List[HookResult] = [] 98 99 # Whether there were any non-hook related errors. For example, trying to 100 # parse the project configuration. 101 internal_failure: bool = False 102 103 def add_results(self, results: Optional[List[HookResult]]) -> None: 104 """Add |results| to our results.""" 105 if results: 106 self.results.extend(results) 107 108 @property 109 def fixups(self): 110 """Yield results that have a fixup available.""" 111 yield from (x for x in self.results if x and x.fixup_cmd) 112 113 def __bool__(self): 114 """Whether there are any errors in this set of results.""" 115 return self.internal_failure or any(self.results) 116