1#!/usr/bin/env python 2# 3# Copyright 2021 Espressif Systems (Shanghai) CO LTD 4# 5# Licensed under the Apache License, Version 2.0 (the "License"); 6# you may not use this file except in compliance with the License. 7# You may obtain a copy of the License at 8# 9# http://www.apache.org/licenses/LICENSE-2.0 10# 11# Unless required by applicable law or agreed to in writing, software 12# distributed under the License is distributed on an "AS IS" BASIS, 13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14# See the License for the specific language governing permissions and 15# limitations under the License. 16 17""" 18Check if all rules in rules.yml used or not in CI yaml files. 19""" 20 21import argparse 22import os 23import sys 24from copy import deepcopy 25 26import yaml 27from idf_ci_utils import IDF_PATH 28 29ROOT_YML_FP = os.path.join(IDF_PATH, '.gitlab-ci.yml') 30 31 32def load_yaml(file_path): 33 return yaml.load(open(file_path), Loader=yaml.FullLoader) 34 35 36class YMLConfig: 37 def __init__(self, root_yml_file_path): 38 self._config = None 39 self._all_extends = None 40 41 self.root_yml = load_yaml(root_yml_file_path) 42 assert self.root_yml 43 44 @staticmethod 45 def _list(str_or_list): 46 if isinstance(str_or_list, str): 47 return [str_or_list] 48 if isinstance(str_or_list, list): 49 return str_or_list 50 raise ValueError('Wrong type: {}. Only supports str or list.'.format(type(str_or_list))) 51 52 @property 53 def config(self): 54 if self._config: 55 return self._config 56 57 all_config = dict() 58 for item in self.root_yml['include']: 59 if not item.endswith('rules.yml'): 60 all_config.update(load_yaml(os.path.join(IDF_PATH, item))) 61 self._config = all_config 62 return self._config 63 64 @property 65 def all_extends(self): 66 if self._all_extends: 67 return self._all_extends 68 69 res = set([]) 70 for v in self.config.values(): 71 if 'extends' in v: 72 for item in self._list(v['extends']): 73 if item.startswith('.rules:'): 74 res.add(item) 75 self._all_extends = res 76 return self._all_extends 77 78 def exists(self, key): 79 if key in self.all_extends: 80 return True 81 return False 82 83 84def validate(rules_yml): 85 yml_config = YMLConfig(ROOT_YML_FP) 86 res = 0 87 needed_rules = deepcopy(yml_config.all_extends) 88 with open(rules_yml) as fr: 89 for index, line in enumerate(fr): 90 if line.startswith('.rules:'): 91 key = line.strip().rsplit(':', 1)[0] 92 if not yml_config.exists(key): 93 print('{}:{}:WARNING:rule "{}" unused'.format(rules_yml, index, key)) 94 else: 95 needed_rules.remove(key) 96 97 if needed_rules: 98 for item in needed_rules: 99 print('ERROR: missing rule: "{}"'.format(item)) 100 res = 1 101 102 if res == 0: 103 print('Pass') 104 return res 105 106 107if __name__ == '__main__': 108 parser = argparse.ArgumentParser(description=__doc__) 109 parser.add_argument('rules_yml', nargs='?', default=os.path.join(IDF_PATH, '.gitlab', 'ci', 'rules.yml'), 110 help='rules.yml file path') 111 args = parser.parse_args() 112 113 sys.exit(validate(args.rules_yml)) 114