diff --git a/tools/release/REAME.md b/tools/release/REAME.md new file mode 100644 index 00000000..30157813 --- /dev/null +++ b/tools/release/REAME.md @@ -0,0 +1,34 @@ +PyQtGraph Release Procedure +--------------------------- + +0. Create your release_config.py based on release_config.example.py + +1. Create a release-x.x.x branch + +2. Run build-release script + - creates clone of master from github + - merges release branch into master + - updates version numbers in code + - creates pyqtgraph-x.x.x tag + - creates release commit + - builds source dist + - builds windows dists + - builds deb dist + +3. test build files + - test setup.py, pip on OSX + - test 32/64 exe on windows + - deb on linux (py2, py3) + - source install on linux (py2, py3) + +4. Run upload-release script + - pip upload + - github push + release + - website upload + +5. publish + - update website + - mailing list announcement + - new conda recipe (http://conda.pydata.org/docs/build.html) + - contact deb maintainer + - other package maintainers? diff --git a/tools/release/build-pg-release.py b/tools/release/build-pg-release.py new file mode 100755 index 00000000..b9ae5bdd --- /dev/null +++ b/tools/release/build-pg-release.py @@ -0,0 +1,108 @@ +#!/usr/bin/python +from common import * + + +usage = """ +Usage: build_pg_release.py x.y.z + + * Will attempt to clone branch release-x.y.z from %s. + * Will attempt to contact windows host at %s (suggest running bitvise ssh server). +""" % (sourcedir, winhost) + + +if len(sys.argv) != 2: + print usage + sys.exit(-1) +version = sys.argv[1] +if re.match(r'\d+\.\d+.*', version) is None: + print 'Invalid version number "%s".' % version + sys.exit(-1) + + +# Clone source repository and tag the release branch +shell(''' + # Clone and merge release branch + cd {bld} + rm -rf pyqtgraph + git clone --depth 1 -b master http://github.com/pyqtgraph/pyqtgraph + cd {bld}/pyqtgraph + git checkout -b release-{ver} + git pull {src} release-{ver} + git checkout master + git merge --no-ff --no-commit release-{ver} + + # Write new version number into the source + sed -i "s/__version__ = .*/__version__ = '{ver}'/" pyqtgraph/__init__.py + #sed -i "s/ version=.*,/ version='{ver}',/" setup.py # now automated + sed -i "s/version = .*/version = '{ver}'/" doc/source/conf.py + sed -i "s/release = .*/release = '{ver}'/" doc/source/conf.py + + # make sure changelog mentions unreleased changes + grep "pyqtgraph-{ver}.*unreleased.*" CHANGELOG + sed -i "s/pyqtgraph-{ver}.*unreleased.*/pyqtgraph-{ver}/" CHANGELOG + + # Commit and tag new release + git commit -a -m "PyQtGraph release {ver}" + git tag pyqtgraph-{ver} + + # Build HTML documentation + cd doc + make clean + make html + cd .. + find ./ -name "*.pyc" -delete + + # package source distribution + python setup.py sdist + + # test pip install source distribution + rm -rf release-{ver}-virtenv + virtualenv --system-site-packages release-{ver}-virtenv + . release-{ver}-virtenv/bin/activate + echo "PATH: $PATH" + echo "ENV: $VIRTUAL_ENV" + pip install --no-index dist/pyqtgraph-{ver}.tar.gz + deactivate + + # build deb packages + #python setup.py --command-packages=stdeb.command bdist_deb + python setup.py --command-packages=stdeb.command sdist_dsc + cd deb_dist/pyqtgraph-{ver} + sed -i "s/^Depends:.*/Depends: python (>= 2.6), python-qt4 | python-pyside, python-numpy/" debian/control + dpkg-buildpackage + cd ../../ + mv deb_dist dist/pyqtgraph-{ver}-deb +'''.format(**vars)) + + +# build windows installers +if winhost is not None: + shell("# Build windows executables") + ssh(winhost, ''' + rmdir /s/q pyqtgraph-build + git clone {self}:{bld}/pyqtgraph pyqtgraph-build + cd pyqtgraph-build + python setup.py build --plat-name=win32 bdist_wininst + python setup.py build --plat-name=win-amd64 bdist_wininst + exit + '''.format(**vars)) + + shell(''' + scp {win}:pyqtgraph-build/dist/*.exe {bld}/pyqtgraph/dist/ + '''.format(**vars)) + + +print """ + +======== Build complete. ========= + +* Dist files in {bld}/pyqtgraph/dist +""".format(**vars) + + +if winhost is not None: + print """ * Dist files on windows host at {win}:pyqtgraph-build/dist + """.format(**vars) + + + diff --git a/tools/release/common.py b/tools/release/common.py new file mode 100644 index 00000000..75b5d775 --- /dev/null +++ b/tools/release/common.py @@ -0,0 +1,47 @@ +import os, sys, getopt, re +import subprocess as sp + +try: + from release_config import * +except ImportError: + print "Error: could not import release_config! See the README..\n\n" + raise + + +vars = dict(ver=version, src=sourcedir, bld=builddir, win=winhost, self=selfhost) + + +def shell(cmd): + """Run each line of a shell script; raise an exception if any line returns + a nonzero value. + """ + pin, pout = os.pipe() + proc = sp.Popen('/bin/sh', stdin=sp.PIPE) + for line in cmd.split('\n'): + line = line.strip() + if line.startswith('#'): + print '\033[33m> ' + line + '\033[0m' + else: + print '\033[32m> ' + line + '\033[0m' + if line.startswith('cd '): + os.chdir(line[3:]) + proc.stdin.write(line + '\n') + proc.stdin.write('echo $? 1>&%d\n' % pout) + ret = "" + while not ret.endswith('\n'): + ret += os.read(pin, 1) + ret = int(ret.strip()) + if ret != 0: + print "Error, bailing out." + sys.exit(-1) + proc.stdin.close() + proc.wait() + + +def ssh(host, cmd): + """Run commands on a remote host by ssh. + """ + proc = sp.Popen(['ssh', host], stdin=sp.PIPE) + proc.stdin.write(cmd) + proc.wait() + diff --git a/tools/release/release_config.example.py b/tools/release/release_config.example.py new file mode 100644 index 00000000..35d569e4 --- /dev/null +++ b/tools/release/release_config.example.py @@ -0,0 +1,18 @@ +""" +Example configuration file required by build-pg-release and upload-pg-release. + +Copy this file to release_config.py and edit. +""" + +# Where to find the repository from which the release files will be built. +# This repository must have a branch called release-x.y.z +sourcedir = '/home/user/pyqtgraph' + +# Where to generate build files--source packages, deb packages, .exe installers, etc. +builddir = '/home/user/pyqtgraph-build' + +# Where to archive build files (optional) +archivedir = builddir + '/archive' + +# A windows machine (typically a VM) running an SSH server for automatically building .exe installers +winhost = 'luke@192.168.56.101' diff --git a/tools/setVersion.py b/tools/release/setVersion.py similarity index 100% rename from tools/setVersion.py rename to tools/release/setVersion.py diff --git a/tools/release/upload-pg-release.py b/tools/release/upload-pg-release.py new file mode 100755 index 00000000..7a538c87 --- /dev/null +++ b/tools/release/upload-pg-release.py @@ -0,0 +1,76 @@ +#!/usr/bin/python + +from release_config import * +import os + +usage = """ +upload-pg-release.py x.y.z + + * Uploads source dist to pypi + * Uploads packages & docs to website + * Pushes new master branch to github + +""" % (source, winhost) + + + +pypi_err = """ +Missing ~/.pypirc file. Should look like: +----------------------------------------- + +[distutils] +index-servers = + pypi + +[pypi] +username:your_username +password:your_password + +""" + +if not os.path.isfile(os.path.expanduser('~/.pypirc')): + print pypi_err + sys.exit(-1) + +### Upload everything to server +shell(""" + # Uploading documentation.. + cd pyqtgraph + rsync -rv doc/build/* slice:/www/code/pyqtgraph/pyqtgraph/documentation/build/ + + # Uploading source dist to website + rsync -v dist/pyqtgraph-{ver}.tar.gz slice:/www/code/pyqtgraph/downloads/ + cp dist/pyqtgraph-{ver}.tar.gz ../archive + + # Upload deb to website + rsync -v dist/pyqtgraph-{ver}-deb/python-pyqtgraph_{ver}-1_all.deb slice:/www/code/pyqtgraph/downloads/ + + # Update APT repository.. + ssh slice "cd /www/debian; ln -sf /www/code/pyqtgraph/downloads/*.deb dev/; dpkg-scanpackages dev /dev/null | gzip -9c > dev/Packages.gz" + cp -a dist/pyqtgraph-{ver}-deb ../archive/ + + # Uploading windows executables.. + rsync -v dist/*.exe slice:/www/code/pyqtgraph/downloads/ + cp dist/*.exe ../archive/ + + # Push to github + git push --tags https://github.com/pyqtgraph/pyqtgraph master:master + + # Upload to pypi.. + python setup.py sdist upload + + +""".format(**vars)) + +print """ + +======== Upload complete. ========= + +Next steps to publish: + - update website + - mailing list announcement + - new conda recipe (http://conda.pydata.org/docs/build.html) + - contact deb maintainer (gianfranco costamagna) + - other package maintainers? + +""".format(**vars)