8 from pprint import pprint
10 from jsonschema import validate, exceptions
12 from subprocess import run, PIPE
13 from io import StringIO
16 # VPP feature JSON schema
18 "$schema": "http://json-schema.org/schema#",
21 "name": {"type": "string"},
22 "description": {"type": "string"},
23 "maintainer": {"$ref": "#/definitions/maintainers"},
24 "state": {"type": "string",
25 "enum": ["production", "experimental", "development"]},
26 "features": {"$ref": "#/definitions/features"},
27 "missing": {"$ref": "#/definitions/features"},
28 "properties": {"type": "array",
29 "items": {"type": "string",
30 "enum": ["API", "CLI", "STATS",
34 "additionalProperties": False,
39 "items": {"type": "string"},
46 "patternProperties": {
47 "^.*$": {"$ref": "#/definitions/features"},
52 "items": {"anyOf": [{"$ref": "#/definitions/featureobject"},
60 DEFAULT_REPO_LINK = "https://github.com/FDio/vpp/blob/master/"
62 def filelist_from_git_status():
64 git_status = 'git status --porcelain */FEATURE*.yaml'
65 rv = run(git_status.split(), stdout=PIPE, stderr=PIPE)
66 if rv.returncode != 0:
67 sys.exit(rv.returncode)
69 for l in rv.stdout.decode('ascii').split('\n'):
71 filelist.append(l.split()[1])
75 def filelist_from_git_ls():
77 git_ls = 'git ls-files :(top)*/FEATURE*.yaml'
78 rv = run(git_ls.split(), stdout=PIPE, stderr=PIPE)
79 if rv.returncode != 0:
80 sys.exit(rv.returncode)
82 for l in rv.stdout.decode('ascii').split('\n'):
87 def version_from_git():
88 git_describe = 'git describe'
89 rv = run(git_describe.split(), stdout=PIPE, stderr=PIPE)
90 if rv.returncode != 0:
91 sys.exit(rv.returncode)
92 return rv.stdout.decode('ascii').split('\n')[0]
97 def __init__(self, stream):
101 def print_maintainer(self, o):
102 write = self.stream.write
104 write('Maintainers: ' +
105 ', '.join('{m}'.format(m=m) for m in
108 write('Maintainer: {o} \n'.format(o=o))
110 _dispatch['maintainer'] = print_maintainer
112 def print_features(self, o, indent=0):
113 write = self.stream.write
115 indentstr = ' ' * indent
117 for k, v in f.items():
118 write('{indentstr}- {k}\n'.format(indentstr=indentstr, k=k))
119 self.print_features(v, indent + 2)
121 write('{indentstr}- {f}\n'.format(indentstr=indentstr, f=f))
123 _dispatch['features'] = print_features
125 def print_markdown_header(self, o):
126 write = self.stream.write
127 write('## {o}\n'.format(o=o))
128 _dispatch['markdown_header'] = print_markdown_header
130 def print_name(self, o):
131 write = self.stream.write
132 write('### {o}\n'.format(o=o))
134 _dispatch['name'] = print_name
136 def print_description(self, o):
137 write = self.stream.write
138 write('\n{o}\n\n'.format(o=o))
139 _dispatch['description'] = print_description
141 def print_state(self, o):
142 write = self.stream.write
143 write('Feature maturity level: {o} \n'.format(o=o))
144 _dispatch['state'] = print_state
146 def print_properties(self, o):
147 write = self.stream.write
148 write('Supports: {s} \n'.format(s=" ".join(o)))
149 _dispatch['properties'] = print_properties
151 def print_missing(self, o):
152 write = self.stream.write
153 write('\nNot yet implemented: \n')
154 self.print_features(o)
155 _dispatch['missing'] = print_missing
157 def print_code(self, o):
158 write = self.stream.write
159 write('Source Code: [{o}]({o}) \n'.format(o=o))
160 _dispatch['code'] = print_code
162 def print(self, t, o):
163 write = self.stream.write
164 if t in self._dispatch:
165 self._dispatch[t](self, o,)
167 write('NOT IMPLEMENTED: {t}\n')
169 def output_toc(toc, stream):
171 write('# VPP Supported Features\n')
174 ref = t.lower().replace(' ', '-')
175 write('[{t}](#{ref}) \n'.format(t=t, ref=ref))
180 def featurelistsort(k):
191 return orderedfields[k[0]]
193 def output_markdown(features, fields, notfields, repository_url):
196 m.print('markdown_header', 'Feature Details:')
197 for path, featuredef in sorted(features.items(), key=featuresort):
198 codeurl = urllib.parse.urljoin(repository_url, os.path.dirname(path))
199 featuredef['code'] = codeurl
200 for k, v in sorted(featuredef.items(), key=featurelistsort):
202 if k not in notfields:
210 tocstream = StringIO()
211 output_toc(m.toc, tocstream)
212 return tocstream, stream
215 parser = argparse.ArgumentParser(description='VPP Feature List.')
216 parser.add_argument('--validate', dest='validate', action='store_true',
217 help='validate the FEATURE.yaml file')
218 parser.add_argument("--repolink", metavar="repolink", default=DEFAULT_REPO_LINK,
219 help="Link to public repository [%s]" %
221 parser.add_argument('--git-status', dest='git_status', action='store_true',
222 help='Get filelist from git status')
223 parser.add_argument('--all', dest='all', action='store_true',
224 help='Validate all files in repository')
225 parser.add_argument('--markdown', dest='markdown', action='store_true',
226 help='Output feature table in markdown')
227 parser.add_argument('infile', nargs='?', type=argparse.FileType('r'),
229 group = parser.add_mutually_exclusive_group()
230 group.add_argument('--include', help='List of fields to include')
231 group.add_argument('--exclude', help='List of fields to exclude')
232 args = parser.parse_args()
236 filelist = filelist_from_git_status()
238 filelist = filelist_from_git_ls()
240 filelist = args.infile
243 fields = args.include.split(',')
247 notfields = args.exclude.split(',')
251 for featurefile in filelist:
252 featurefile = featurefile.rstrip()
254 # Load configuration file
255 with open(featurefile, encoding='utf-8') as f:
256 cfg = yaml.load(f, Loader=yaml.SafeLoader)
258 validate(instance=cfg, schema=schema)
259 except exceptions.ValidationError:
260 print('File does not validate: {featurefile}' \
261 .format(featurefile=featurefile), file=sys.stderr)
263 features[featurefile] = cfg
267 tocstream, stream = output_markdown(features, fields, notfields, args.repolink)
268 print(tocstream.getvalue())
269 print(stream.getvalue())
273 if __name__ == '__main__':