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