• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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