docs-update.py 9.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272
  1. #!/usr/bin/env python
  2. #
  3. # Sven's quick hack script to update the documentation
  4. #
  5. # call with:
  6. # ./docs/update.py /usr/bin/docker
  7. #
  8. import datetime
  9. import re
  10. from sys import argv
  11. import subprocess
  12. import os
  13. import os.path
  14. script, docker_cmd = argv
  15. date_string = datetime.date.today().strftime('%B %Y')
  16. def print_usage(outtext, docker_cmd, command):
  17. try:
  18. help_string = subprocess.check_output(
  19. "".join((docker_cmd, " ", command, " --help")),
  20. stderr=subprocess.STDOUT,
  21. shell=True
  22. )
  23. except subprocess.CalledProcessError, e:
  24. help_string = e.output
  25. for l in str(help_string).strip().split("\n"):
  26. l = l.rstrip()
  27. if l == '':
  28. outtext.write("\n")
  29. else:
  30. # `docker --help` tells the user the path they called it with
  31. l = re.sub(docker_cmd, "docker", l)
  32. outtext.write(" {}\n".format(l))
  33. outtext.write("\n")
  34. # TODO: look for an complain about any missing commands
  35. def update_cli_reference():
  36. originalFile = "docs/sources/reference/commandline/cli.md"
  37. os.rename(originalFile, originalFile+".bak")
  38. intext = open("{}.bak".format(originalFile), "r")
  39. outtext = open(originalFile, "w")
  40. mode = 'p'
  41. space = " "
  42. command = ""
  43. # 2 mode line-by line parser
  44. for line in intext:
  45. if mode == 'p':
  46. # Prose
  47. match = re.match("( \s*)Usage: docker ([a-z]+)", line)
  48. if match:
  49. # the beginning of a Docker command usage block
  50. space = match.group(1)
  51. command = match.group(2)
  52. mode = 'c'
  53. else:
  54. match = re.match("( \s*)Usage of .*docker.*:", line)
  55. if match:
  56. # the beginning of the Docker --help usage block
  57. space = match.group(1)
  58. command = ""
  59. mode = 'c'
  60. else:
  61. outtext.write(line)
  62. else:
  63. # command usage block
  64. match = re.match("("+space+")(.*)|^$", line)
  65. if not match:
  66. # The end of the current usage block
  67. # Shell out to run docker to see the new output
  68. print_usage(outtext, docker_cmd, command)
  69. outtext.write(line)
  70. mode = 'p'
  71. if mode == 'c':
  72. print_usage(outtext, docker_cmd, command)
  73. def update_man_pages():
  74. cmds = []
  75. try:
  76. help_string = subprocess.check_output(
  77. "".join((docker_cmd)),
  78. stderr=subprocess.STDOUT,
  79. shell=True
  80. )
  81. except subprocess.CalledProcessError, e:
  82. help_string = e.output
  83. for l in str(help_string).strip().split("\n"):
  84. l = l.rstrip()
  85. if l != "":
  86. match = re.match(" (.*?) .*", l)
  87. if match:
  88. cmds.append(match.group(1))
  89. desc_re = re.compile(
  90. r".*# DESCRIPTION(.*?)# (OPTIONS|EXAMPLES?).*",
  91. re.MULTILINE | re.DOTALL
  92. )
  93. options_re = re.compile(
  94. r".*# OPTIONS(.*?)# (HISTORY|EXAMPLES?).*",
  95. re.MULTILINE | re.DOTALL
  96. )
  97. example_re = re.compile(
  98. r".*# EXAMPLES?(.*)# HISTORY.*",
  99. re.MULTILINE | re.DOTALL
  100. )
  101. history_re = re.compile(
  102. r".*# HISTORY(.*)",
  103. re.MULTILINE | re.DOTALL
  104. )
  105. for command in cmds:
  106. print "COMMAND: "+command
  107. if command == "":
  108. print "SKIPPING"
  109. continue
  110. history = ""
  111. description = ""
  112. original_options = ""
  113. examples = ""
  114. if os.path.isfile("docs/man/docker-"+command+".1.md"):
  115. intext = open("docs/man/docker-"+command+".1.md", "r")
  116. txt = intext.read()
  117. intext.close()
  118. match = desc_re.match(txt)
  119. if match:
  120. description = match.group(1)
  121. match = options_re.match(txt)
  122. if match:
  123. original_options = match.group(1)
  124. #print "MATCHED OPTIONS\n" + original_options
  125. match = example_re.match(txt)
  126. if match:
  127. examples = match.group(1)
  128. match = history_re.match(txt)
  129. if match:
  130. history = match.group(1).strip()
  131. usage = ""
  132. usage_description = ""
  133. params = {}
  134. key_params = {}
  135. try:
  136. help_string = subprocess.check_output(
  137. "".join((docker_cmd, " ", command, " --help")),
  138. stderr=subprocess.STDOUT,
  139. shell=True
  140. )
  141. except subprocess.CalledProcessError, e:
  142. help_string = e.output
  143. last_key = ""
  144. for l in str(help_string).split("\n"):
  145. l = l.rstrip()
  146. if l != "":
  147. match = re.match("Usage: docker {}(.*)".format(command), l)
  148. if match:
  149. usage = match.group(1).strip()
  150. else:
  151. match = re.match(" (-+)(.*) \s+(.*)", l)
  152. if match:
  153. last_key = match.group(2).rstrip()
  154. key_params[last_key] = match.group(1)+last_key
  155. params[last_key] = match.group(3)
  156. else:
  157. if last_key != "":
  158. params[last_key] = "{}\n{}".format(params[last_key], l)
  159. else:
  160. if usage_description != "":
  161. usage_description = usage_description + "\n"
  162. usage_description = usage_description + l
  163. # replace [OPTIONS] with the list of params
  164. options = ""
  165. match = re.match("\[OPTIONS\]\s*(.*)", usage)
  166. if match:
  167. usage = match.group(1)
  168. new_usage = ""
  169. # TODO: sort without the `-`'s
  170. for key in sorted(params.keys(), key=lambda s: s.lower()):
  171. # split on commas, remove --?.*=.*, put in *'s mumble
  172. flags = []
  173. ps = []
  174. opts = []
  175. for k in key_params[key].split(","):
  176. match = re.match("(-+)([A-Za-z-0-9]*)(?:=(.*))?", k.lstrip())
  177. if match:
  178. flags.append("{}{}".format(match.group(1), match.group(2)))
  179. p = "**{}{}**".format(match.group(1), match.group(2))
  180. o = "**{}{}**".format(match.group(1), match.group(2))
  181. if match.group(3):
  182. val = match.group(3)
  183. if val == "\"\"":
  184. val = match.group(2).upper()
  185. p = "{}[=*{}*]".format(p, val)
  186. val = match.group(3)
  187. if val in ("true", "false"):
  188. params[key] = params[key].rstrip()
  189. if not params[key].endswith('.'):
  190. params[key] = params[key]+ "."
  191. params[key] = "{} The default is *{}*.".format(params[key], val)
  192. val = "*true*|*false*"
  193. o = "{}={}".format(o, val)
  194. ps.append(p)
  195. opts.append(o)
  196. else:
  197. print "nomatch:{}".format(k)
  198. new_usage = "{}\n[{}]".format(new_usage, "|".join(ps))
  199. options = "{}{}\n {}\n\n".format(options, ", ".join(opts), params[key])
  200. # look at the original options documentation and if its hand written, add it too.
  201. print "SVEN_re: "+flags[0]
  202. singleoption_re = re.compile(
  203. r".*[\r\n]\*\*"+flags[0]+"\*\*([^\r\n]*)[\r\n]+(.*?)[\r\n](\*\*-|# [A-Z]|\*\*[A-Z]+\*\*).*",
  204. #r""+flags[0]+"(.*)(^\*\*-.*)?",
  205. re.MULTILINE | re.DOTALL
  206. )
  207. match = singleoption_re.match(original_options)
  208. if match:
  209. info = match.group(2).strip()
  210. print "MATCHED: " + match.group(1).strip()
  211. if info != params[key].strip():
  212. #info = re.sub(params[key].strip(), '', info, flags=re.MULTILINE)
  213. print "INFO changed: " +info
  214. options = "{} {}\n\n".format(options, info.strip())
  215. if new_usage != "":
  216. new_usage = "{}\n".format(new_usage.strip())
  217. usage = new_usage + usage
  218. outtext = open("docs/man/docker-{}.1.md".format(command), "w")
  219. outtext.write("""% DOCKER(1) Docker User Manuals
  220. % Docker Community
  221. % JUNE 2014
  222. # NAME
  223. """)
  224. outtext.write("docker-{} - {}\n\n".format(command, usage_description))
  225. outtext.write("# SYNOPSIS\n**docker {}**\n{}\n\n".format(command, usage))
  226. if description != "":
  227. outtext.write("# DESCRIPTION{}".format(description))
  228. if options == "":
  229. options = "There are no available options.\n\n"
  230. outtext.write("# OPTIONS\n{}".format(options))
  231. if examples != "":
  232. outtext.write("# EXAMPLES{}".format(examples))
  233. outtext.write("# HISTORY\n")
  234. if history != "":
  235. outtext.write("{}\n".format(history))
  236. recent_history_re = re.compile(
  237. ".*{}.*".format(date_string),
  238. re.MULTILINE | re.DOTALL
  239. )
  240. # if not recent_history_re.match(history):
  241. # outtext.write("{}, updated by Sven Dowideit <SvenDowideit@home.org.au>\n".format(date_string))
  242. outtext.close()
  243. # main
  244. update_cli_reference()
  245. update_man_pages()