github.com/daaku/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()