#! /usr/bin/env python # Copyright 2014 SUSE Linux Products GmbH # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. import io import glob import sys import voluptuous as v # The files uses YAML extensions like !include, therefore use the # jenkins-job-builder yaml parser for loading. from jenkins_jobs import local_yaml BUILDER = v.Schema({ v.Required('name'): v.All(str), v.Required('builders'): v.All(list), 'description': v.All(str) }, extra=True) JOB = v.Schema({ v.Required('builders'): v.All(list), v.Required('name'): v.All(str), v.Required('node'): v.All(str), v.Required('publishers'): v.All(list), 'description': v.All(str), 'parameters': v.All(list), 'wrappers': v.All(list) }) JOB_GROUP = v.Schema({ v.Required('name'): v.All(str), v.Required('jobs'): v.All(list), 'description': v.All(str) }, extra=True) JOB_TEMPLATE = v.Schema({ v.Required('builders'): v.All(list), v.Required('name'): v.All(str), v.Required('node'): v.All(str), v.Required('publishers'): v.All(list), 'description': v.All(str), 'wrappers': v.All(list) }) PROJECT = v.Schema({ v.Required('name'): v.All(str), v.Required('jobs'): v.All(list), 'description': v.All(str) }, extra=True) PUBLISHER = v.Schema({ v.Required('name'): v.All(str), v.Required('publishers'): v.All(list), 'description': v.All(str) }) def normalize(s): "Normalize string for comparison." return s.lower().replace("_", "-") def check_alphabetical(): """Check that the projects are in alphabetical order and that indenting looks correct""" print("Checking jenkins/jobs/projects.yaml") print("===================================") # Note that the file has different sections and we need to check # entries within these sections only errors = False last = "" count = 1 for line in open('jenkins/jobs/projects.yaml', 'r'): if line.startswith(' name: '): i = line.find(' name: ') current = line[i + 7:].strip() if normalize(last) > normalize(current): print(" Wrong alphabetical order: %(last)s, %(current)s" % {"last": last, "current": current}) errors = True last = current if (len(line) - len(line.lstrip(' '))) % 2 != 0: print("Line %(count)s not indented by multiple of 2:\n\t%(line)s" % {"count": count, "line": line}) errors = True count = count+1 if errors: print("Found errors in jenkins/jobs/projects.yaml!\n") else: print("No errors found in jenkins/jobs/projects.yaml!\n") return errors def validate_jobs(): """Minimal YAML file validation.""" count = 0 errors = False print("Validating YAML files") print("=====================") for job_file in glob.glob('jenkins/jobs/*.yaml'): jobs = local_yaml.load(io.open(job_file, 'r', encoding='utf-8')) for item in jobs: if 'builder' in item: schema = BUILDER entry = item['builder'] elif 'job' in item: schema = JOB entry = item['job'] elif 'job-group' in item: schema = JOB_GROUP entry = item['job-group'] elif 'job-template' in item: schema = JOB_TEMPLATE entry = item['job-template'] elif 'project' in item: schema = PROJECT entry = item['project'] elif 'publisher' in item: schema = PUBLISHER entry = item['publisher'] elif 'wrapper' in item: continue elif 'defaults' in item: continue else: print("Unknown entry in file %s" % job_file) print(item) try: schema(entry) except Exception as e: print("Failure: %s" % e) print("Failure in file %s" % job_file) print("Failure parsing item:") print(item) count += 1 errors = True # NOTE(pabelanger): Make sure console-log is our last publisher # defined. We use the publisher to upload logs from zuul-launcher. result = _check_console_log_publisher(schema, entry) result += _check_tox_builder(schema, entry) if result: print(job_file) count += result errors = True print("%d errors found validating YAML files in jenkins/jobs/*.yaml.\n" % count) return errors def _check_console_log_publisher(schema, entry): count = 0 if schema == JOB or schema == JOB_TEMPLATE: if 'publishers' in entry: if 'console-log' in entry['publishers'] and \ entry['publishers'][-1] != 'console-log': print("ERROR: The console-log publisher MUST be the last " "publisher in '%s':" % entry['name']) count += 1 return count def _check_tox_builder(schema, entry): count = 0 if schema == JOB or schema == JOB_TEMPLATE: if 'builders' in entry: for b in entry['builders']: # Test for dict, coming from "tox:" if isinstance(b, dict): if 'tox' in b: print("ERROR: Use 'run-tox' instead of 'tox' " "builder in '%s':" % entry['name']) count += 1 # And test for "tox" without arguments elif isinstance(b, str) and b == 'tox': print("ERROR: Use 'run-tox' instead of 'tox' " "builder in '%s':" % entry['name']) count += 1 return count def check_all(): errors = validate_jobs() errors = check_alphabetical() or errors if errors: print("Found errors in jenkins/jobs/*.yaml!") else: print("No errors found in jenkins/jobs/*.yaml!") return errors if __name__ == "__main__": sys.exit(check_all())