Skip to main content

Software releasing made easy and repeatable

Project description

Package releasing made easy: zest.releaser overview and installation

zest.releaser is collection of command-line programs to help you automate the task of releasing a Python project.

It does away with all the boring bits. This is what zest.releaser automates for you:

  • It updates the version number. The version number can either be in or version.txt or in a __versions__ attribute in a Python file or in setup.cfg. For example, it switches you from 0.3.dev0 (current development version) to 0.3 (release) to 0.4.dev0 (new development version).
  • It updates the history/changes file. It logs the release date on release and adds a new heading for the upcoming changes (new development version).
  • It tags the release. It creates a tag in your version control system named after the released version number.
  • It optionally uploads a source release to PyPI. It will only do this if the package is already registered there (else it will ask, defaulting to ‘no’); zest releaser is careful not to publish your private projects!

Most important URLs

First the three most important links:

And… we’re automatically being tested by Travis and Landscape: Code Health

Compatibility / Dependencies

zest.releaser works on Python 2.7. Python 3.5+ is supported. PyPy2 is supported.

To be sure: the packages that you release with zest.releaser may very well work on other Python versions: that totally depends on your package.

We depend on:

  • setuptools for the entrypoint hooks that we offer.
  • colorama for colorized output (some errors printed in red).
  • six for python2/python3 compatibility.
  • twine for secure uploading via https to pypi. Plain setuptools doesn’t support this.

Since version 4.0 there is a recommended extra that you can get by installing zest.releaser[recommended] instead of zest.releaser. It contains a few trusted add-ons that we feel are useful for the great majority of zest.releaser users:

  • wheel for creating a Python wheel that we upload to PyPI next to the standard source distribution. Wheels are the new Python package format. Create or edit setup.cfg in your project (or globally in your ~/.pypirc) and create a section [zest.releaser] with create-wheel = yes to create a wheel to upload to PyPI. See for deciding whether this is a good idea for your package. Briefly, if it is a pure Python 2 or pure Python 3 package: just do it. If it is a pure Python 2 and a pure Python 3 project, it is known as a “universal” wheel, because one wheel can be installed on all implementations and versions of Python. If you indicate this in setup.cfg with the section [bdist_wheel] having universal = 1, then we will automatically upload a wheel, unless create-wheel is explicitly set to false.
  • check-manifest checks your file for completeness, or tells you that you need such a file. It basically checks if all version controlled files are ending up the the distribution that we will upload. This may avoid ‘brown bag’ releases that are missing files.
  • pyroma checks if the package follows best practices of Python packaging. Mostly it performs checks on the file, like checking for Python version classifiers.
  • chardet, the universal character encoding detector. To do the right thing in case your readme or changelog is in a non-utf-8 character set.
  • readme to check your long description in the same way as pypi does. No more unformatted restructured text on your pypi page just because there was a small error somewhere. Handy.


Just a simple pip install zest.releaser or easy_install zest.releaser is enough. If you want the recommended extra utilities, do a pip install zest.releaser[recommended].

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

parts =

recipe = zc.recipe.egg
eggs = zest.releaser[recommended]

Version control systems: svn, hg, git, bzr

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

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

Others could be added if there are volunteers! Git and mercurial support have been contributed years ago when we were working with bzr and subversion, for instance.

Available commands

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 CHANGES/HISTORY/CHANGELOG file (with either .rst/.txt/.md/.markdown or no extension) 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 CHANGES/HISTORY/CHANGELOG file with this and offers to commit those changes to version control. Note that with git and hg, you’d also be asked to push your changes (since 3.27). Otherwise the release and tag only live in your local hg/git repository and not on the server.
  • fullrelease: all of the above in order.

Note: markdown files should use the “underline” style of headings, not the “atx” style where you prefix the headers with # signs.

There are some 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 current branch with the last released tag. Handy for checking whether all the changes are adequately described in the changes file.
  • lasttaglog: small tool that shows the log of the current branch since the last released tag. Handy for checking whether all the changes are adequately described in the changes file.
  • addchangelogentry: pass this a text on the command line and it will add this as an entry in the changelog. This is probably mostly useful when you are making the same change in a batch of packages. The same text is used as commit message. In the changelog, the text is indented and the first line is started with a dash. The command detects it if you use for example a star as first character of an entry.
  • bumpversion: do not release, only bump the version. A development marker is kept when it is there. With --feature we update the minor version. With option --breaking we update the major version.


  • Reinout van Rees (Nelen & Schuurmans) is the original author. He’s still maintaining it, together with Maurits.
  • Maurits van Rees (Zest Software) added a heapload of improvements and is the maintainer, together with Reinout.
  • 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.
  • Richard Mitchell (Isotoma) added Python 3 support.
  • Mateusz Legięcki added a dockerfile for much easier testing.

Changelog for zest.releaser

6.22.2 (2021-10-29)

6.22.1 (2020-09-22)

  • When replacing new version in __version__ = "1.0", keep the existing quote style. We always replaced double with single quotes, but now we keep them. [graingert]

6.22.0 (2020-09-03)

  • Fixed deadlock when communicating with git-lfs, or anything else that gives back lots of output. [mcdeck]
  • Fixed TypeError setting unicode in environment on Python 2.7 on Windows. [mcdeck]

6.21.1 (2020-08-07)

  • Fixed uploading to multiple servers if we do not want to upload to the first server. Fixes issue #357. [maurits]

6.21.0 (2020-07-01)

  • Added support for Twine environment variables. Especially, setting TWINE_REPOSITORY and TWINE_REPOSITORY_URL had no effect previously. Fixes issue #353. [mctwynne]

6.20.1 (2020-02-21)

  • Added Dockerfile for much easier testing. See the developer documentation. Previously, getting the tests to run reliably locally was hard, now it is easy. [Behoston]

6.20.0 (2020-01-07)

  • Zest.releaser now sets an environment variable ZESTRELEASER so that tools that we call on the command line can detect us. Don’t depend on the variable’s textual content, just on the variable’s name.

6.19.1 (2019-09-03)

  • Percent signs (%) don’t crash addchangelogentry anymore.

6.19.0 (2019-06-03)

  • Do not go to the root of the repo by default. When you were not in the root of a repo, zest.releaser already asked if you wanted to go there. The default answer has now changed from yes to no. This might help when releasing from monorepos. Issue #326. [maurits]

6.18.2 (2019-04-10)

  • Remove nothing_changed_yet line from history lines in unreleased section. [kleschenko]

6.18.1 (2019-04-04)

  • Document that we only support underline-style headings for markdown. Fixes issue 317. [reinout]
  • Using simply git ls-files to list files in a git repo instead of an older much longer command. (Suggestion by @mgedmin). [reinout]

6.18.0 (2019-04-03)

  • Calling twine in a more generic way to let it automatically do the right thing. This saves us a lot of code and lets twine do what it’s good at. [htgoebel,reinout]

6.17.2 (2019-03-25)

  • When bdist_wheel is in setup.cfg, release a wheel. No longer check if this should be a universal wheel or not. That is handled automatically when calling python bdist_wheel. You can still set [zest.releaser] create-wheel = no to prevent creating a wheel. Fixes issue 315. [maurits]

6.17.1 (2019-03-19)

  • Also accept 201 as valid statuscode when uploading using twine Fixes issue 318 [fRiSi]

6.17.0 (2019-02-20)

  • Refuse to edit history header when it looks to be from an already released version. We look for a date in it (like 2019-02-20). Give a warning when this happens. Fixes issue 311. [maurits]
  • Better support for zestreleaser.towncrier (and similar extensions): the update_history setting is now also respected by the bumpversion command. Fixes issue 310. [maurits]

6.16.0 (2019-01-17)

  • Fix for issue #259: using zest.releaser on windows no longer can result in accidental extra \r (carriage return) characters in the changelog and your [reinout]

6.15.4 (2019-01-11)

  • We retain the existing quoting style for the version='1.0' in files. The “black” code formatting prefers double quotes and zest.releaser by default wrote single quotes. [reinout]
  • Fix for issue #299: bumpversion now also compares versions numerically instead of as a string, so 2.9 < 2.10 is now true. [reinout]

6.15.3 (2018-12-03)

  • Fix for issue #297: bytes+int problem on python 3 when detecting encodings. [reinout]

6.15.2 (2018-08-30)

  • If a tag already exists, zest.releaser asks a safety question. The location where the question gets asked was moved slightly to help a program that uses zest.releaser as a library. [reinout]
  • Switched our readthedocs urls to https. [reinout]

6.15.1 (2018-06-22)

  • Fix for #286: removed the confusing word “register” from the info message you got when a package wasn’t available yet on pypi.

    Registering isn’t used anymore on pypi, but it was still in our textual message. [reinout]

6.15.0 (2018-05-15)

  • Use, especially when checking if a package is on PyPI. Fixes issue #281. [maurits]
  • Added key update_history in prerelease and postrelease data. Plugins can use this to tell zest.releaser (and other plugins) to not touch the history, presumably because the plugin handles it. [maurits]
  • Declared requests dependency. Declared zope.testing test dependency. [maurits]

6.14.0 (2018-03-26)

  • Advertise setup.cfg option [zest.releaser] history-file. Usually zest.releaser can find the correct history or changelog file on its own. But sometimes it may not find anything, or it finds multiple files and selects the wrong one. Then you can set a path here. A history_file option with an underscore was already read, but not documented. Now we try both a dash and an underscore for good measure. [maurits]
  • Use new setup.cfg option [zest.releaser] encoding. Set this to, for example, utf-8 when the encoding of your CHANGES.rst file is not determined correctly. Fixes issue 264. [maurits]
  • When inserting changelog entry, check that it conforms to the existing encoding. Try to recover if there is a difference, especially when the changelog file was ascii and we insert utf-8. [maurits]
  • When determining encoding, first look for coding hints in the file itself. Only when that fails, we try tokenize or chardet. Fixes issue 264. [maurits]
  • Get PyPI password raw, without interpolation. If you had a password with a percentage sign, you could get an error. Fixes issue 271. [maurits]
  • Prevent unclosed files. Python 3.6 warned about them, and PyPy may have more problems with it. Fixed several other DeprecationWarnings. [maurits]
  • Print commands in a nicer way. You could get ugly output like this, especially on Python 2.7: INFO: The '[u'git', u'diff']': or worse: Command failed: u"t w i n e ' ' u p l o a d". [maurits]
  • Test compatibility with Python 2.7, 3.4, 3.5, 3.6, PyPy2. [maurits]

6.13.5 (2018-02-16)

  • Quit in postrelease when we cannot find a version. Fixes issue #262 <>. [maurits]

6.13.4 (2018-02-05)

  • Fixed IOError when setup.cfg is missing and no version is found. [maurits]

6.13.3 (2017-12-19)

  • Fixed writing of files in original encoding on python3, too. [andreparames]

6.13.2 (2017-11-27)

  • Fixed tests with mercurial 4.4+. [maurits]
  • Fixed writing of files in original encoding. [mgedmin]

6.13.1 (2017-11-13)

  • Add tag message formatting (option tag-message). [htgoebel]

6.13.0 (2017-11-10)

  • Add support for signing tags (option tag-signing). [htgoebel]

6.12.5 (2017-09-25)

  • Sorting uploadable filenames so that wheels are uploaded first. (For most filesystems this happened automatically, but the order on OSX’ new filesystem is non-deterministic, so we added sorting.) [reinout]
  • Release process will now fail when specified hooks cannot be imported. (PR #236)

6.12.4 (2017-08-30)

  • Also support version in setup.cfg. [ewjoachim]

6.12.3 (2017-08-16)

  • Allows {version} format for tag-format. [leorochael]

6.12.2 (2017-07-13)

  • Subversion fix: create tag of entire trunk or branch when not in repo root. If you have trunk/pkg1 and trunk/pkg2 and you make tag 1.0 in directory pkg1, then until now we would create tags/1.0 with the contents of directory pkg1. Checking out the tag and changing to the pkg1 directory then failed. We now make a tag of the entire trunk or branch, just like in the other version control systems. Fixes issue #213. [maurits]
  • Do not needlessly run svn info. [maurits]

6.12.1 (2017-07-03)

  • Quote the path when making a git clone, to fix problems with spaces. [halkeye]
  • Fixed percentage signs in date-format in setup.cfg. You need double percentages. [mgedmin]

6.12 (2017-06-19)

  • Add date format in the config. Default is ISO-8601 (%Y-%m-%d). Put date-format = format string in your ~/.pypirc or setup.cfg. [mgedmin]

6.11 (2017-06-09)

  • If the package wants to build universal wheels by setting [bdist_wheel] universal = 1, then the default for create-wheel is now yes.

6.10 (2017-04-18)

  • Corner case fix: a top-level version = 1.0 in your is now also allowed to be in uppercase, like VERSION = 1.0. This fixes issue 216. [reinout]

6.9 (2017-02-17)

  • Add tag formatter in the config. This is a formatter that changes the name of the tag. Default is the same as the version. Put tag-format = a string in your ~/.pypirc or setup.cfg. It needs to contain %(version)s. [tcezard]

6.8.1 (2017-01-13)

  • Catch error when uploading first package file in new PyPI project. This fixes issue 206. [maurits]

6.8 (2016-12-30)

  • Before retrying a twine command, reload the pypi config. Then when the user fixes his account settings in ~/.pypirc and retries, these changes take effect. This used to work a while ago, but got broken. [maurits]

  • Added development-marker config option. With this can override the default .dev0. [drucci]

  • Added version-levels and less-zeroes options. This influences the suggested version. [maurits]

  • Allow .pypirc with just a pypi section. Previously, we required either a [server-login] section with a username option, or a [distutils] section with an index-servers option. Failing this, we gave a warning about a not properly configured file, and happily continued without uploading anything. Now if there is something missing from the pypirc file, we give an error and explicitly ask if you want to continue without uploading. Fixes issue #199.

    Note for developers of extensions for zest.releaser: this removes the is_old_pypi_config and is_new_pypi_config methods, because they made no sense anymore. If you were using these, see if you can use the distutils_server method instead. [maurits]

  • Added push-changes config file option. Default: yes. When this is false, zest.releaser sets no as default answer for the question if you want to push the changes to the remote. [newlog]

  • By default no longer register a new package, but only upload it. Registering a package is no longer needed on PyPI: uploading a new distribution takes care of this. If you do want to register, maybe because a different package server requires it, then in your setup.cfg or ~/.pypirc, use the following:

    register = yes

    Fixes issue 191. [willowmck]

6.7.1 (2016-12-22)

  • Create the list of distributions after the before_upload hook has fired. This allows the before_upload hook to create additional distributions, which will then be uploaded. [t-8ch]

6.7 (2016-10-23)

  • Use the intended API of twine. This should work with twine 1.6.0 and higher, including future versions. [maurits]

6.6.5 (2016-09-12)

6.6.4 (2016-02-24)

  • Really create a shallow git clone when creating a distribution. See issue #169. [maurits]

6.6.3 (2016-02-24)

  • Using a “shallow” git clone when creating a distribution. This speeds up releases, especially on big repositories. See issue #169. [gforcada]

6.6.2 (2016-02-11)

  • Added no-input option also to global (.pypirc) options. Issue #164. [jcerjak]

6.6.1 (2016-02-02)

  • Fixed version in changelog after bumpversion call. [maurits]

6.6.0 (2016-01-29)

  • Added bumpversion command. Options --feature and --breaking. Issue #160. The exact behavior might change in future versions after more practical experience. Try it out and report any issues you find. [maurits]
  • Fixed possible encoding problems when writing files. This is especially for an ascii file to which we add non ascii characters, like in the addchangelogentry command. [maurits]
  • Added addchangelogentry command. Issue #159. [maurits]
  • Moved _diff_and_commit, _push and _grab_version to, as the first was duplicated and the second and third may be handy for other code too. _grab_version is the basic implementation, and is overridden in the prereleaser. [maurits]
  • Show changelog of current release before asking for the new version number. Issue #155. [maurits]
  • Moved _diff_and_commit, _push and _grab_version to, as the first was duplicated and the second and third may be handy for other code too. _grab_version is the basic implementation, and is overridden in the prereleaser. [maurits]

6.5 (2016-01-05)

  • Adjusted bin/longtest for the (necessary) rename of the readme library to readme_renderer. Fixes #153

    Note: the current readme package on pypi is broken to force an upgrade. If you use an older zest.releaser, you have to pin readme to 0.6.0, it works just fine. [reinout]

6.4 (2015-11-13)

  • Fixed error when retrying twine command. Fixes #148 [maurits]

6.3 (2015-11-11)

  • Fixed exception when logging an exception when a twine command fails. [maurits]

6.2 (2015-10-29)


  • Use twine as library instead of as command. You no longer need to have twine on your PATH. Fixes issue #142. [maurits]

6.1 (2015-10-29)


  • Fixed registering on servers other than PyPI. We forgot to specify the server in that case. [maurits]

6.0 (2015-10-27)

  • Made twine a core dependency. We now always use it for registering and uploading. We require at least version 1.6.0, as this introduces the register command. [maurits]
  • When uploading with twine first use the twine register command. On PyPI, when the project is already registered, we do not call it again, but we can only check this for PyPI, not for other servers. Issue #128. [maurits]
  • Always exit with error code 1 when we exit explicitly. In some cases we would exit with success code 0 when we exited based on the answer to a question. This happened when the user did not want us to create the missing tags directory in subversion, and also after asking if the user wanted to continue even though ‘nothing changed yet’ was in the history. [maurits]
  • Extensions can now tell zest.releaser to look for specific required words in the history. Just add required_changelog_text to the prerelease data. It can be a string or a list, for example ["New:", "Fixes:"]. For a list, only one of them needs to be present. [maurits]
  • Look for the ‘Nothing changed yet’ text in the complete text of the history entries of the current release, instead of looking at it line by line. This means that zest releaser extensions can overwrite nothing_changed_yet in the prerelease data to span multiple lines. [maurits]
  • zest.releaser extensions can now look at history_insert_line_here in the prerelease data. On this line number in the history file they can add an extra changelog entry if wanted. [maurits]
  • Added history_last_release to the prerelease data. This is the text with all history entries of the current release. [maurits]
  • When using the --no-input option, show the question and the chosen answer. Otherwise in case of a problem it is not clear why the command stopped. Fixes issue #136. [maurits]

5.7 (2015-10-14)

  • The history/changelog file is now written back with the originally detected encoding. The functionality was added in 5.2, but only used for writing the, not the changelog. This is fixed now. [reinout]

5.6 (2015-09-23)

  • Add support for PyPy. [jamadden]

5.5 (2015-09-05)

  • The bin/longtest command adds the correct utf-8 character encoding hint to the resulting html so that non-ascii long descriptions are properly rendered in all browsers. [reinout]

5.4 (2015-08-28)

  • Requiring at least version 0.6 of the (optional, btw) readme package. The API of readme changed slightly. Only needed when you want to check your package’s long description with bin/longtest. [reinout]

5.3 (2015-08-21)

  • Fixed typo in svn command to show the changelog since the last tag. [awello]

5.2 (2015-07-27)

  • When we find no version control in the current directory, look a few directories up. When looking for version and history files, we look in the current directory and its sub directories, and not in the repository root. After making a tag checkout, we change directory to the same relative path that we were in before. You can use this when you want to release a Python package that is in a sub directory of the repository. When we detect this, we first offer to change to the root directory of the repository. [maurits]
  • Write file with the same encoding that we used for reading them. Issue #109. [maurits]

5.1 (2015-06-11)

  • Fix writing history/changelog file with non-ascii. Issue #109. [maurits]
  • Release zest.releaser as universal wheel, so one wheel for Python 2 and 3. As usual, we release it also as a source distribution. [maurits]
  • Regard “Skipping installation of (namespace package)” as warning, printing it in magenta. This can happen when creating a wheel. Issue #108. [maurits]

5.0 (2015-06-05)

  • Python 3 support. [mitchellrj]
  • Use the same readme library that PyPI uses to parse long descriptions when we test and render them. [mitchellrj]

4.0 (2015-05-21)

  • Try not to treat warnings as errors. [maurits]
  • Allow retrying some commands when there is an error. Currently only for commands that talk to PyPI or another package index. We ask the user if she wants to retry: Yes, no, quit. [maurits]
  • Added support for twine. If the twine command is available, it is used for uploading to PyPI. It is installed automatically if you use the zest.releaser[recommended] extra. Note that if the twine command is not available, you may need to change your system PATH or need to install twine explicitly. This seems more needed when using zc.buildout than when using pip. Added releaser.before_upload entry point. Issue #59. [maurits]
  • Added check-manifest and pyroma to the recommended extra. Issue #49. [maurits]
  • Python 2.6 not officially supported anymore. It may still work, but we are no longer testing against it. [maurits]
  • Do not accept y or n as answer for a new version. [maurits]
  • Use colorama to output errors in red. Issue #86 [maurits]
  • Show errors when uploading to PyPI. They were unintentionally swallowed before, so you did not notice when an upload failed. Issue #84. [maurits]
  • Warn when between the last postrelease and a new prerelease no changelog entry has been added. ‘- Nothing changed yet’ would still be in there. Issue #26. [maurits]
  • Remove code for support of collective.sdist. That package was a backport from distutils for Python 2.5 and earlier, which we do not support. [maurits]
  • Add optional support for uploading Python wheels. Use the new zest.releaser[recommended] extra, or run pip install wheel yourself next to zest.releaser. Create or edit setup.cfg in your project (or globally in your ~/.pypirc) and create a section [zest.releaser] with create-wheel = yes to create a wheel to upload to PyPI. See for deciding whether this is a good idea for your package. Briefly, if it is a pure Python 2 or pure Python 3 package: just do it. Issue #55 [maurits]
  • Optionally add extra text to commit messages. This can be used to avoid running Travis Continuous Integration builds. See To activate this, add extra-message = [ci skip] to a [zest.releaser] section in the setup.cfg of your package, or your global ~/.pypirc. Or add your favorite geeky quotes there. [maurits]
  • Fix a random test failure on Travis CI, by resetting AUTO_RESPONSE. [maurits]
  • Added clarification to logging: making an sdist/wheel now says that it is being created in a temp folder. Fixes #61. [reinout]

Release history Release notifications | RSS feed

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-6.22.2.tar.gz (141.3 kB view hashes)

Uploaded source

Built Distribution

zest.releaser-6.22.2-py2.py3-none-any.whl (124.0 kB view hashes)

Uploaded py2 py3

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