Newer
Older
legac
committed
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
NAME
build_version -- helper script to build a plugin_dbui version
legac
committed
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
SYNOPSIS
build_version [options] version
DESCRIPTION
Helper script to build a version of the plugin_dbui.
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.
Build the web2py plugin file
EXAMPLES
> build_version -h
AUTHOR
R. Le Gac, renaud.legac@free.fr
Copyright (c) 2012-2015 R. Le Gac
"""
import datetime
import optparse
import os
import re
import sys
import tempfile
import urllib
from os.path import join as opj
legac
committed
from subprocess import call
# constants
APP = os.path.basename(os.getcwd())
API = 'api'
BUILDDIR = '../plugin_dbui_build'
CHANGELOG = 'static/plugin_dbui/CHANGELOG'
DBUI_W2P = 'web2py.plugin.dbui.%s.w2p'
DOCS = 'static/plugin_dbui/docs'
DOCSRC = 'docs'
EXTJSSRC = 'static/plugin_extjs/src'
JSBASE = 'static/plugin_dbui/src/App.js'
JSDOC = opj(DOCS, 'jsduck')
legac
committed
JSLIBDEBUG = 'static/plugin_dbui/dbui-debug.js'
JSLIBMIN = 'static/plugin_dbui/dbui-min.js'
JSLIBSRC = 'static/plugin_dbui/src'
LATEX = 'latex'
LATEXDOC = opj(DOCS, LATEX)
legac
committed
NOW = datetime.datetime.now()
PACK_PLUGIN_URL = 'http://localhost:8000/%s/default/pack_plugin' % APP
PDF = "pdf"
PDFDOC = opj(DOCS, PDF)
legac
committed
REFERENCE = 'reference'
legac
committed
# basic commands
GIT = '/usr/bin/git'
JSDUCK = os.path.expandvars("$HOME/bin/jsduck")
PDFLATEX = '/usr/bin/pdflatex'
SENCHA = os.path.expandvars("$HOME/bin/sencha")
SPHINX = '/usr/bin/sphinx-build'
MSG_RELEASE = 'Enter the new release: '
def build():
"""Compile the javascript code and build documentations.
"""
compile_js()
build_html()
for doc in (API, REFERENCE, USER):
docsrc = opj(DOCSRC, doc)
if os.path.exists(docsrc):
build_pdf(doc)
legac
committed
def build_html():
"""Build HTML documentations.
The source files are located in the following directory::
myapp/docs
myapp/docs/api
myapp/docs/reference
myapp/docs/user
legac
committed
"""
print "Build the HTML documentations..."
jsduck()
for doc in (API, REFERENCE, USER):
docsrc = opj(DOCSRC, doc)
legac
committed
if os.path.exists(docsrc):
sphinx("-b html", docsrc, opj(DOCS, doc))
legac
committed
def build_pdf(doc):
legac
committed
""" Build PDF documentations...
Args:
doc (str): sub-directory. Possible values are api, reference, user.
legac
committed
"""
print "Build the PDF documentations..."
# generate the latex
sphinx("-b latex", opj(DOCSRC, doc), LATEXDOC)
legac
committed
# the current directory
cwd = os.getcwd()
# find the name of the tex file
os.chdir(LATEXDOC)
legac
committed
li = [el for el in os.listdir('.') if el.endswith('.tex')]
fn = (li[0] if len(li) == 1 else None)
if not fn:
print "\n\tNo latex file !"
return
# process the latex file twice
call([PDFLATEX, fn])
call([PDFLATEX, fn])
legac
committed
legac
committed
os.chdir(cwd)
if not os.path.exists(PDFDOC):
os.mkdir(PDFDOC)
fin = fn.replace('.tex', '.pdf')
fout = "%s_%s.pdf" % (os.path.splitext(fn)[0], doc)
os.rename(opj(LATEXDOC, fin), opj(PDFDOC, fout))
legac
committed
# remove the latex directory
call(["rm", "-rf", LATEXDOC])
legac
committed
print "%s documentation in" % fout, PDFDOC
legac
committed
def commit_change_log():
legac
committed
"""Commit CHANGELOG and App.js.
"""
print "Commit CHANGELOG and App.js..."
if not os.path.exists(GIT):
print '\n\tThe application git is missing !'
print '\tSkip this step.\n'
return
# move to the master branch
git("checkout master")
legac
committed
# Commit modified files
print 'git add', JSBASE, CHANGELOG
git("add", JSBASE, CHANGELOG)
print 'git commit'
msg = "Start release candidate %s" % get_version()
legac
committed
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
git("commit -m", msg)
def compile_js():
"""compile_js the javascript code and generate the debug version
as well as the minified version of the dbui 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 dbui classes and
the Ext JS ones have to be loaded separately. In that sense
this command is very similar to the yuicompressor one.
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
"""
print "Compile the javascript code ..."
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 version
for el in (JSLIBDEBUG, JSLIBMIN):
if os.path.exists(el):
os.remove(el)
print 'Remove old javascript library', el
# debug version of the javascript library
cwd = os.getcwd()
cmd = ["sencha", "-sdk", opj(cwd, EXTJSSRC),
"compile", "-class", opj(cwd, JSLIBSRC),
legac
committed
"exclude", "--namespace", "Ext",
"and", "concat", opj(cwd, JSLIBDEBUG)]
legac
committed
call(cmd)
print 'Debug version of the javascript library', JSLIBDEBUG, 'is ready'
# Minified version of the javascript library
cmd = ["sencha", "-sdk", opj(cwd, EXTJSSRC),
"compile", "-class", opj(cwd, JSLIBSRC),
legac
committed
"exclude", "--namespace", "Ext",
"and", "concat", "--yui", opj(cwd, JSLIBMIN)]
legac
committed
call(cmd)
print 'Minified version of the javascript library', JSLIBMIN, 'is ready'
def get_version():
"""Get the current version identifier.
"""
txt = open(JSBASE, 'rb').read()
match = re.match(r"(.+ version: ')([\w._-]*)('.+)", txt, re.DOTALL)
return match.group(2)
def git(*args, **kwargs):
"""run any git instruction:
Args:
*args: list of variable arguments containing git command
and their options
**kwargs: keyword argument of the function ``subprocess.call``.
Examples:
git("add CHANGELOG foo.txt")
git("add", CHANGELOG, "foo.txt")
legac
committed
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
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
git("commit", stdout=fi)
"""
if not os.path.exists(GIT):
print '\n\tThe application git is missing !'
print '\tSkip this step.\n'
return
cmd = [GIT]
cmd.extend(args[0].split())
if len(args) > 1:
cmd.extend(args[1:])
call(cmd, **kwargs)
def jsduck():
"""Generate the JavaScript documentation.
The HTML files are located in static/plugin_dbui/docs/jsduck
"""
print "Build the javascript documentation..."
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]
call(cmd)
# run JsDuck
cmd = ["jsduck", EXTJSSRC, JSLIBSRC, \
"--output", JSDOC, \
"--title", "plugin_dbui %s" % get_version(), \
"--warnings=-all:" + EXTJSSRC]
call(cmd)
print "JavaScript documentation in", JSDOC
def set_version(version):
"""Set release identifier in CHANGELOG and App.js
Args:
version (str): release identifier
legac
committed
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
"""
print "Update CHANGELOG and App.js files with the release", version, "..."
# check tag in git
fi = tempfile.TemporaryFile()
git("tag", stdout=fi)
fi.seek(0)
if version in fi.read():
print "\n\tRelease %s already exit in git" % version
sys.exit(1)
print 'Set release', version, 'in', JSBASE
txt = open(JSBASE, 'rb').read()
# look for a pattern App.version = '0.8.3'; in appbase.js
# split the the string in 3 parts (pre, version, post)
match = re.match(r"(.+ version: ')([\w._-]*)('.+)", txt, re.DOTALL)
if match.group(2) == version:
msg = '\n\tVersion "%s" already exists in the appbase.js file !'
print msg % version
rep = raw_input('\tDo you want to continue [n]?')
if rep not in ('y', 'yes'):
sys.exit(1)
# update the version and write a new file
txt = match.group(1) + version + match.group(3)
fi = open(JSBASE, 'wb')
fi.write(txt)
fi.close()
# look for a pattern HEAD in the CHANGELOG
# split the the string in 2 parts (pre HEAD, post HEAD)
print 'Set release', version, 'in', CHANGELOG
txt = open(CHANGELOG, 'rb').read()
match = re.match("(.+HEAD\n)(.*)", txt, re.DOTALL)
if match == 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
tpl = (match.group(1), version, NOW.strftime('%b %Y'), match.group(2))
txt = '%s\n%s (%s)\n%s' % tpl
fi = open(CHANGELOG, 'wb')
fi.write(txt)
fi.close()
call(["vim", CHANGELOG])
# cleaning
fn = "%s~" % CHANGELOG
if os.path.exists(fn):
os.remove(fn)
def sphinx(*args, **kwargs):
"""run the sphinx-build:
Args:
*args: list of variable arguments containing the sphinx-build options.
**kwargs: keyword argument of the function ``subprocess.call``.
Examples:
sphinx("-b html sourcedir outdir")
sphinx("-b html", sourcedir, outdir)
legac
committed
"""
if not os.path.exists(SPHINX):
print '\n\tThe application sphinx is missing !'
print '\tSkip this step.\n'
return
cmd = [SPHINX]
cmd.extend(args[0].split())
if len(args) > 1:
cmd.extend(args[1:])
call(cmd, **kwargs)
def start_release():
"""Start the release cycle.
Set the new release number in the CHANGELOG and App.js files.
Commit then in the master branch.
legac
committed
"""
print "Start the release cycle..."
old_release = get_version()
print "Current release is", old_release
new_release = raw_input(MSG_RELEASE)
if not new_release:
sys.exit(0)
branch = "release-%s" % new_release
git("checkout", "-b", branch, "develop")
set_version(new_release)
commit_change_log()
legac
committed
build()
def tag():
"""Tag the release in the production branch, locally and on the
remote repository.
legac
committed
"""
print "Tag the release...",
# move to production branch
git("checkout", "production")
legac
committed
# annotated tag
version = get_version()
legac
committed
print 'git tag', version
msg = "Tag %s" % version
legac
committed
git("tag", "-a", version, "-m", msg)
# push the tag on the server
git("push --tags")
legac
committed
def web2py():
"""Produce the binary file for the web2py plugin.
"""
print 'Build the web2py plugin binary file...'
msg = 'Check that the web2py service is running [y/N]'
rep = raw_input(msg)
legac
committed
print '\n\tSkip this step.\n'
return
connection = urllib.urlopen(PACK_PLUGIN_URL)
txt = connection.read()
fn = DBUI_W2P % get_version().replace('.', '')
fi = open(fn, 'wb')
fi.write(txt)
fi.close()
# move the file to the build directory
fn_build = opj(BUILDDIR, fn)
legac
committed
if os.path.exists(fn_build):
os.remove(fn_build)
os.rename(fn, fn_build)
print 'Binary file', fn, 'is created in ', BUILDDIR
if __name__ == '__main__':
# define script options
OPS = optparse.OptionParser()
OPS.add_option("-a", "--api-doc",
action="store_true",
dest="api_doc",
help="build the API documentation in HTML.")
OPS.add_option("-A", "--api-pdf",
action="store_true",
dest="api_pdf",
help="build the API documentation in PDF.")
legac
committed
OPS.add_option("-c", "--compile",
action="store_true",
dest="compile",
help="compile the javascript library.")
OPS.add_option("--commit-changelog",
action="store_true",
dest="changelog",
help="commit CHANGELOG and App.js. "
"To be used with --write-release.")
legac
committed
OPS.add_option("-j", "--jsduck",
action="store_true",
dest="jsduck",
help="build the JavaScript documentation.")
OPS.add_option("-r", "--reference-doc",
action="store_true",
dest="reference_doc",
help="build the reference manual in HTML.")
OPS.add_option("-R", "--reference-pdf",
legac
committed
action="store_true",
dest="reference_pdf",
help="build the reference manual in PDF.")
OPS.add_option("-s", "--start-release",
legac
committed
action="store_true",
dest="start_release",
help="start the release candidate by setting "
"release number in change log and App.js. "
"Commit changes in the master branch. "
"Equivalent to --write-release followed by "
"--commit-changelog.")
legac
committed
OPS.add_option("-t", "--tag",
legac
committed
action="store_true",
dest="tag",
help="tag the release in the production branch "
"locally and on the remote repository.")
legac
committed
OPS.add_option("-u", "--user-doc",
action="store_true",
dest="user_doc",
help="build the user manual in HTML.")
OPS.add_option("-U", "--user-pdf",
action="store_true",
dest="user_pdf",
help="build the user manual in PDF.")
legac
committed
OPS.add_option("-v", "--version",
action="store_true",
dest="version",
help="get the current release identifier.")
OPS.add_option("-w", "--web2py",
action="store_true",
dest="web2py",
help="pack the plugin_dbui.")
OPS.add_option("--write-release",
action="store_true",
dest="release",
help="write the release number in CHANGELOG and App.js. "
"To be used with --commit-changelog.")
legac
committed
OPS.set_defaults(api_doc=False,
legac
committed
changelog=False,
compile=False,
jsduck=False,
reference_doc=False,
reference_pdf=False,
release=False,
start_release=False,
legac
committed
tag=False,
user_doc=False,
user_pdf=False,
legac
committed
version=False)
(OPT, ARGS) = OPS.parse_args()
if OPT.api_doc:
sphinx("-b html", opj(DOCSRC, API), opj(DOCS, API))
legac
committed
if OPT.compile:
compile_js()
if OPT.changelog:
commit_change_log()
legac
committed
if OPT.jsduck:
jsduck()
if OPT.release:
set_version(raw_input(MSG_RELEASE))
if OPT.reference_doc:
sphinx("-b html", opj(DOCSRC, REFERENCE), opj(DOCS, REFERENCE))
legac
committed
if OPT.reference_pdf:
build_pdf(REFERENCE)
if OPT.start_release:
start_release()
if OPT.tag:
tag()
if OPT.user_doc:
sphinx("-b html", opj(DOCSRC, USER), opj(DOCS, USER))
if OPT.user_pdf:
build_pdf(USER)