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"},
26 "enum": ["production", "experimental", "development"],
28 "features": {"$ref": "#/definitions/features"},
29 "missing": {"$ref": "#/definitions/features"},
32 "items": {"type": "string", "enum": ["API", "CLI", "STATS", "MULTITHREAD"]},
35 "additionalProperties": False,
41 "items": {"type": "string"},
49 "patternProperties": {
50 "^.*$": {"$ref": "#/definitions/features"},
57 {"$ref": "#/definitions/featureobject"},
66 DEFAULT_REPO_LINK = "https://github.com/FDio/vpp/blob/master/"
69 def filelist_from_git_status():
71 git_status = "git status --porcelain */FEATURE*.yaml"
72 rv = run(git_status.split(), stdout=PIPE, stderr=PIPE)
73 if rv.returncode != 0:
74 sys.exit(rv.returncode)
76 for l in rv.stdout.decode("ascii").split("\n"):
78 filelist.append(l.split()[1])
82 def filelist_from_git_ls():
84 git_ls = "git ls-files :(top)*/FEATURE*.yaml"
85 rv = run(git_ls.split(), stdout=PIPE, stderr=PIPE)
86 if rv.returncode != 0:
87 sys.exit(rv.returncode)
89 for l in rv.stdout.decode("ascii").split("\n"):
95 def version_from_git():
96 git_describe = "git describe"
97 rv = run(git_describe.split(), stdout=PIPE, stderr=PIPE)
98 if rv.returncode != 0:
99 sys.exit(rv.returncode)
100 return rv.stdout.decode("ascii").split("\n")[0]
106 def __init__(self, stream):
110 def print_maintainer(self, o):
111 write = self.stream.write
113 write("Maintainers: " + ", ".join("{m}".format(m=m) for m in o) + " \n")
115 write("Maintainer: {o} \n".format(o=o))
117 _dispatch["maintainer"] = print_maintainer
119 def print_features(self, o, indent=0):
120 write = self.stream.write
122 indentstr = " " * indent
124 for k, v in f.items():
125 write("{indentstr}- {k}\n".format(indentstr=indentstr, k=k))
126 self.print_features(v, indent + 2)
128 write("{indentstr}- {f}\n".format(indentstr=indentstr, f=f))
131 _dispatch["features"] = print_features
133 def print_markdown_header(self, o):
134 write = self.stream.write
135 write("## {o}\n".format(o=o))
137 _dispatch["markdown_header"] = print_markdown_header
139 def print_name(self, o):
140 write = self.stream.write
141 write("### {o}\n".format(o=o))
144 _dispatch["name"] = print_name
146 def print_description(self, o):
147 write = self.stream.write
148 write("\n{o}\n\n".format(o=o))
150 _dispatch["description"] = print_description
152 def print_state(self, o):
153 write = self.stream.write
154 write("Feature maturity level: {o} \n".format(o=o))
156 _dispatch["state"] = print_state
158 def print_properties(self, o):
159 write = self.stream.write
160 write("Supports: {s} \n".format(s=" ".join(o)))
162 _dispatch["properties"] = print_properties
164 def print_missing(self, o):
165 write = self.stream.write
166 write("\nNot yet implemented: \n")
167 self.print_features(o)
169 _dispatch["missing"] = print_missing
171 def print_code(self, o):
172 write = self.stream.write
173 write("Source Code: [{o}]({o}) \n".format(o=o))
175 _dispatch["code"] = print_code
177 def print(self, t, o):
178 write = self.stream.write
179 if t in self._dispatch:
185 write("NOT IMPLEMENTED: {t}\n")
188 def output_toc(toc, stream):
190 write("# VPP Supported Features\n")
193 ref = t.lower().replace(" ", "-")
194 write("[{t}](#{ref}) \n".format(t=t, ref=ref))
201 def featurelistsort(k):
212 return orderedfields[k[0]]
215 def output_markdown(features, fields, notfields, repository_url):
218 m.print("markdown_header", "Feature Details:")
219 for path, featuredef in sorted(features.items(), key=featuresort):
220 codeurl = urllib.parse.urljoin(repository_url, os.path.dirname(path))
221 featuredef["code"] = codeurl
222 for k, v in sorted(featuredef.items(), key=featurelistsort):
224 if k not in notfields:
232 tocstream = StringIO()
233 output_toc(m.toc, tocstream)
234 return tocstream, stream
238 parser = argparse.ArgumentParser(description="VPP Feature List.")
243 help="validate the FEATURE.yaml file",
248 default=DEFAULT_REPO_LINK,
249 help="Link to public repository [%s]" % DEFAULT_REPO_LINK,
255 help="Get filelist from git status",
261 help="Validate all files in repository",
267 help="Output feature table in markdown",
270 "infile", nargs="?", type=argparse.FileType("r"), default=sys.stdin
272 group = parser.add_mutually_exclusive_group()
273 group.add_argument("--include", help="List of fields to include")
274 group.add_argument("--exclude", help="List of fields to exclude")
275 args = parser.parse_args()
279 filelist = filelist_from_git_status()
281 filelist = filelist_from_git_ls()
283 filelist = args.infile
286 fields = args.include.split(",")
290 notfields = args.exclude.split(",")
294 for featurefile in filelist:
295 featurefile = featurefile.rstrip()
297 # Load configuration file
298 with open(featurefile, encoding="utf-8") as f:
299 cfg = yaml.load(f, Loader=yaml.SafeLoader)
301 validate(instance=cfg, schema=schema)
302 except exceptions.ValidationError:
304 "File does not validate: {featurefile}".format(featurefile=featurefile),
308 features[featurefile] = cfg
312 tocstream, stream = output_markdown(features, fields, notfields, args.repolink)
313 print(tocstream.getvalue())
314 print(stream.getvalue())
318 if __name__ == "__main__":