/release.py
Python | 348 lines | 285 code | 19 blank | 44 comment | 21 complexity | de4e11ed782135f19d77f516c035d353 MD5 | raw file
Possible License(s): GPL-3.0
- #!/usr/bin/env python
- # *********************************************************************
- # * Copyright (C) 2012 Luca Baldini (luca.baldini@pi.infn.it) *
- # * *
- # * For the license terms see the file LICENCE, distributed *
- # * along with this software. *
- # *********************************************************************
- #
- # This program is free software; you can redistribute it and/or modify
- # it under the terms of the GNU General Public License as published by
- # the Free Software Foundation; either version 3 of the License, or
- # (at your option) any later version.
- #
- # This program is distributed in the hope that it will be useful,
- # but WITHOUT ANY WARRANTY; without even the implied warranty of
- # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- # GNU General Public License for more details.
- #
- # You should have received a copy of the GNU General Public License along
- # with this program; if not, write to the Free Software Foundation, Inc.,
- # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- """ Release manager for the plasduino package.
- """
- import os
- import sys
- import time
- import glob
- # Small hack to be able to run the release script without having to
- # source any enviroment file.
- curdir = os.path.abspath(os.path.dirname(__file__))
- pardir = os.path.split(curdir)[0]
- # This will allow to import stuff as plasduino.xxx
- sys.path.append(pardir)
- # And this will allow to run plasduino modules through __utils__.cmd()
- try:
- os.environ['PYTHONPATH'] = '%s:%s' % (pardir, os.environ['PYTHONPATH'])
- except KeyError:
- os.environ['PYTHONPATH'] = pardir
- # End of hack.
- import plasduino.__utils__ as __utils__
- from plasduino.__logging__ import logger, abort
- from plasduino.__plasduino__ import PLASDUINO_ROOT, PLASDUINO_DOC,\
- PLASDUINO_WEB_HTML, PLASDUINO_WEB, RELEASE_NOTES_PATH,\
- TAG_FILE_PATH, getTagInfo
- sys.path.append(PLASDUINO_WEB)
- import deploy
- PLASDUINO_DEBIAN = os.path.join(PLASDUINO_ROOT, 'debian')
- PLASDUINO_RPM = os.path.join(PLASDUINO_ROOT, 'rpm')
- BUILD_DIR = 'build'
- DIST_DIR = 'dist'
- DEBIAN_CHANGELOG_PATH = os.path.join(PLASDUINO_DEBIAN, 'changelog')
- RPM_SPEC_TEMPLATE_PATH = os.path.join(PLASDUINO_RPM, 'plasduino_template.spec')
- """ The build date should be compliant to the debian standards, as documented at
- http://www.debian.org/doc/debian-policy/ch-source.html
- """
- BUILD_DATE = time.strftime('%a, %d %b %Y %H:%M:%S %z')
- DEBIAN_PACKAGER = 'Jacopo Nespolo <j.nespolo@gmail.com>'
- TAG_MODES = ['major', 'minor', 'patch']
- def updateVersionFile(mode, dryRun = False):
- """ Update the __tag__.py module with the new tag and build date.
- """
- prevTag, prevBuildDate = getTagInfo()
- logger.info('Previous tag was %s...' % prevTag)
- version, release, patch = [int(item) for item in prevTag.split('.')]
- if mode == 'major':
- version += 1
- release = 0
- patch = 0
- elif mode == 'minor':
- release += 1
- patch = 0
- elif mode == 'patch':
- patch += 1
- else:
- abort('Unknown release mode %s.' % mode)
- nextTag = '%s.%s.%s' % (version, release, patch)
- logger.info('Writing new tag (%s) to %s...' % (nextTag, TAG_FILE_PATH))
- if not dryRun:
- outputFile = open(TAG_FILE_PATH, 'w')
- outputFile.writelines('TAG = \'%s\'\n' % nextTag)
- outputFile.writelines('BUILD_DATE = \'%s\'\n' % BUILD_DATE)
- outputFile.close()
- logger.info('Done.')
- return nextTag
- def updateReleaseNotes(tag, dryRun = False):
- """ Write the new tag and build date on top of the release notes
- (which must be kept up to date during the release process).
- """
- hline = '-'*79
- logger.info('Reading in %s...' % RELEASE_NOTES_PATH)
- notes = open(RELEASE_NOTES_PATH).read().strip('\n')
- logger.info('Writing out %s...' % RELEASE_NOTES_PATH)
- if not dryRun:
- outputFile = open(RELEASE_NOTES_PATH, 'w')
- outputFile.writelines('\n%s\nplasduino (%s) - %s\n%s\n' %\
- (hline, tag, BUILD_DATE, hline))
- outputFile.writelines(notes)
- outputFile.close()
- logger.info('Done.')
- def debianizeReleaseNotes(dryRun = False):
- """ Process the release notes file and produce a new one conforming to the
- debian rules.
- """
- logger.info('Processing %s and writing out %s...' %\
- (RELEASE_NOTES_PATH, DEBIAN_CHANGELOG_PATH))
- inputFile = open(RELEASE_NOTES_PATH)
- outputFile = open(DEBIAN_CHANGELOG_PATH, 'w')
- for line in inputFile:
- if line.startswith('-----'):
- pass
- elif line.startswith('plasduino'):
- tag, date = line.split(' - ')
- notes = []
- line = inputFile.next()
- assert line.startswith('-----')
- line = inputFile.next()
- while len(line.strip('\n').strip()):
- notes.append(line)
- try:
- line = inputFile.next()
- except StopIteration:
- break
- outputFile.writelines('%s UNRELEASED; urgency=low\n\n' % tag)
- for note in notes:
- outputFile.writelines(note)
- outputFile.writelines('\n -- %s %s\n' % (DEBIAN_PACKAGER, date))
- outputFile.close()
- inputFile.close()
- logger.info('Done.')
- def tagPackage(mode, dryRun = False):
- """ Tag the package.
- This means:
- (*) hg pull/update to make sure we're not missing remote modification;
- (*) figure out the target tag, update the release.notes and create the
- debian version;
- (*) commit the modifications, tag and push.
- (*) regeister the new tag on PyPi.
- """
- __utils__.cmd('hg pull', verbose = True, dryRun = dryRun)
- __utils__.cmd('hg update', verbose = True, dryRun = dryRun)
- __utils__.cmd('hg status', verbose = True, dryRun = dryRun)
- tag = updateVersionFile(mode, dryRun)
- updateReleaseNotes(tag, dryRun)
- debianizeReleaseNotes(dryRun)
- msg = 'Prepare for tag %s.' % tag
- __utils__.cmd('hg commit -m "%s"' % msg, verbose = True, dryRun = dryRun)
- __utils__.cmd('hg push', verbose = True, dryRun = dryRun)
- __utils__.cmd('hg tag %s' % tag, verbose = True, dryRun = dryRun)
- __utils__.cmd('hg push', verbose = True, dryRun = dryRun)
- __utils__.cmd('hg status', verbose = True, dryRun = dryRun)
- __utils__.cmd('python setup.py register', verbose = True, dryRun = dryRun)
- def distsrc():
- """ Create a plain source distribution.
- """
- tag, buildDate = getTagInfo()
- logger.info('Creating plain source distribution...')
- distDir = os.path.join(DIST_DIR, 'src')
- srcLogFilePath = 'src.log'
- # Create the distribution.
- __utils__.cmd('python setup.py sdist --dist-dir=%s --prune' % distDir,
- verbose = False, logFilePath = srcLogFilePath)
- # Cleanup.
- __utils__.rm(srcLogFilePath)
- logger.info('Done.')
- def distdeb(cleanup=True):
- """ Create the deb distribution.
- """
- debianizeReleaseNotes()
- tag, buildDate = getTagInfo()
- logger.info('Creating debian package...')
- prog = 'debuild -us -uc'
- if not __utils__.programInstalled(prog)[0]:
- logger.warn('%s is not installed, skipping deb dist...' % prog)
- return
- distDir = os.path.join(DIST_DIR, 'deb')
- srcLogFilePath = 'src_deb.log'
- binLogFilePath = 'bin_deb.log'
- # Create a source distribution with the proper files (i.e.,
- # use setup_deb.py).
- __utils__.cmd('python setup_deb.py sdist --dist-dir=%s --prune' % distDir,
- verbose = False, logFilePath = srcLogFilePath)
- # Unzip the archive.
- archFilePath = os.path.join(distDir, 'plasduino-%s.tar.gz' % tag)
- __utils__.cmd('tar xzf %s --directory %s' % (archFilePath, distDir),
- verbose = False)
- srcFolderPath = os.path.join(distDir, 'plasduino-%s' % tag)
- # Create the package.
- __utils__.cmd('cd %s; %s' %(srcFolderPath, prog),
- verbose = False, logFilePath = binLogFilePath)
- # Cleanup.
- if cleanup:
- __utils__.rm(archFilePath)
- __utils__.rmdir(srcFolderPath)
- __utils__.rm(srcLogFilePath)
- __utils__.rm(binLogFilePath)
- logger.info('Done.')
- def createSpecFile(tag, dest, dryRun = False):
- """ Create the .spec file for the rpm generation.
- """
- logger.info('Creating the .spec file for the rpm generation...')
- cmd = 'sed s/VERSION/%s/ <%s >%s' % (tag, RPM_SPEC_TEMPLATE_PATH, dest)
- __utils__.cmd(cmd, verbose = False, dryRun = dryRun)
- logger.info('Done.')
- def distrpm():
- """ Create the deb distribution.
- """
- tag, buildDate = getTagInfo()
- logger.info('Creating rpm package...')
- prog = 'rpmbuild'
- if not __utils__.programInstalled(prog)[0]:
- logger.warn('rpm-build is not installed, skipping rpm dist...')
- return
- # Create the rpm directory structure.
- buildDir = os.path.join(BUILD_DIR, 'rpm')
- distDir = os.path.join(DIST_DIR, 'rpm')
- srcLogFilePath = 'src_rpm.log'
- binLogFilePath = 'bin_rpm.log'
- if os.path.exists(buildDir):
- __utils__.cleanup(buildDir)
- else:
- __utils__.createFolder(buildDir)
- if not os.path.exists(distDir):
- __utils__.createFolder(distDir)
- for folderName in ['SOURCES', 'SPECS', 'BUILD', 'RPMS', 'SRPMS']:
- folderPath = os.path.join(buildDir, folderName)
- __utils__.createFolder(folderPath)
- archDir = os.path.join(buildDir, 'SOURCES')
- specDir = os.path.join(buildDir, 'SPECS')
- # Create the .spec file.
- specFilePath = os.path.join(specDir, 'plasduino.spec')
- createSpecFile(tag, specFilePath)
- # Create a source distribution with the proper files (i.e.,
- # use setup_rpm.py).
- __utils__.cmd('python setup_rpm.py sdist --dist-dir=%s --prune' % archDir,
- verbose = False, logFilePath = srcLogFilePath)
- archFilePath = os.path.join(archDir, 'plasduino-%s.tar.gz' % tag)
- # Create the actual rpm.
- cmd = 'rpmbuild --target=noarch -ba --define "_topdir %s" --clean %s' %\
- (os.path.abspath(buildDir), specFilePath)
- __utils__.cmd(cmd, verbose = False, logFilePath = binLogFilePath)
- # Copy the rpms in the dist folder.
- source = os.path.join(buildDir, 'SRPMS', 'plasduino-%s-fc.src.rpm' % tag)
- __utils__.cp(source, distDir)
- source = os.path.join(buildDir, 'RPMS', 'noarch',
- 'plasduino-%s-fc.noarch.rpm' % tag)
- __utils__.cp(source, distDir)
- # Cleanup.
- __utils__.rm(srcLogFilePath)
- __utils__.rm(binLogFilePath)
- logger.info('Done.')
- def dumpModuleInfo():
- """ Run the code dumping the module info, faking the post-installation
- script.
-
- This is just to make sure that the post-installation script won't
- crash at install time.
- """
- logger.info('Dumping module info (making sure the post-installation '
- 'script won\'t crash).')
- from plasduino.modulemanager.__modulemanager__ import dumpModuleInfo
- from plasduino.__plasduino__ import PLASDUINO_DIST_MODULEINFO_FILE_PATH,\
- PLASDUINO_MODULES
- dumpModuleInfo([PLASDUINO_MODULES], PLASDUINO_DIST_MODULEINFO_FILE_PATH)
- def compileWebPage():
- """ Compile the documentation to be put on the web.
- """
- tag, buildDate = getTagInfo()
- logger.info('Generating the web page...')
- deploy.deploy()
- logger.info('Creating zipped archive for the html documentation...')
- import zipfile
- distDir = os.path.join(DIST_DIR, 'doc')
- __utils__.createFolder(distDir)
- dest = os.path.join(distDir, 'plasduino-%s-doc.zip' % tag)
- logger.info('Opening output file %s...' % dest)
- archive = zipfile.ZipFile(dest, 'w')
- for base, dirs, files in os.walk(PLASDUINO_WEB_HTML):
- for fileName in files:
- filePath = os.path.join(base, fileName)
- archPath = filePath[len(PLASDUINO_WEB_HTML) + 1:]
- logger.info('Adding %s...' % filePath)
- archive.write(filePath, archPath)
- archive.close()
- logger.info('Done.')
- if __name__ == '__main__':
- from optparse import OptionParser
- parser = OptionParser()
- parser.add_option('-t', dest = 'tagmode', type = str, default = None,
- help = 'The release tag mode %s.' % TAG_MODES)
- parser.add_option('-n', action = 'store_true', dest = 'dryrun',
- help = 'Dry run (i.e. do not actually do anything).')
- parser.add_option('-s', action = 'store_true', dest = 'src',
- help = 'Create a source distribution.')
- parser.add_option('-d', action = 'store_true', dest = 'deb',
- help = 'Create a debian package.')
- parser.add_option('-r', action = 'store_true', dest = 'rpm',
- help = 'Create a rpm package.')
- parser.add_option('--no-cleanup', action = 'store_false', dest = 'noclean',
- default = True,
- help = """Do NOT clean up source tarball and log files \
- created during build""")
- (opts, args) = parser.parse_args()
- if not opts.tagmode and not (opts.src or opts.deb or opts.rpm):
- parser.print_help()
- parser.error('Please specify at least one valid option.')
- dumpModuleInfo()
- tag = None
- if opts.tagmode is not None:
- if opts.tagmode not in TAG_MODES:
- parser.error('Invalid tag mode %s (allowed: %s)' %\
- (opts.tagmode, TAG_MODES))
- tagPackage(opts.tagmode, opts.dryrun)
- compileWebPage()
- if opts.src and not opts.dryrun:
- distsrc()
- if opts.deb and not opts.dryrun:
- distdeb(cleanup=opts.noclean)
- if opts.rpm and not opts.dryrun:
- distrpm()