2018-09-14 13:33:23 +02:00
|
|
|
#!/usr/bin/env python
|
|
|
|
"""Make sure the data in BOTMETA.yml is valid"""
|
|
|
|
|
|
|
|
import glob
|
2018-09-14 20:12:06 +02:00
|
|
|
import os
|
|
|
|
import re
|
2018-09-14 13:33:23 +02:00
|
|
|
import sys
|
|
|
|
import yaml
|
|
|
|
|
|
|
|
from voluptuous import Any, MultipleInvalid, Required, Schema
|
|
|
|
from voluptuous.humanize import humanize_error
|
|
|
|
|
|
|
|
from ansible.module_utils.six import string_types
|
2018-09-14 20:12:06 +02:00
|
|
|
|
2018-09-14 13:33:23 +02:00
|
|
|
list_string_types = list(string_types)
|
|
|
|
|
|
|
|
|
|
|
|
def main():
|
|
|
|
"""Validate BOTMETA"""
|
|
|
|
path = '.github/BOTMETA.yml'
|
2018-09-14 20:12:06 +02:00
|
|
|
|
|
|
|
try:
|
|
|
|
with open(path, 'r') as f_path:
|
|
|
|
botmeta = yaml.safe_load(f_path)
|
|
|
|
except yaml.error.MarkedYAMLError as ex:
|
|
|
|
print('%s:%d:%d: YAML load failed: %s' % (path, ex.context_mark.line + 1, ex.context_mark.column + 1, re.sub(r'\s+', ' ', str(ex))))
|
|
|
|
sys.exit()
|
|
|
|
except Exception as ex:
|
|
|
|
print('%s:%d:%d: YAML load failed: %s' % (path, 0, 0, re.sub(r'\s+', ' ', str(ex))))
|
|
|
|
sys.exit()
|
2018-09-14 13:33:23 +02:00
|
|
|
|
|
|
|
files_schema = Any(
|
|
|
|
Schema(*string_types),
|
|
|
|
Schema({
|
|
|
|
'ignored': Any(list_string_types, *string_types),
|
|
|
|
'keywords': Any(list_string_types, *string_types),
|
|
|
|
'labels': Any(list_string_types, *string_types),
|
|
|
|
'maintainers': Any(list_string_types, *string_types),
|
|
|
|
'notified': Any(list_string_types, *string_types),
|
2018-12-21 01:36:43 +01:00
|
|
|
'supershipit': Any(list_string_types, *string_types),
|
2018-09-14 13:33:23 +02:00
|
|
|
'support': Any("core", "network", "community"),
|
|
|
|
})
|
|
|
|
)
|
|
|
|
|
|
|
|
list_dict_file_schema = [{str_type: files_schema}
|
|
|
|
for str_type in string_types]
|
|
|
|
|
|
|
|
schema = Schema({
|
|
|
|
Required('automerge'): bool,
|
|
|
|
Required('files'): Any(None, *list_dict_file_schema),
|
|
|
|
Required('macros'): dict, # Any(*list_macros_schema),
|
|
|
|
})
|
|
|
|
|
|
|
|
# Ensure schema is valid
|
|
|
|
|
|
|
|
try:
|
|
|
|
schema(botmeta)
|
|
|
|
except MultipleInvalid as ex:
|
|
|
|
for error in ex.errors:
|
|
|
|
# No way to get line numbers
|
2018-09-14 20:12:06 +02:00
|
|
|
print('%s:%d:%d: %s' % (path, 0, 0, humanize_error(botmeta, error)))
|
2018-09-14 13:33:23 +02:00
|
|
|
|
2018-09-25 19:37:01 +02:00
|
|
|
# Ensure botmeta is always support:core
|
|
|
|
botmeta_support = botmeta.get('files', {}).get('.github/BOTMETA.yml', {}).get('support', '')
|
|
|
|
if botmeta_support != 'core':
|
|
|
|
print('%s:%d:%d: .github/BOTMETA.yml MUST be support: core' % (path, 0, 0))
|
|
|
|
|
2018-11-21 12:15:56 +01:00
|
|
|
# Find all path (none-team) macros so we can substitute them
|
|
|
|
macros = botmeta.get('macros', {})
|
|
|
|
path_macros = []
|
|
|
|
for macro in macros:
|
|
|
|
if macro.startswith('team_'):
|
|
|
|
continue
|
|
|
|
path_macros.append(macro)
|
|
|
|
|
|
|
|
# Ensure all `files` correspond to a file
|
2018-09-14 13:33:23 +02:00
|
|
|
for file in botmeta['files']:
|
2018-11-21 12:15:56 +01:00
|
|
|
for macro in path_macros:
|
|
|
|
file = file.replace('$' + macro, botmeta.get('macros', {}).get(macro, ''))
|
2018-09-14 13:33:23 +02:00
|
|
|
if not os.path.exists(file):
|
|
|
|
# Not a file or directory, though maybe the prefix to one?
|
|
|
|
# https://github.com/ansible/ansibullbot/pull/1023
|
|
|
|
if not glob.glob('%s*' % file):
|
2018-09-14 20:12:06 +02:00
|
|
|
print("%s:%d:%d: Can't find '%s.*' in this branch" % (path, 0, 0, file))
|
2018-09-14 13:33:23 +02:00
|
|
|
|
|
|
|
|
|
|
|
if __name__ == '__main__':
|
|
|
|
main()
|
|
|
|
|
|
|
|
# Possible future work
|
|
|
|
# * Schema for `macros:` - currently ignored due to team_ansible
|
|
|
|
# * Ensure that all $teams mention in `files:` exist in `$macros`
|
2018-11-21 12:15:56 +01:00
|
|
|
# * Validate GitHub names - possibly expensive lookup needed - No should be validated when module is added - gundalow
|