buildVersion.py 10.9 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48
#!/usr/bin/env python
# -*- coding: utf-8 -*-
""" 
   NAME
          buildVersion -- helper script to build and tag a track_lhcbfrance version

    SYNOPSIS
          buildVersion [options] version

    DESCRIPTION
          Helper script to build a version of the track_lhcbfrance.
          
          The version identifier should contains alphanumeric characters 
          including ".", "-" and "_".
          
          Push version identifier in the javascript library.
          Push version number in the CHANGELOG.
          Build debug and minified version of the javascript library.
          Commit the new version in git and tag it
          Build the web2py plugin file
              
    EXAMPLES

            > buildVersion -h
            > buildVersion -g
            > buildVersion -g 0.8.2
            > buildVersion -a 0.8.3

    AUTHOR
          R. Le Gac, renaud.legac@free.fr

    Copyright (c) 2012-2014 R. Le Gac
    
"""

import datetime
import optparse
import os
import re
import subprocess
import sys
import tempfile
import urllib


# constants
APP = os.path.basename(os.getcwd())
CHANGELOG = 'static/CHANGELOG'
49 50 51 52
DBUIJSSRC = 'static/plugin_dbui/src'
EXTJSSRC = 'static/plugin_extjs/src'
JSDOC = 'static/docs/jsduck'
JSLIBMIN = 'static/%s-min.js' % APP
53
JSLIBSRC = 'static/src'
54
LATEXDOC = 'static/docs/latex'
55
PYDOC = 'static/docs/epydoc'
56
PDFDOC = 'static/docs/pdf'
LE GAC Renaud's avatar
LE GAC Renaud committed
57 58
SPHINXDOC = 'static/docs/sphinx'
SPHINXSRC = 'documentations/userguide/'
59 60 61 62

NOW = datetime.datetime.now()

# basic commands
63
JSDUCK = os.path.expandvars("$HOME/bin/jsduck")
64
GIT = '/usr/bin/git'
65
PDFLATEX = '/usr/bin/pdflatex'
66
SENCHA = os.path.expandvars("$HOME/bin/sencha")
LE GAC Renaud's avatar
LE GAC Renaud committed
67
SPHINX = '/usr/bin/sphinx-build'
68

69 70
# message
MSG_VERSION = 'Enter the new version: '
71

72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92

def compile():
    """compile the javascript code and generate the minified version
     of the application library.
    
    The compiler verify that the code complied with the class model
    and order the file in the proper way.
    
    The minified library can be build in several ways, including
    the Ext JS class required by the applications. In the
    current version, the library contains only the application classes and
    the Ext JS ones have to be loaded separately.
    
    Several compressor can be used yui, closure compiler, ....
    In the current version, the default yui compressor is used?
    
    This operation relies on the Sencha Cmd:
    http://www.sencha.com/products/sencha-cmd/download
    
    The details documantation can be found:
    http://docs.sencha.com/extjs/4.2.2/#!/guide/command
93 94
    
    """
95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115
    if not os.path.exists(SENCHA):
        print '\n\tThe application sencha is missing !'
        print '\tSee: http://www.sencha.com/products/sencha-cmd/download'
        print '\tSkip this step.\n'
        return

    # clean previous build
    if os.path.exists(JSLIBMIN):
        os.remove(JSLIBMIN)
    
    # Minified version of the javascript library
    print '\nMinified version of the javascript library', JSLIBMIN

    cwd = os.getcwd()
   
    cmd = ["sencha", "-sdk", os.path.join(cwd, EXTJSSRC),
           "compile", "-class", os.path.join(cwd, JSLIBSRC), 
           "exclude", "--namespace", "Ext",
           "and", "concat", "--yui", os.path.join(cwd, JSLIBMIN)]
    
    subprocess.call(cmd)
116 117 118 119


def epydoc():
    """Generate the epydoc documentation
120
    The HTML files are located in static/plugin_dbui/docs/epydoc
121 122
    
    """
123 124 125 126
    if not os.path.exists(GIT):
        print '\n\tThe application epydoc is missing !'
        print '\tSkip this step.\n'
        return
127
    
128 129 130
    # create the directory
    if not os.path.exists(PYDOC):
        os.makedirs(PYDOC)
131 132
    
    # clean the directory
133 134
    cmd = ["rm", "-rf", PYDOC]
    subprocess.call(cmd)
135

136
    # run epydoc
137 138
    cmd = ["epydoc", "--docformat", "epytext",
                     "--html",
139 140
                     "--name", APP,
                     "-o", PYDOC,
141 142 143 144 145 146
                     "--parse-only",
                     "-v",
                     "modules/*.py"]

    subprocess.call(cmd)

147
    print "HTML documentation in", PYDOC
148 149 150 151 152 153 154 155 156 157 158 159
    

def get_version():
    """Return the identifier of the current version.
    
    """
    fi = tempfile.TemporaryFile()
    subprocess.call(["git", "describe", "--tags"], stdout=fi)
    fi.seek(0)
    return fi.read()


LE GAC Renaud's avatar
LE GAC Renaud committed
160
def git(version):
161 162 163
    """Commit and tag the current release.
    
    """
164 165 166 167 168
    if not os.path.exists(GIT):
        print '\n\tThe application git is missing !'
        print '\tSkip this step.\n'
        return
    
169 170 171 172
    # check tag in git
    fi = tempfile.TemporaryFile()
    subprocess.call(["git", "tag"], stdout=fi)
    fi.seek(0)
173 174
    if version in fi.read():
        print "\n\ttag %s already exit in git" % version
175 176 177
        sys.exit(1)
    
    # release message
178
    m = "Release version %s" % version
179 180 181 182 183 184 185 186 187 188 189
    
    # Commit the new release in git an tag it
    print 'git add', CHANGELOG
    cmd = ["git", "add", CHANGELOG]
    subprocess.call(cmd)
    
    print 'git commit'
    cmd = ["git", "commit", "-m", m]
    subprocess.call(cmd)
    
    # annotated tag
190 191
    print 'git tag', version
    cmd = ["git", "tag", "-a", version, "-m", m]
192 193 194
    subprocess.call(cmd)


195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223
def jsduck():
    """Generate the JavaScript documentation.
    The HTML files are located in static/docs/jsduck
    
    """
    if not os.path.exists(JSDUCK):
        print '\n\tThe application jsduck is missing !'
        print '\tSkip this step.\n'
        return
    
    # create the directory
    if not os.path.exists(JSDOC):
        os.makedirs(JSDOC)

    # clean the directory
    cmd = ["rm", "-rf", JSDOC]
    subprocess.call(cmd)

    # run JsDuck
    cmd = ["jsduck", EXTJSSRC, DBUIJSSRC, JSLIBSRC, \
           "--output", JSDOC, \
           "--title", "%s %s" % (APP, get_version()), \
           "--warnings=-all:"+EXTJSSRC]
    
    subprocess.call(cmd)

    print "JavaScript documentation in", JSDOC


224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248
def set_version(version):
    """Set version identifier in CHANGELOG
    
    """
    # look for a pattern HEAD in the CHANGELOG
    # split the the string in 2 parts (pre HEAD, post HEAD)
    print 'Set version in', CHANGELOG
    s = open(CHANGELOG, 'rb').read()
    
    m = re.match("(.+HEAD\n)(.*)", s, re.DOTALL)
    
    if m == None:
        print '\n\tNo HEAD tag in the CHANGELOG!\n'
        rep = raw_input('\tDo you want to continue [n]?')
        if rep not in ('y', 'yes'):
            sys.exit(1)

    # update the version and edit the CHANGELOG    
    s = '%s\n%s (%s)\n%s' % (m.group(1), version, NOW.strftime('%b %Y'), m.group(2))
    fi = open(CHANGELOG, 'wb')
    fi.write(s)
    fi.close()
    subprocess.call(["vim", CHANGELOG])

    # cleaning
LE GAC Renaud's avatar
LE GAC Renaud committed
249 250 251
    fn = "%s~" % CHANGELOG
    if os.path.exists(fn): 
        os.remove(fn)
252 253


LE GAC Renaud's avatar
LE GAC Renaud committed
254 255 256 257
def sphinx():
    """Generate the Sphinx documentation.
    
    """
LE GAC Renaud's avatar
LE GAC Renaud committed
258 259 260
    if not os.path.exists(SPHINX):
        print '\n\tThe application sphinx is missing !'
        print '\tSkip this step.\n'
LE GAC Renaud's avatar
LE GAC Renaud committed
261 262
        return
    
LE GAC Renaud's avatar
LE GAC Renaud committed
263 264 265
    if not os.path.exists(SPHINXSRC):
        return

266
    # generate the HTML version
LE GAC Renaud's avatar
LE GAC Renaud committed
267
    cmd = [SPHINX, "-b", "html", SPHINXSRC, SPHINXDOC]
LE GAC Renaud's avatar
LE GAC Renaud committed
268
    subprocess.call(cmd)
269
    print "\nSphinx HTML documentation in", SPHINXDOC,"\n"
LE GAC Renaud's avatar
LE GAC Renaud committed
270

271 272 273 274 275
    # generate the PDF version
    if not os.path.exists(PDFLATEX):
        print '\n\tThe application pdflatex is missing !'
        print '\tSkip this step.\n'
        return
276

277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315
    rep = raw_input("Produce the sphinx pdf (y/N) [n]:")
    if not rep.lower().startswith("y"):
        return
    
    if not os.path.exists(PDFDOC):
        os.makedirs(PDFDOC)

    # generate the latex
    cmd = [SPHINX, "-b", "latex", SPHINXSRC, LATEXDOC]
    subprocess.call(cmd)
    
    # find the tex file
    cwd = os.getcwd()
    os.chdir(LATEXDOC)
    li = [el for el in os.listdir('.') if el.endswith('.tex')]
    fn = (li[0] if len(li) == 1 else None)
     
    if fn:
        pdf = fn.replace('.tex', '.pdf')
 
        cmd = [PDFLATEX, "-output-directory", "../pdf", fn]
        
        # run pdflatex twice to get reference right
        subprocess.call(cmd)
        subprocess.call(cmd)
 
        # clean the pdf directory         
        os.chdir(cwd)
        for el in os.listdir(PDFDOC):
            if el.endswith('.pdf'):
                continue
            os.remove(os.path.join(PDFDOC, el))
                 
        cmd = ["rm", "-rf", LATEXDOC]
        subprocess.call(cmd)
         
        print "\nSphinx PDF documentation in", PDFDOC,"\n"

 
316
if __name__ == '__main__':
317
            
318 319 320 321 322 323 324
    ops = optparse.OptionParser()

    ops.add_option("-a", "--all",
                   action="store_true",
                   dest= "all",
                   help= "run all steps.")
    
325
    ops.add_option("-c", "--compile",
326
                   action="store_true",
327 328
                   dest="compile",
                   help="compile the javascript library using sencha command.")
329 330 331 332 333 334 335

    ops.add_option("-e", "--epydoc",
                   action="store_true",
                   dest= "epydoc",
                   help= "generate the epydoc documentation.")
    
    ops.add_option("-g", "--git",
336
                   action="store_true",
337 338 339
                   dest= "git",
                   help= "commit and tag the current release.")

340 341 342 343 344
    ops.add_option("-j", "--jsduck",
                   action="store_true",
                   dest= "jsduck",
                   help= "generate the JavaScript documentation.")

345 346 347 348
    ops.add_option("-r", "--release",
                   action="store_true",
                   dest= "get",
                   help= "get the tag of the current release and exit.")
349

LE GAC Renaud's avatar
LE GAC Renaud committed
350
    ops.add_option("-s", "--sphinx",
351
                   action="store_true",
LE GAC Renaud's avatar
LE GAC Renaud committed
352 353 354 355 356 357
                   dest= "sphinx",
                   help= "generate the sphinx documentation.")

    ops.add_option("-v", "--version",
                   action="store_true",
                   dest= "version",
358
                   help= "set the version.")
359 360
    
    ops.set_defaults(all=False,
361
                     compile=False,
362 363
                     epydoc=False,
                     get=False,
364 365
                     git=False,
                     jsduck=False,
LE GAC Renaud's avatar
LE GAC Renaud committed
366 367
                     sphinx=False,
                     version=False)
368 369 370
    
    (opt, args) = ops.parse_args()
    
371 372 373
    print '\nStart buildVersion'
    
    # standalone action
374 375 376 377
    if opt.get:
        version = get_version()
        print "\nThe version of the current release is %s\n" % version
        
LE GAC Renaud's avatar
LE GAC Renaud committed
378
    if opt.version:
379 380 381 382
        version = (args[0] if args else raw_input(MSG_VERSION))
        set_version(version)
 
    if opt.git: 
LE GAC Renaud's avatar
LE GAC Renaud committed
383
        git(version)
384 385 386
    
    if opt.epydoc:
        epydoc()
387 388 389 390

    if opt.jsduck:
        jsduck()

LE GAC Renaud's avatar
LE GAC Renaud committed
391 392
    if opt.sphinx:
        sphinx()
393
        
394 395 396 397 398 399 400 401 402 403 404 405
    if opt.compile:
        compile()

    # run all steps
    if opt.all:

        version = get_version()
        print "\nThe version of the current release is %s\n" % version

        version = (args[0] if args else raw_input(MSG_VERSION))
        set_version(version)

LE GAC Renaud's avatar
LE GAC Renaud committed
406
        git(version)
407 408
        epydoc()
        jsduck()
LE GAC Renaud's avatar
LE GAC Renaud committed
409
        sphinx()
410
        compile()
411 412 413

    print 'Exit buidVersion\n'
    sys.exit(0)