OLD | NEW |
| (Empty) |
1 #!/usr/bin/env python | |
2 # Copyright 2015 The Chromium Authors. All rights reserved. | |
3 # Use of this source code is governed by a BSD-style license that can be | |
4 # found in the LICENSE file. | |
5 | |
6 """CQ config validation library.""" | |
7 | |
8 import argparse | |
9 # This file was originally copied together with the cq_client library from the | |
10 # internal commit_queue repository and then modified to import protobuf26 | |
11 # instead of google.protobuf to prevent conflicts with a different version of | |
12 # google.protobuf that some users of depot_tools have installed. If you need to | |
13 # update this file, please make similar changes again and add this comment back. | |
14 # More details on why we chose to rename the package can be found in the file | |
15 # depot_tools/third_party/protobuf26/README.chromium. | |
16 import protobuf26 as protobuf | |
17 import logging | |
18 import re | |
19 import sys | |
20 | |
21 from cq_client import cq_pb2 | |
22 | |
23 | |
24 REQUIRED_FIELDS = [ | |
25 'version', | |
26 'verifiers', | |
27 'cq_name', | |
28 ] | |
29 | |
30 LEGACY_FIELDS = [ | |
31 'svn_repo_url', | |
32 ] | |
33 | |
34 EMAIL_REGEXP = '^[^@]+@[^@]+\.[^@]+$' | |
35 | |
36 | |
37 def _HasField(message, field_path): | |
38 """Checks that at least one field with given path exist in the proto message. | |
39 | |
40 This function correctly handles repeated fields and will make sure that each | |
41 repeated field will have required sub-path, e.g. if 'abc' is a repeated field | |
42 and field_path is 'abc.def', then the function will only return True when each | |
43 entry for 'abc' will contain at least one value for 'def'. | |
44 | |
45 Args: | |
46 message (google.protobuf.message.Message): Protocol Buffer message to check. | |
47 field_path (string): Path to the target field separated with ".". | |
48 | |
49 Return: | |
50 True if at least one such field is explicitly set in the message. | |
51 """ | |
52 path_parts = field_path.split('.', 1) | |
53 field_name = path_parts[0] | |
54 sub_path = path_parts[1] if len(path_parts) == 2 else None | |
55 | |
56 field_labels = {fd.name: fd.label for fd in message.DESCRIPTOR.fields} | |
57 repeated_field = (field_labels[field_name] == | |
58 protobuf.descriptor.FieldDescriptor.LABEL_REPEATED) | |
59 | |
60 if sub_path: | |
61 field = getattr(message, field_name) | |
62 if repeated_field: | |
63 if not field: | |
64 return False | |
65 return all(_HasField(entry, sub_path) for entry in field) | |
66 else: | |
67 return _HasField(field, sub_path) | |
68 else: | |
69 if repeated_field: | |
70 return len(getattr(message, field_name)) > 0 | |
71 else: | |
72 return message.HasField(field_name) | |
73 | |
74 | |
75 def IsValid(cq_config): | |
76 """Validates a CQ config and prints errors/warnings to the screen. | |
77 | |
78 Args: | |
79 cq_config (string): Unparsed text format of the CQ config proto. | |
80 | |
81 Returns: | |
82 True if the config is valid. | |
83 """ | |
84 try: | |
85 config = cq_pb2.Config() | |
86 protobuf.text_format.Merge(cq_config, config) | |
87 except protobuf.text_format.ParseError as e: | |
88 logging.error('Failed to parse config as protobuf:\n%s', e) | |
89 return False | |
90 | |
91 if _HasField(config, 'gerrit'): | |
92 if _HasField(config, 'rietveld'): | |
93 logging.error('gerrit and rietveld are not supported at the same time.') | |
94 return False | |
95 # TODO(tandrii): validate gerrit. | |
96 required_fields = REQUIRED_FIELDS + ['gerrit.cq_verified_label'] | |
97 if _HasField(config, 'verifiers.reviewer_lgtm'): | |
98 logging.error('reviewer_lgtm verifier is not supported with Gerrit.') | |
99 return False | |
100 elif _HasField(config, 'rietveld'): | |
101 required_fields = REQUIRED_FIELDS + ['rietveld.url'] | |
102 else: | |
103 logging.error('either rietveld gerrit are required fields.') | |
104 return False | |
105 | |
106 for fname in required_fields: | |
107 if not _HasField(config, fname): | |
108 logging.error('%s is a required field', fname) | |
109 return False | |
110 | |
111 for fname in LEGACY_FIELDS: | |
112 if _HasField(config, fname): | |
113 logging.warn('%s is a legacy field', fname) | |
114 | |
115 | |
116 for base in config.rietveld.project_bases: | |
117 try: | |
118 re.compile(base) | |
119 except re.error: | |
120 logging.error('failed to parse "%s" in project_bases as a regexp', base) | |
121 return False | |
122 | |
123 # TODO(sergiyb): For each field, check valid values depending on its | |
124 # semantics, e.g. email addresses, regular expressions etc. | |
125 return True | |
OLD | NEW |