Index: third_party/cq_client/validate_config.py |
diff --git a/third_party/cq_client/validate_config.py b/third_party/cq_client/validate_config.py |
new file mode 100644 |
index 0000000000000000000000000000000000000000..1e34b868e1172e0222856d9a8a79a09397c1460a |
--- /dev/null |
+++ b/third_party/cq_client/validate_config.py |
@@ -0,0 +1,112 @@ |
+#!/usr/bin/env python |
+# Copyright 2015 The Chromium Authors. All rights reserved. |
+# Use of this source code is governed by a BSD-style license that can be |
+# found in the LICENSE file. |
+ |
+"""CQ config validation library.""" |
+ |
+import argparse |
+# The 'from google import protobuf' below was replaced to fix an issue where |
+# some users may have built-in google package installed on their system, which |
+# is incompatible with cq_pb2 below. This hack can be removed after |
+# http://crbug.com/503067 is resolved. |
+import protobuf26 as protobuf |
+import logging |
+import re |
+import sys |
+ |
+from cq_client import cq_pb2 |
+ |
+ |
+REQUIRED_FIELDS = [ |
+ 'version', |
+ 'rietveld', |
+ 'rietveld.url', |
+ 'verifiers', |
+ 'cq_name', |
+] |
+ |
+LEGACY_FIELDS = [ |
+ 'svn_repo_url', |
+ 'server_hooks_missing', |
+ 'verifiers_with_patch', |
+] |
+ |
+EMAIL_REGEXP = '^[^@]+@[^@]+\.[^@]+$' |
+ |
+ |
+def _HasField(message, field_path): |
+ """Checks that at least one field with given path exist in the proto message. |
+ |
+ This function correctly handles repeated fields and will make sure that each |
+ repeated field will have required sub-path, e.g. if 'abc' is a repeated field |
+ and field_path is 'abc.def', then the function will only return True when each |
+ entry for 'abc' will contain at least one value for 'def'. |
+ |
+ Args: |
+ message (google.protobuf.message.Message): Protocol Buffer message to check. |
+ field_path (string): Path to the target field separated with ".". |
+ |
+ Return: |
+ True if at least one such field is explicitly set in the message. |
+ """ |
+ path_parts = field_path.split('.', 1) |
+ field_name = path_parts[0] |
+ sub_path = path_parts[1] if len(path_parts) == 2 else None |
+ |
+ field_labels = {fd.name: fd.label for fd in message.DESCRIPTOR.fields} |
+ repeated_field = (field_labels[field_name] == |
+ protobuf.descriptor.FieldDescriptor.LABEL_REPEATED) |
+ |
+ if sub_path: |
+ field = getattr(message, field_name) |
+ if repeated_field: |
+ if not field: |
+ return False |
+ return all(_HasField(entry, sub_path) for entry in field) |
+ else: |
+ return _HasField(field, sub_path) |
+ else: |
+ if repeated_field: |
+ return len(getattr(message, field_name)) > 0 |
+ else: |
+ return message.HasField(field_name) |
+ |
+ |
+def IsValid(cq_config): |
+ """Validates a CQ config and prints errors/warnings to the screen. |
+ |
+ Args: |
+ cq_config (string): Unparsed text format of the CQ config proto. |
+ |
+ Returns: |
+ True if the config is valid. |
+ """ |
+ try: |
+ config = cq_pb2.Config() |
+ protobuf.text_format.Merge(cq_config, config) |
+ except protobuf.text_format.ParseError as e: |
+ logging.error('Failed to parse config as protobuf:\n%s', e) |
+ return False |
+ |
+ for fname in REQUIRED_FIELDS: |
+ if not _HasField(config, fname): |
+ logging.error('%s is a required field', fname) |
+ return False |
+ |
+ for fname in LEGACY_FIELDS: |
+ if _HasField(config, fname): |
+ logging.warn('%s is a legacy field', fname) |
+ |
+ |
+ for base in config.rietveld.project_bases: |
+ try: |
+ re.compile(base) |
+ except re.error: |
+ logging.error('failed to parse "%s" in project_bases as a regexp', base) |
+ return False |
+ |
+ # TODO(sergiyb): For each field, check valid values depending on its |
+ # semantics, e.g. email addresses, regular expressions etc. |
+ |
+ return True |