Skip to main content

Software releasing made easy and repeatable

Project description

Package releasing made easy

zest.releaser is collection of command-line programs to help you automate the task of releasing a software project. It’s particularly helpful with Python package projects, but it can also be used for non-Python projects. For example, it’s used to tag buildouts - a project only needs a version.txt file to be used with zest.releaser.

It will help you to automate:

  • Updating the version number. The version number can either be in or version.txt. For example, 0.3 dev (current) to 0.3 (release) to 0.4 dev (new development version).
  • Updating the history/changes file. It logs the release date on release and adds a new section for the upcoming changes (new development version).
  • Tagging the release. It creates a tag in your version control system named after the released version number.
  • Uploading a source release to PyPI. It will only do this if the package is already registered there, the Zest Releaser is careful not to publish your private projects! It can also check out the tag in a temporary directory in case you need to modify it.


Getting a good installation consists of two steps: getting the zest.releaser commands, and setting up your environment so you can upload releases to pypi (if you want that).

Get the zest.releaser commands

Just a simple easy_install zest.releaser is enough.

Alternatively, buildout users can install zest.releaser as part of a specific project’s buildout, by having a buildout configuration such as:

parts = releaser

recipe = zc.recipe.egg
eggs = zest.releaser

Prepare for pypi distribution

Of course you must have a version control system installed. zest.releaser currently supports:

  • Subversion (svn)
  • Mercurial (hg)
  • Git (git)
  • Bazaar (bzr)

Others could be added if there are volunteers.

When the (full)release command tries to upload your package to a pypi server, zest.releaser basically just executes the command python register sdist upload. The python here is the same python that was used to install zest.releaser. If that command would fail when you try it manually (for example because you have not configured a .pypirc file yet), then zest.releaser does not magically make it work. This means that you may need to have some extra python packages installed:

  • setuptools or distribute (when using subversion 1.5 or higher you need setuptools 0.6c11 or higher or any distribute version)
  • setuptools-git (Setuptools plugin for finding files under Git version control)
  • setuptools_hg (Setuptools plugin for finding files under Mercurial version control)
  • setuptools_bzr (Setuptools plugin for finding files under Bazaar version control)
  • collective.dist (when using python2.4, depending on your ~/.pypirc file)

The setuptools plugins are mostly so you do not miss files in the generated sdist that is uploaded to pypi.

For more info, see the section on Uploading to pypi server(s).


Zest.releaser gives you four commands to help in releasing python packages. They must be run in a version controlled checkout. The commands are:

  • prerelease: asks you for a version number (defaults to the current version minus a ‘dev’ or so), updates the or version.txt and the HISTORY.txt/CHANGES.txt/CHANGES with this new version number and offers to commit those changes to subversion (or bzr or hg or git)
  • release: copies the the trunk or branch of the current checkout and creates a version control tag of it. Makes a checkout of the tag in a temporary directory. Offers to register and upload a source dist of this package to PyPI (Python Package Index). Note: if the package has not been registered yet, it will not do that for you. You must register the package manually (python register) so this remains a conscious decision. The main reason is that you want to avoid having to say: “Oops, I uploaded our client code to the internet; and this is the initial version with the plaintext root passwords.”
  • postrelease: asks you for a version number (gives a sane default), adds a development marker to it, updates the or version.txt and the HISTORY.txt with this and offers to commit those changes to version control.
  • fullrelease: all of the above in order.

There are two additional tools:

  • longtest: small tool that renders a’s long description and opens it in a web browser. This assumes an installed docutils (as it needs
  • lasttagdiff: small tool that shows the diff of the currently committed trunk with the last released tag. Handy for checking whether all the changes are adequately described in the HISTORY.txt/CHANGES.txt.


Current assumptions

Zest.releaser originated at Zest software so there are some assumptions build-in that might or might not fit you. Lots of people are using it in various companies and open source projects, so it’ll probably fit :-)

  • If you are using svn, your svn is structured with /trunk, /tags (or /tag) and optionally /branches (or /branch). Both a /trunk or a /branches/something checkout is ok.

  • There’s a version.txt or in your project. The version.txt has a single line with the version number (newline optional). The should have a single version = '0.3' line somewhere. You can also have it in the actual setup() call, on its own line still, as `` version = ‘0.3’,``. Indentation and the comma are preserved. If you need something special, you can always do a version=version and put the actual version statement in a zest.releaser-friendly format near the top of the file. Reading (in Plone products) a version.txt into works great, too.

  • The history file (either HISTORY.txt, CHANGES.txt or CHANGES) restriction is probably the most severe at the moment. zest.releaser searches for a restructuredtext header with parenthesis. So something like:

    Changelog for xyz
    0.3 (unreleased)
    - Did something
    0.2 (1972-12-25)
    - Reinout was born.

    That’s just the style we started with. Pretty clear and useful. It also supports the current zopeskel style with 0.3 - unreleased.

  • If using Python 2.4 you don’t want to have tar.gz eggs due to an obscure bug on python

Development notes, bug tracker

The svn source can be found at . If you have access to the collective, you can fix bugs right away. Bigger changes on a branch please and mail and about it :-)

If you are going to do a fix or want to run the tests, please see the DEVELOPERS.txt file in the root of the package.

Bugs can be added to .

Note that there are alternative release scripts available, for instance which installs itself as a setuptools command (“python release”), so it “only” works with setuptools projects.

Uploading to pypi server(s)

Like noted earlier, for safety reasons zest.releaser will only offer to upload your package to when the package is already registered there. If this is not the case yet, you can go to the directory where zest.releaser put the checkout (or make a fresh checkout yourself. Then with the python version of your choice do:

python register sdist upload

For this to work you will need a .pypirc file in your home directory that has your pypi login credentials like this:


Since python 2.6, or in earlier python versions with collective.dist, you can specify multiple indexes for uploading your package in .pypirc:

index-servers =


# You may need to specify the realm, which is the domain the
# server sends back when you do a challenge:

See for more info.

When all this is configured correctly, zest.releaser will first reregister and upload at the official pypi (if the package is registered there already). Then it will offer to upload to the other index servers that you have specified in .pypirc.

Note that since version 3.15, zest.releaser also looks for this information in the setup.cfg if your package has that file. One way to use this, is to restrict the servers that zest.releaser will ask you upload to. If you have defined 40 index-servers in your pypirc but you have the following in your setup.cfg, you will not be asked to upload to any server:

index-servers =

Note that after creating the tag we still ask you if you want to checkout that tag for tweaks or pypi/distutils server upload. We could add some extra checks to see if that is really needed, but someone who does not have index-servers listed, may still want to use an entry point like gocept.zestreleaser.customupload to do uploading, or do some manual steps first before uploading.

Some people will hardly ever want to do a release on pypi but only want to create a tag in 99 out of 100 cases. They won’t like the default answer of ‘yes’ to that question of whether to create a checkout of the tag. So since version 3.16 you can influence this default answer. You can add some lines to the .pypirc file in your home directory to change the default answer for all packages, or change it for individual packages in their setup.cfg file. The lines are this:

release = no

You can use no/false/off/0 or yes/true/on/1 as answers; upper, lower or mixed case are all fine.

Entrypoints documentation

Warning: entry points were added in 3.0, I’m reserving the right to make backwards-incompatible changes to the entry point mechanism in the next couple of releases. It is a major new piece of functionality for zest.releaser and getting all the details right at the first attempt isn’t guaranteed.

A zest.releaser entrypoint gets passed a data dictionary and that’s about it. You can do tasks like generating documentation. Or downloading external files you don’t want to store in your repository but that you do want to have included in your egg.

Every release step (prerelease, release and postrelease) has three points where you can hook in an entry point:

Only the workingdir and name are available in the data dictionary, nothing has happened yet.
All data dictionary items are available and some questions (like new version number) have been asked. No filesystem changes have been made yet.
The action has happened, everything has been written to disk or uploaded to pypi or whatever.

For the release step it made sense to create one extra entry point:

The middle entry point has been handled, the tag has been made, a checkout of that tag has been made and we are now in that checkout directory. Of course, when the user chooses not to do a checkout, this entry point never triggers.

Note that an entry point can be specific for one package (usually the package that you are now releasing) or generic for all packages. An example of a generic one is gocept.zestreleaser.customupload, which offers to upload the generated distribution to a chosen destination (like a server for internal company use). If your entry point is specific for the current package only, you should add an extra check to make sure it is not run while releasing other packages; something like this should do the trick:

def my_entry_point(data):
    if data['name'] != 'my.package':

Entry point specification

An entry point is configured like this in your

    #'console_scripts': [
    #    'myscript = my.package.scripts:main'],
    'zest.releaser.prereleaser.middle': [
        'dosomething = my.package.some:some_entrypoint',

Replace prereleaser and middle in zest.releaser.prereleaser.middle with prerelease/release/postrelease and before/middle/after where needed.

See the of zest.releaser itself for some real world examples.

You’ll have to make sure that the zest.releaser scripts know about your entry points, for instance by placing your egg (with entry point) in the same zc.recipe.egg section in your buildout as where you placed zest.releaser. Or, if you installed zest.releaser globally, your egg-with-entrypoint has to be globally installed, too.

Comments about data dict items

Your entry point gets a data dictionary: the items you get in that dictionary are documented below. Some comments about them:

  • Not all items are available. If no history/changelog file is found, there won’t be any data['history_lines'] either.
  • Items that are templates are normal python string templates. They use dictionary replacement: they’re actually passed the same data dict. For instance, prerelease’s data['commit_message'] is by default Preparing release %(new_version)s. A “middle” entry point could modify this template to get a different commit message.

Prerelease data dict items

Message template used when committing
Filename of history/changelog file (when found)
Header template used for 1st history header
List with all history file lines (when found)
Name of the project being released
New version (so 1.0 instead of 1.0dev)
Version before prereleasing (e.g. 1.0dev)
Date string used in history header
Original working directory

Release data dict items

Name of the project being released
Internal detail, don’t touch this :-)
Directory where the tag checkout is placed (if a tag checkout has been made)
Version we’re releasing
Original working directory

Postrelease data dict items

Message template used when committing
New development version with dev marker (so 1.1dev)
Template for dev version number
Header template used for 1st history header
Name of the project being released
New development version (so 1.1)
First line in new changelog section
Original working directory

To do


  • Reinout van Rees (The Health Agency) is the originator and main author.
  • Maurits van Rees (Zest Software) added a heapload of improvements.
  • Kevin Teague (Canada’s Michael Smith Genome Sciences Center) added support for multiple version control systems, most notable Mercurial.
  • Wouter vanden Hove (University of Gent) added support for uploading to multiple servers, using collective.dist.
  • Godefroid Chapelle (BubbleNet) added /tag besides /tags for subversion.

Changelog for zest.releaser

3.18 (2010-12-08)

3.17 (2010-11-17)

  • When the package that is being released neither has a nor a setup.cfg, use No as default answer for creating a checkout of the tag. [maurits]

3.16 (2010-11-15)

  • For (pypi) output, also show the first few lines instead of only the last few. [maurits]
  • See if pypirc or setup.cfg has a [zest.releaser] section with option release = yes/no. During the release stage, this influences the default answer when asked if you want to make a checkout of the tag. The default when not set, is ‘yes’. You may want to set this to ‘no’ if most of the time you only make releaser of internal packages for customers and only need the tag. [maurits]
  • Specify bazaar (bzr) tag numbers using the ‘tag’ revision specifier (like ‘tag:0.1’) instead of only the tag number (0.1) to add compatibility with earlier bzr versions (tested with 2.0.2). [maurits]

3.15 (2010-09-10)

  • Read pypi config not only from the .pypirc file, but also from the setup.cfg file of the package. Patch by Erico Andrei. [maurits]

3.14 (2010-08-26)

  • experimental support for git svn tagging, fully test-covered [chaoflow]
  • fail if no tag was created, not test-covered [chaoflow]
  • svn available_tags method: intercept ‘Repository moved’ note in svn info and stop processing then. [maurits]

3.13 (2010-08-16)

  • Fixed check that tested whether a package was already available on pypi, as the pypi implementation changed slightly. We now just check for a 404 status. Patch by Wolfgang Schnerring. [maurits]

3.12 (2010-07-22)

  • Added extra entry point for the release step: after_checkout. When this is run, the middle entry point has been handled, the tag has been made, a checkout of that tag has been made and we are now in that checkout directory. Idea: Jan-Wijbrand Kolman. [maurits]
  • Fix: in the zest.releaser.releaser.after entry point data, pass the ‘tagdir’ value (if a checkout has been made). Patch by Wolfgang Schnerring, thanks! [maurits]
  • Fixed tests to also pass with slightly newer git. [maurits]

3.11 (2010-06-25)

  • Small tweak: allowing zc.rst2’s “rst2 html” in addition to docutils’ own “rst2html”.

3.10 (2010-06-15)

  • Fix : when running ‘release’ with python2.6 against a private egg server, the distutils ‘register’ command would run against PyPI while ‘upload’ command would run against private server. (-r option needs to be stated twice) [gotcha]

3.9 (2010-06-11)

  • Again at the end of a fullrelease report the location of the directory containing the checkout of the tag, if it has been made. [maurits]

3.8 (2010-05-28)

  • Also allowing CHANGES in addition to HISTORY.txt and CHANGES.txt as a history filename. Keeps several Django packages happy.

3.6 (2010-04-13)

  • A version='1.0', string inside the setup() call no longer has non-pep8 spaces around the =. Fixes [reinout]
  • Got rid of ugly hack with UltraMagicString that was meant to avoid encoding errors when registering this package at pypi but which was not working for python2.4 (at least with collective.dist). Only ascii is allowed in the long_description if you want to avoid problems at one point or another. [maurits]

3.5 (2010-02-26)

  • Treat CHANGES.txt and HISTORY.txt the same: the first that is found in a directory is chosen for changing, instead of first looking everywhere for a HISTORY.txt and then for a CHANGES.txt. [maurits]

3.4 (2010-02-02)

  • Always build zip files if using python2.4 [do3cc]
  • bugfix: added ‘spreaded_internal’ property to BaseVersionControl objects, so filefind() does not exclude a directory just because there is no ‘.git’ folder in it. It still excludes directory where there is no ‘.svn’ folder in SVN repositories. [vincent]

3.3 (2009-12-29)

  • Fixed test failures when run on a computer with a new style pypi config. We now always use an old style config when running the tests. [maurits]
  • Fixed the release command for hg 1.1 (e.g. Ubuntu 9.04). [maurits]

3.2 (2009-12-22)

  • Replaced commands.getoutput() with a system() function grabbed from buildout on suggestion by Adam Groszer. Goal: make zest.releaser work also on windows.
  • Improved entry point documentation.
  • Added launchpad bugtracker at (and pointing at that in the documentation).

3.1 (2009-11-27)

  • Added documentation for entry points. [reinout]

3.0 (2009-11-27)

  • Added support for extension by means of entry points. There is no documentation that advertises it yet as I want to treat it as experimental till I’ve used it a few times. [reinout]

2.12 (2009-11-26)

  • Fixed mercurial sdist creation. [reinout]
  • A missing history file does not result anymore in a keyerror in prerelease. [reinout]
  • Added lots of test output normalization so that errors aren’t hidden by the large number of ... in the doctests. [reinout]

2.11 (2009-11-25)

  • Added /tag besides /tags for subversion [gotcha]
  • Fixed tests failures. [gotcha]

2.10 (2009-10-22)

  • Added support for git. [reinout]
  • Lots of internal refactoring and small fixes. [reinout]
  • Started tests. zest.releaser went from 0 to 94% coverage. [reinout]

2.9.3 (2009-09-22)

  • Uploading to multiple package indexes should now work in python2.6 (though ironically it now does not work for me on python2.4, but that has nothing to do with zest.releaser.) Added documentation for this. [maurits]
  • Make sure the next version suggestion for 1.0rc6 is 1.0rc7. [maurits]
  • In subversion, first try to get the package from the before falling back to the svn info, just like for mercurial. This fixes the problem that e.g. Products.feedfeeder was not recognized as being on pypi as the svn directory name was feedfeeder. [maurits]

2.9.2 (2009-09-17)

  • Umlauts in a changelog don’t break the logger anymore when using python2.6.2 when the umlauts turn up in the diff. This is due to a 2.6.2 regression bug, see Should be fixed in 2.6.3 when it comes out. [reinout]
  • (Release 2.9 and 2.9.1 are unreleased because of a setuptools bug with, sigh, non-ascii characters which made a dirty hack necessary). [reinout]

2.8 (2009-08-27)

  • Fixed the release command when used in a french environment. In French “svn info” returns ‘URL :’, not ‘URL:’. [vincentfretin]

2.7 (2009-07-08)

  • Before asking for its version or name, first run egg_info, as that may get rid of some warnings that otherwise end up in the extracted version or name, like UserWarnings. [maurits]

2.6 (2009-05-25)

  • Small change: the questions don’t print a newline anymore after the question (and before the user pressed enter). This makes it clearer if enter has been pressed yet. Suggestion by jkolman. [reinout]

2.5 (2009-05-20)

  • Revert to previous behaviour: when a package has not been released yet on pypi, decline to register it: the first time should be deliberate and manual, to avoid accidentally uploading client packages to pypi. [maurits]

2.4 (2009-05-15)

  • Factored out into a new, solving a few possible problems with missing or misconfigured .pypirc files. [maurits]

2.3 (2009-05-11)

  • Fixed release script when the .pypirc file does not contain a distutils section or that section does not contain a index-servers option. [maurits]

2.2 (2009-05-11)

2.1 (2009-04-09)

  • Fix lasttagdiff command to work with Mercurial by truncating the ‘+’ character from the revision id, since that only indicates uncommitted changes exist.
  • Make sure we find package/name/HISTORY.txt before we find docs/HISTORY.txt. [maurits]
  • Fixed checking for self.internal_filename: we would incorrectly check (‘.’, ‘s’, ‘v’, ‘n’) instead of ‘.svn’. [maurits]

2.0 (2009-04-01)

  • Added tag_url method to get lasttagdiff (and zest.stabilizer) working again. [maurits]
  • Merged kteague-multi-vcs branch with, woohoo, mercurial support! [reinout]
  • Mercurial support by Kevin Teague. [kteague]
  • postrelease put a space in the new version number in (between version number and dev). Removed this space as it is not necessary (in best case). [icemac]

1.13 (2009-03-17)

  • Also looking for CHANGES in addition to HISTORY.txt and CHANGES.txt as some packages use that convention. [reinout]
  • Added lasttagdiff command that shows the diff between the last release and the currently committed trunk. Handy for checking whether the changelog is up to date. [reinout]

1.12 (2009-03-17)

  • When doing a fullrelease and if the release step made a checkout of the tag into an temp directory, that temp directory is again printed after fullrelease finishes. Otherwise you’ve got to do a lot of scrolling. [reinout]

1.11 (2009-03-04)

  • When the found history file contains no version headings, look for a second history file: more than once I have the standard docs/HISTORY.txt that paster creates and I just add a pointer there to the real package/name/HISTORY.txt. [maurits]

1.10 (2009-02-25)

  • A `` version = ‘1.0’,`` in is now also rewritten correctly. Previously just a version = '1.0' would be injected, so without indentation and comma. [reinout]
  • Ask before checking out the tag. Sometimes the checkout is huge and you know you don’t want it. You don’t get asked for a pypi upload, though if you don’t check out the tag. [reinout]

1.9 (2009-02-24)

  • ‘release’ now also makes a tag checkout in a temporary directory. [Reinout]
  • Made ‘longtest’ work on Linux as there the command is ‘rst2html’ and apparently on the Mac it is ‘’. [maurits]

1.8 (2009-02-23)

  • Added ‘longtest’ command that renders a’s long description and opens it in a web browser. [reinout]

1.7 (2009-02-16)

  • Supporting alternative history version header format: ‘version - date’. [reinout]

1.6 (2009-02-14)

  • Patch by Michael Howitz: sys.executable is used instead of a string that doesn’t work on every system. [reinout]

1.5 (2009-02-11)

  • Changed y/n into Y/n, so defaulting to ‘yes’. [reinout]
  • Improved the documentation. [reinout]
  • When a yes/no question is asked, do not treat ‘no’ as the default but explicitly ask for an input – it was too easy to press enter and wrongly expect ‘yes’ as default. [maurits]

1.4 (2008-10-23)

  • Fixed missing import of utils. [maurits]

1.3 (2008-10-23)

  • Moved stabilize script to zest.stabilizer so that zest.releaser is just for releasing individual packages. Nice, tidy, reusable. [reinout]
  • Allowing ‘-v’ option on all commands: it gives you debug-level logging. [reinout]

1.2 (2008-10-16)

  • We now prefer the version from over any version.txt file found. When getting or changing the version we get/change the version when it differs from the found version.txt version. [maurits]

1.1 (2008-10-15)

  • Cleaned out zest-specific stuff. Cleaned up ‘release’. [reinout]

1.0 (2008-10-15)

  • Stabilize looks up the most recent tag of our development packages and uses gp.svndevelop to allow svn checkouts as development eggs. [reinout]
  • Do not look for version.txt in directories that are not handled by subversion. Use case: Products.feedfeeder, which has a buildout.cfg and so can have a parts directory with lots of version.txt files… [maurits]

0.9 (2008-10-02)

  • release: offer to register and upload the egg to the cheese shop. After that you still have the option to upload to our own tgz server. [maurits]
  • postrelease: for the suggestion of a new version simply try add 1 to the last character in the version; the previous logic failed for example for ‘1.0b3’. [maurits]
  • prerelease: ask user to enter next version (give him a suggestion). Handy when you want to change ‘1.0b3 dev’ into ‘1.0’. [maurits]
  • Started ‘stabilize’. [reinout]

0.8 (2008-09-26)

  • fullrelease: change back to the original directory after each pre/actual/post release step. [maurits]
  • release: switch back to original directory when ready to fix ‘commit to tag’ error. [maurits]
  • prerelease: quit when no version is found. [maurits]
  • Reverted sleeping fix from 0.7 as it did not work. [maurits]

0.7 (2008-09-26)

  • fullrelease: hopefully fix a ‘commit on tag’ bug by sleeping three seconds before doing the post release. [maurits]

0.6 (2008-09-26)

  • Added fullrelease script that does a prerelease, actual release and post release in one go. [maurits]

0.5 (2008-09-26)

  • Factored part of prerelease.check_version() out into utils.cleanup_version(). We now use that while setting the version in the history during postrelease. [maurits]
  • Add newline at the end of the generated version.txt. [maurits]

0.4 (2008-09-26)

  • Made the logging userfriendly.

0.3 (2008-09-26)

  • Postrelease: Better injection of history. Various other robustness fixes.

0.2 (2008-09-26)

  • postrelease: added suggestion for new version (a plain enter is enough to accept it). [reinout]
  • prerelease: ask before changing version + solidified regex for heading detection. [reinout]
  • prerelease: detect non-development versions better and change them. [maurits]
  • prerelease: made the commit message read: ‘Preparing release xxx’. [maurits]
  • postrelease: made the new version something like ‘1.0 dev’. [maurits]
  • postrelease: we now add some lines to the history now. [maurits]
  • prerelease: try changing the version to a non-development version, stripping off something like ‘(…)’. [maurits]
  • release: Refactored so has the ‘main’ function required by [maurits]

0.1 (2008-09-24)

  • Got a basic version of the prerelease script working (version check, history file updating, committing). [reinout]
  • Started by copying the guidelines script. [reinout]

Project details

Release history Release notifications | RSS feed

This version


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

zest.releaser-3.18.tar.gz (71.0 kB view hashes)

Uploaded source

Supported by

AWS AWS Cloud computing Datadog Datadog Monitoring Facebook / Instagram Facebook / Instagram PSF Sponsor Fastly Fastly CDN Google Google Object Storage and Download Analytics Huawei Huawei PSF Sponsor Microsoft Microsoft PSF Sponsor NVIDIA NVIDIA PSF Sponsor Pingdom Pingdom Monitoring Salesforce Salesforce PSF Sponsor Sentry Sentry Error logging StatusPage StatusPage Status page