| 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 |