257 lines
8.7 KiB
Python
Executable file
257 lines
8.7 KiB
Python
Executable file
#!/bin/python
|
|
|
|
|
|
import sys
|
|
if sys.version_info.major < 3:
|
|
print('ERROR: current Python version is {0}.{1}.{2} whereas Python 3 is required.'.format(
|
|
sys.version_info[0], sys.version_info[1], sys.version_info[2]
|
|
))
|
|
sys.exit(1)
|
|
|
|
|
|
import argparse
|
|
import base64
|
|
import os
|
|
import re
|
|
import subprocess
|
|
import urllib.request
|
|
|
|
|
|
class Version:
|
|
def __init__(self, major=0, minor=0, patch=0, branch=None, ncommit=0, _hash=None, fromStr=None):
|
|
if fromStr is not None:
|
|
m = re.match('^([0-9]+)\\.([0-9]+)\\.([0-9]+)(-alpha|-beta|-pre)?((-[0-9]+)(-g[0-9a-f]+))?$', fromStr)
|
|
if m is None:
|
|
raise ValueError("'{0}' isn't a valid git describe string".format(fromStr))
|
|
major = m.group(1)
|
|
minor = m.group(2)
|
|
patch = m.group(3)
|
|
if m.group(4) is not None:
|
|
branch = m.group(4)[1:]
|
|
if m.group(5) is not None:
|
|
ncommit = int(m.group(6)[1:])
|
|
_hash = m.group(7)[2:]
|
|
|
|
if ncommit > 0 and _hash is None:
|
|
raise ValueError('missing hash')
|
|
if ncommit == 0 and _hash is not None:
|
|
raise ValueError('missing or null commit number')
|
|
|
|
self.major = major
|
|
self.minor = minor
|
|
self.patch = patch
|
|
self.branch = branch
|
|
self.ncommit = ncommit
|
|
self.hash = _hash
|
|
|
|
@property
|
|
def short_version(self):
|
|
return '{0}.{1}.{2}'.format(self.major, self.minor, self.patch)
|
|
|
|
@property
|
|
def git_version(self):
|
|
res = self.short_version
|
|
if self.branch is not None:
|
|
res += '-{0}'.format(self.branch)
|
|
if self.ncommit > 0:
|
|
res += '-{0}-g{1}'.format(self.ncommit, self.hash)
|
|
return res
|
|
|
|
|
|
class FlexisipProxy:
|
|
def __init__(self, binaryPath):
|
|
self.path = binaryPath
|
|
self._version = None
|
|
|
|
@property
|
|
def section_list(self):
|
|
p = subprocess.Popen([self.path, '--list-sections'], stdout=subprocess.PIPE , stderr=subprocess.PIPE)
|
|
out, err = p.communicate()
|
|
return str(out, encoding='utf-8').rstrip('\n').split('\n')
|
|
|
|
@property
|
|
def version(self):
|
|
if self._version is None:
|
|
_version = self._get_version()
|
|
return _version
|
|
|
|
def dump_section_doc(self, moduleName):
|
|
p = subprocess.Popen([self.path, '--dump-format', 'xwiki', '--show-experimental', '--dump-default', moduleName], stdout=subprocess.PIPE , stderr=subprocess.PIPE)
|
|
out, err = p.communicate()
|
|
out = str(out, encoding='utf-8')
|
|
# replace all the -- in the doc with {{{--}}} to escape xwiki autoformatting -- into striken
|
|
return re.sub("--", "{{{--}}}", out)
|
|
|
|
def _get_version(self):
|
|
p = subprocess.Popen([self.path, '-v'], stdout=subprocess.PIPE , stderr=subprocess.PIPE)
|
|
out, err = p.communicate()
|
|
out = str(out, encoding='utf-8')
|
|
m = re.search('version: ([.a-z0-9-]+)', out)
|
|
if m is None:
|
|
raise RuntimeError("unexpected output of 'flexisip -v': [{0}]".format(out))
|
|
version = m.group(1)
|
|
try:
|
|
return Version(fromStr=version)
|
|
except ValueError:
|
|
raise RuntimeError("invalid version string in output of 'flexisip -v' [{0}]".format(version))
|
|
|
|
|
|
class XWikiProxy:
|
|
class Credential:
|
|
def __init__(self, user, password):
|
|
self.user = user
|
|
self.password = password
|
|
|
|
def to_base64(self):
|
|
return base64.b64encode(bytes('{0}:{1}'.format(self.user, self.password), encoding='utf-8'))
|
|
|
|
def __init__(self, host, wikiname, credentials=None, cafile=None):
|
|
m = re.fullmatch('(?:(http[s]?)\\://)?(\\S+)', host)
|
|
if m is None:
|
|
raise ValueError('invalid host [{0}]'.format(host))
|
|
|
|
self.scheme = m.group(1) if m.group(1) is not None else 'http'
|
|
self.host = m.group(2)
|
|
self.wikiname = wikiname
|
|
self.credentials = credentials
|
|
self.cafile = cafile
|
|
|
|
def update_page(self, path, content):
|
|
uri = self._forge_page_uri(path)
|
|
request = self._forge_http_request(uri, 'PUT', content)
|
|
response = urllib.request.urlopen(request, cafile=self.cafile)
|
|
if response.status not in (201, 202):
|
|
raise RuntimeError('page creation or modification has failed' if response.status == 304 \
|
|
else 'unexpected status code ({0})'.format(response.status))
|
|
|
|
def _forge_page_uri(self, path):
|
|
uri = self._forge_root_uri()
|
|
|
|
scopepath = os.path.dirname(path)
|
|
scopepath = scopepath.split('/')
|
|
if scopepath[0] == '':
|
|
del scopepath[0]
|
|
for scopename in scopepath:
|
|
uri += ('/spaces/' + self._escape(scopename))
|
|
|
|
pagename = os.path.basename(path)
|
|
uri += ('/pages/' + self._escape(pagename))
|
|
return uri
|
|
|
|
def _escape(self, string):
|
|
return string.translate({0x20 : '%20'})
|
|
|
|
def _forge_root_uri(self):
|
|
return self.scheme + '://' + self.host + XWikiProxy._apipath + '/wikis/' + self.wikiname
|
|
|
|
_apipath = '/xwiki/rest'
|
|
|
|
def _forge_http_request(self, uri, method, body):
|
|
headers = { 'Content-Type': 'text/plain' }
|
|
if self.credentials is not None:
|
|
headers['Authorization'] = ('Basic ' + str(self.credentials.to_base64(), encoding='ascii'))
|
|
return urllib.request.Request(uri, data=bytes(body, encoding='utf-8'), headers=headers, method=method)
|
|
|
|
|
|
class DocWriter:
|
|
def __init__(self, wikiProxy, fProxy):
|
|
self.proxy = wikiProxy
|
|
self.fProxy = fProxy
|
|
self.documentRoot = '/Flexisip/A. Configuration Reference Guide'
|
|
|
|
def write_and_push(self):
|
|
fProxy = FlexisipProxy(args.flexisip_binary)
|
|
|
|
childrenMacro = '{{children/}}'
|
|
wiki.update_page(os.path.join(self.documentRoot, 'WebHome'), childrenMacro)
|
|
wiki.update_page(os.path.join(self._get_version_page_path(), 'WebHome'), childrenMacro)
|
|
wiki.update_page(os.path.join(self._get_version_page_path(), 'module/WebHome'), childrenMacro)
|
|
|
|
for section in fProxy.section_list:
|
|
out = fProxy.dump_section_doc(section)
|
|
|
|
#add commit version on top of the file
|
|
message = "// Documentation based on repostory git version commit {0} //\n\n".format(fProxy.version.git_version)
|
|
out = message + out
|
|
|
|
path = self._section_name_to_page_path(section)
|
|
|
|
print("Updating page '{0}'".format(path))
|
|
wiki.update_page(path, out)
|
|
|
|
def _section_name_to_page_path(self, module_name):
|
|
return os.path.join(self._get_version_page_path(), *tuple(module_name.split('::')))
|
|
|
|
def _get_version_page_path(self):
|
|
if fProxy.version.branch == 'alpha':
|
|
version = 'master'
|
|
elif fProxy.version.branch is None or fProxy.version.branch == 'beta':
|
|
version = fProxy.version.short_version
|
|
else:
|
|
raise RuntimeError("Reference documentation isn't authorized to be pushed for this version of Flexisip [{0}]".format(fProxy.version.git_version))
|
|
return os.path.join(self.documentRoot, version)
|
|
|
|
|
|
class Settings:
|
|
def __init__(self):
|
|
self.section_name = 'main'
|
|
self.host = ''
|
|
self.wikiname = ''
|
|
self.user = ''
|
|
self.password = ''
|
|
|
|
def load(self, filename):
|
|
import configparser
|
|
config = configparser.ConfigParser()
|
|
config.read(config_file)
|
|
self.host = config.get(self.section_name, 'host', fallback=self.host)
|
|
self.wikiname = config.get(self.section_name, 'wiki', fallback=self.wikiname)
|
|
self.user = config.get(self.section_name, 'username')
|
|
self.password = config.get(self.section_name, 'password')
|
|
|
|
def dump_example(self):
|
|
return """[{section}]
|
|
host=example.com
|
|
wiki=public
|
|
username=titi
|
|
password=toto""".format(self.section_name)
|
|
|
|
|
|
if __name__ == '__main__':
|
|
# parse cli arguments
|
|
parser = argparse.ArgumentParser(description='Send the Flexisip documentation to the Wiki. All options passed override the config file.')
|
|
parser.add_argument('-H', '--host' , help='the host to which we should send the documentation', default='wiki.linphone.org:8080')
|
|
parser.add_argument('-w', '--wiki' , help='name of the wiki', default='public', dest='wikiname')
|
|
parser.add_argument('-u', '--user' , help='the user to authenticate to the server', dest='config_user')
|
|
parser.add_argument('-p', '--password' , help='the password to authenticate to the server', dest='config_password')
|
|
parser.add_argument('--cafile' , help='file containing a set of trusted certificates', default=None)
|
|
parser.add_argument('--flexisip-binary', help='location of the Flexisip executable to run', default='../OUTPUT/bin/flexisip')
|
|
args = parser.parse_args()
|
|
|
|
# read from a configuration file for user/pass/host. This allows for out-of-cli specification of these parameters.
|
|
settings = Settings()
|
|
config_file = os.path.expanduser('~/.flexiwiki.x.cfg')
|
|
if os.access(config_file, os.R_OK):
|
|
settings.load(config_file)
|
|
|
|
# require a password for REST
|
|
if args.config_password is not None:
|
|
settings.password = args.config_password
|
|
if args.config_user is not None:
|
|
settings.user = args.config_user
|
|
if args.host is not None:
|
|
settings.host = args.host
|
|
if args.wikiname is not None:
|
|
settings.wikiname = args.wikiname
|
|
|
|
if settings.password == '':
|
|
print("Please define a password using " + config_file + " or using the --password option")
|
|
print("Example of " + config_file + ":")
|
|
print(settings.dump_example())
|
|
sys.exit(1)
|
|
|
|
credentials = XWikiProxy.Credential(settings.user, settings.password)
|
|
wiki = XWikiProxy(settings.host, settings.wikiname, credentials=credentials, cafile=args.cafile)
|
|
fProxy = FlexisipProxy(args.flexisip_binary)
|
|
docWriter = DocWriter(wiki, fProxy)
|
|
docWriter.write_and_push()
|