diff --git a/build/main.sh b/build/main.sh index f95a2278..3d5ce9fc 100755 --- a/build/main.sh +++ b/build/main.sh @@ -30,7 +30,7 @@ else fi echo "running format validation..." -./validate_format.rb $FORMAT_FILE +./validate_format.py $FORMAT_FILE if [[ $? != 0 ]]; then echo "format validation failed!" exit 1 diff --git a/build/validate_format.py b/build/validate_format.py new file mode 100755 index 00000000..b7866858 --- /dev/null +++ b/build/validate_format.py @@ -0,0 +1,111 @@ +#!/usr/bin/env python3 + +import json +import string +import sys + +anchor = '###' +auth_keys = ['apiKey', 'OAuth', 'X-Mashape-Key', 'No'] +punctuation = ['.', '?', '!'] +https_keys = ['Yes', 'No'] + +index_title = 0 +index_desc = 1 +index_auth = 2 +index_https = 3 +index_link = 4 + +errors = [] + + +def add_error(line_num, message): + """adds an error to the dynamic error list""" + err = '(L{:03d}) {}'.format(line_num+1, message) + errors.append(err) + + +def check_format(filename): + """ + validates that each line is formatted correctly, + appending to error list as needed + """ + with open(filename) as fp: + lines = list(line.rstrip() for line in fp) + + # START Alphabetical Order + category = "" + sections = {} + section_line_num = {} + for line_num, line in enumerate(lines): + if line.startswith(anchor): + category = line.split(anchor)[1].strip() + sections[category] = [] + section_line_num[category] = line_num + continue + if not line.startswith('|') or line.startswith('|---'): + continue + title = [x.strip() for x in line.split('|')[1:-1]][0].upper() + sections[category].append(title) + + for category, entries in sections.items(): + if sorted(entries) != entries: + add_error(section_line_num[category], '{} section is not in alphabetical order'.format(category)) + # END Alphabetical Order + + # START Check Entries + for line_num, line in enumerate(lines): + if not line.startswith('|') or line.startswith('|---'): + continue + segments = line.split('|')[1:-1] + # START Global + for segment in segments: + # every line segment should start and end with exactly 1 space + if len(segment) - len(segment.lstrip()) != 1 or len(segment) - len(segment.rstrip()) != 1: + add_error(line_num, "each segment must start and end with exactly 1 space") + # END Global + segments = [seg.strip() for seg in segments] + # START Description + # first character should be capitalized + char = segments[index_desc][0] + if char.upper() != char: + add_error(line_num, "first char of Description is not capitalized") + # last character should not punctuation + char = segments[index_desc][-1] + if char in punctuation: + add_error(line_num, "description should not end with {}".format(char)) + # END Description + # START Auth + # values should conform to valid options only + auth = segments[index_auth].replace('`', '') + if auth not in auth_keys: + add_error(line_num, "{} is not a valid Auth option".format(auth)) + # END Auth + # START HTTPS + # values should conform to valid options only + https = segments[index_https] + if https not in https_keys: + add_error(line_num, "{} is not a valid HTTPS option".format(https)) + # END HTTPS + # START Link + # url should be wrapped in '[Go!]()' Markdown syntax + link = segments[index_link] + if not link.startswith('[Go!](') or not link.endswith(')'): + add_error(line_num, 'link format should be "[Go!](LINK)"') + # END Link + # END Check Entries + +def main(): + num_args = len(sys.argv) + if num_args < 2: + print("No .md file passed") + sys.exit(1) + + check_format(sys.argv[1]) + if len(errors) > 0: + for err in errors: + print(err) + sys.exit(1) + + +if __name__ == "__main__": + main() diff --git a/build/validate_format.rb b/build/validate_format.rb deleted file mode 100755 index d746224a..00000000 --- a/build/validate_format.rb +++ /dev/null @@ -1,111 +0,0 @@ -#!/usr/bin/env ruby - -auth_keys = ['apiKey', 'OAuth', 'X-Mashape-Key', 'No'] -punctuation = ['.', '?', '!'] -https_keys = ['Yes', 'No'] - -INDEX_TITLE = 1 -INDEX_DESCRIPTION = 2 -INDEX_AUTH = 3 -INDEX_HTTPS = 4 -INDEX_LINK = 5 -filename = ARGV[0] -$errors = [] - -def add_error(line_num, val_index, message) - case val_index - when INDEX_TITLE - segment = "Title" - when INDEX_DESCRIPTION - segment = "Description" - when INDEX_AUTH - segment = "Auth" - when INDEX_HTTPS - segment = "HTTPS" - when INDEX_LINK - segment = "Link" - end - $errors.push("(L%03d) %-14.14s #{message}" % [line_num, segment]) -end - -################### CHECK ALPHABETICAL ORDER ################### -section = '' -sections = [] -section_to_line_num = {} -section_to_entries = Hash.new {|h,k| h[k] = Array.new } -File.foreach(filename).with_index do | line, line_num | - if line.start_with?('###') - section = line.sub('###', '').lstrip.chop - sections.push(section) - section_to_line_num[section] = line_num + 1 - end - # Skip non-markdown table lines and table schema lines - if !line.start_with?('|') || line.eql?("|---|---|---|---|---|\n") - next - end - # char to check is the first column - check_char = line.split("|")[1].strip.upcase - section_to_entries[section].push(check_char) -end -sections.each do | sect | - if section_to_entries[sect] != section_to_entries[sect].sort - add_error(section_to_line_num[sect], INDEX_TITLE, "#{sect} section is not in alphabetical order") - end -end - -#################### CHECK LINE ENTRIES ######################## -File.foreach(filename).with_index do | line, line_num | - line_num += 1 - - # Skip non-markdown table lines and table schema lines - if !line.start_with?('|') || line.eql?("|---|---|---|---|---|\n") - next - end - - values = line.split("|") - - ################### GLOBAL ################### - values.each.with_index do |val, val_index| - msg = "" - case val_index - when INDEX_TITLE..INDEX_LINK - # every line segment should start and end with exactly 1 space - if val[/\A */].size != 1 || val[/ *\z/].size != 1 - add_error(line_num, val_index, "string should start and end with exactly 1 space") - end - end - end - ################# DESCRIPTION ################ - # First character should be capitalized - desc_val = values[INDEX_DESCRIPTION].lstrip.chop - if !/[[:upper:]]/.match(desc_val[0]) - add_error(line_num, INDEX_DESCRIPTION, "first char not uppercase") - end - # value should not be punctuated - last_char = desc_val[desc_val.length-1] - if punctuation.include?(last_char) - add_error(line_num, INDEX_DESCRIPTION, "description should not end with \"#{last_char}\"") - end - #################### AUTH #################### - # Values should conform to valid options only - auth_val = values[INDEX_AUTH].lstrip.chop.tr('``', '') - if !auth_keys.include?(auth_val) - add_error(line_num, INDEX_AUTH, "not a valid option: #{auth_val}") - end - #################### HTTPS ################### - # Values should be either "Yes" or "No" - https_val = values[INDEX_HTTPS].lstrip.chop - if !https_keys.include?(https_val) - add_error(line_num, INDEX_HTTPS, "must use \"Yes\" or \"No\": #{https_val}") - end - #################### LINK #################### - # Url should be wrapped in "[Go!]" view - link_val = values[INDEX_LINK].lstrip.chop - if !link_val.start_with?("[Go!](") || !link_val.end_with?(')') - add_error(line_num, INDEX_LINK, "format should be \"[Go!]()\": #{link_val}") - end -end -$errors.each do | e | - puts e -end -exit($errors.length)