diff --git a/docs/internals/howto-release-django.txt b/docs/internals/howto-release-django.txt index bb0d061363..0f293d575b 100644 --- a/docs/internals/howto-release-django.txt +++ b/docs/internals/howto-release-django.txt @@ -2,9 +2,7 @@ How is Django Formed? ===================== -This document explains how to release Django. If you're unlucky enough to -be driving a release, you should follow these instructions to get the -package out. +This document explains how to release Django. **Please, keep these instructions up-to-date if you make changes!** The point here is to be descriptive, not prescriptive, so feel free to streamline or @@ -13,28 +11,26 @@ otherwise make changes, but **update this document accordingly!** Overview ======== -There are three types of releases that you might need to make +There are three types of releases that you might need to make: -* Security releases, disclosing and fixing a vulnerability. This'll +* Security releases: disclosing and fixing a vulnerability. This'll generally involve two or three simultaneous releases -- e.g. 1.5.x, 1.6.x, and, depending on timing, perhaps a 1.7 alpha/beta/rc. -* Regular version releases, either a final release (e.g. 1.5) or a +* Regular version releases: either a final release (e.g. 1.5) or a bugfix update (e.g. 1.5.1). -* Pre-releases, e.g. 1.6 beta or something. +* Pre-releases: e.g. 1.6 alpha, beta, or rc. -In general the steps are about the same regardless, but there are a few -differences noted. The short version is: +The short version of the steps involved is: #. If this is a security release, pre-notify the security distribution list - at least one week before the actual release. + one week before the actual release. -#. Proofread (and create if needed) the release notes, looking for - organization, writing errors, deprecation timelines, etc. Draft a blog post - and email announcement. +#. Proofread the release notes, looking for organization and writing errors. + Draft a blog post and email announcement. -#. Update version numbers and create the release package(s)! +#. Update version numbers and create the release package(s). #. Upload the package(s) to the ``djangoproject.com`` server. @@ -51,18 +47,29 @@ There are a lot of details, so please read on. Prerequisites ============= -You'll need a few things hooked up to make this work: +You'll need a few things before getting started: -* A GPG key recorded as an acceptable releaser in the `Django releasers`__ - document. (If this key is not your default signing key, you'll need to add - ``-u you@example.com`` to every GPG signing command below, where +* A GPG key. If the key you want to use is not your default signing key, you'll + need to add ``-u you@example.com`` to every GPG signing command below, where ``you@example.com`` is the email address associated with the key you want to - use.) + use. -* Access to Django's record on PyPI. +* An install of some required Python packages: -* Access to the ``djangoproject.com`` server to upload files and trigger a - deploy. + .. code-block:: bash + + $ pip install wheel twine + +* Access to Django's record on PyPI. Create a file with your credentials: + + .. snippet:: + :filename: ~/.pypirc + + [pypi] + username:YourUsername + password:YourPassword + +* Access to the ``djangoproject.com`` server to upload files. * Access to the admin on ``djangoproject.com`` as a "Site maintainer". @@ -74,8 +81,6 @@ You'll need a few things hooked up to make this work: If this is your first release, you'll need to coordinate with James and/or Jacob to get all these things lined up. -__ https://www.djangoproject.com/m/pgp/django-releasers.txt - Pre-release tasks ================= @@ -85,17 +90,10 @@ any time leading up to the actual release: #. If this is a security release, send out pre-notification **one week** before the release. We maintain a list of who gets these pre-notification emails in - the private ``django-core`` repository. This email should be signed by the - key you'll use for the release, and should include patches for each issue - being fixed. Also make sure to update the security issues archive; this will - be in ``docs/releases/security.txt``. - -#. If this is a major release, make sure the tests pass, then increase - the default PBKDF2 iterations in - ``django.contrib.auth.hashers.PBKDF2PasswordHasher`` by about 20% - (pick a round number). Run the tests, and update the 3 failing - hasher tests with the new values. Make sure this gets noted in the - release notes (see release notes on 1.6 for an example). + the private ``django-core`` repository. Send the mail to + ``security@djangoproject.com`` and BCC the pre-notification recipients. + This email should be signed by the key you'll use for the release, and + should include patches for each issue being fixed. #. As the release approaches, watch Trac to make sure no release blockers are left for the upcoming release. @@ -159,20 +157,24 @@ OK, this is the fun part, where we actually push out a release! checkout security/1.5.x; git rebase stable/1.5.x``) and then switch back and do the merge. Make sure the commit message for each security fix explains that the commit is a security fix and that an announcement will follow - (`example security commit`__) + (`example security commit`__). __ https://github.com/django/django/commit/3ef4bbf495cc6c061789132e3d50a8231a89406b -#. Update version numbers for the release. This has to happen in three - places: ``django/__init__.py``, ``docs/conf.py``, and ``setup.py``. - Please see `notes on setting the VERSION tuple`_ below for details - on ``VERSION``. Here's `an example commit updating version numbers`__ - - __ https://github.com/django/django/commit/18d920ea4839fb54f9d2a5dcb555b6a5666ee469 - #. For a major version release, remove the ``UNDER DEVELOPMENT`` header at the top of the release notes and add the release date on the next line. For a - minor release, replace ``*Under Development*`` with the release date. + minor release, replace ``*Under Development*`` with the release date. Make + this change on all branches where the release notes for a particular version + are located. + +#. Update the version number in ``django/__init__.py`` for the release. + Please see `notes on setting the VERSION tuple`_ below for details + on ``VERSION``. + + In 1.4, the version number in ``docs/conf.py`` and ``setup.py`` should also + be updated. Here's `an example commit updating version numbers`__ for that. + + __ https://github.com/django/django/commit/592187e11b934f83153133cd5b3a246a881359e7 #. If this is a pre-release package, update the "Development Status" trove classifier in ``setup.py`` to reflect this. Otherwise, make sure the @@ -180,7 +182,7 @@ OK, this is the fun part, where we actually push out a release! #. Tag the release using ``git tag``. For example:: - git tag --sign --message="Django 1.5.1" 1.5.1 + git tag --sign --message="Tag 1.5.1" 1.5.1 You can check your work by running ``git tag --verify ``. @@ -189,17 +191,22 @@ OK, this is the fun part, where we actually push out a release! #. Make sure you have an absolutely clean tree by running ``git clean -dfx``. #. Run ``make -f extras/Makefile`` to generate the release packages. This will - create the release packages in a ``dist/`` directory. + create the release packages in a ``dist/`` directory. Note that we don't + publish wheel files for 1.4. -#. Generate the hashes of the release packages:: +#. Generate the hashes of the release packages: - $ md5sum dist/Django-* - $ sha1sum dist/Django-* - $ openssl dgst -sha256 dist/Django-* + .. code-block:: bash -#. Create a "checksums" file containing the hashes and release information. - Start with this template and insert the correct version, date, release URL - and checksums:: + $ cd dist + $ md5sum * + $ sha1sum * + $ sha256sum * + +#. Create a "checksums" file, ``Django-<>.checksum.txt`` containing + the hashes and release information. Start with this template and insert the + correct version, date, GPG key ID (from + ``gpg --list-keys --keyid-format LONG``), release URL, and checksums:: This file contains MD5, SHA1, and SHA256 checksums for the source-code tarball of Django <>, released <>. @@ -207,11 +214,11 @@ OK, this is the fun part, where we actually push out a release! To use this file, you will need a working install of PGP or other compatible public-key encryption software. You will also need to have the Django release manager's public key in your keyring; this key has - the ID ``0x3684C0C08C8B2AE1`` and can be imported from the MIT + the ID ``XXXXXXXXXXXXXXXX`` and can be imported from the MIT keyserver. For example, if using the open-source GNU Privacy Guard - implementation of PGP:: + implementation of PGP: - gpg --keyserver pgp.mit.edu --recv-key 0x3684C0C08C8B2AE1 + gpg --keyserver pgp.mit.edu --recv-key XXXXXXXXXXXXXXXX Once the key is imported, verify this file:: @@ -221,29 +228,31 @@ OK, this is the fun part, where we actually push out a release! checksumming applications to generate the checksums of the Django package and compare them to the checksums listed below. + Release packages: + ================= - Release package: - ================ + Django <> (tar.tgz): https://www.djangoproject.com/m/releases/<> + Django <> (.whl): https://www.djangoproject.com/m/releases/<> - Django <>: https://www.djangoproject.com/m/releases/<> - - - MD5 checksum: - ============= - - MD5(<>)= <> - - SHA1 checksum: + MD5 checksums: ============== - SHA1(<>)= <> + <> <> + <> <> - SHA256 checksum: - ================ + SHA1 checksums: + =============== - SHA256(<>)= <> + <> <> + <> <> -#. Sign the checksum file (``gpg --clearsign + SHA256 checksums: + ================= + + <> <> + <> <> + +#. Sign the checksum file (``gpg --clearsign --digest-algo SHA256 Django-.checksum.txt``). This generates a signed document, ``Django-.checksum.txt.asc`` which you can then verify using ``gpg --verify Django-.checksum.txt.asc``. @@ -255,26 +264,35 @@ Making the release(s) available to the public Now you're ready to actually put the release out there. To do this: -#. Upload the release package(s) to the djangoproject server; releases go - in ``/home/www/djangoproject.com/src/media/releases``, under a - directory for the appropriate version number (e.g. - ``/home/www/djangoproject.com/src/media/releases/1.5`` for a ``1.5.x`` - release.). +#. Upload the release package(s) to the djangoproject server, replacing + A.B. with the appropriate version number, e.g. 1.5 for a 1.5.x release: -#. Upload the checksum file(s); these go in - ``/home/www/djangoproject.com/src/media/pgp``. + .. code-block:: bash + + $ scp Django-* djangoproject.com:/home/www/www/media/releases/A.B + +#. Upload the checksum file(s): + + .. code-block:: bash + + $ scp Django-A.B.C.checksum.txt.asc djangoproject.com:/home/www/www/media/pgp/Django-A.B.C.checksum.txt #. Test that the release packages install correctly using ``easy_install`` - and ``pip``. Here's one method (which requires `virtualenvwrapper`__):: + and ``pip``. Here's one method (which requires `virtualenvwrapper`__): + + .. code-block:: bash + + $ RELEASE_VERSION='1.7.2' + $ MAJOR_VERSION=`echo $RELEASE_VERSION| cut -c 1-3` $ mktmpenv - $ easy_install https://www.djangoproject.com/m/releases/1.5/Django-1.5.1.tar.gz + $ easy_install https://www.djangoproject.com/m/releases/$MAJOR_VERSION/Django-$RELEASE_VERSION.tar.gz $ deactivate $ mktmpenv - $ pip install https://www.djangoproject.com/m/releases/1.5/Django-1.5.1.tar.gz + $ pip install https://www.djangoproject.com/m/releases/$MAJOR_VERSION/Django-$RELEASE_VERSION.tar.gz $ deactivate $ mktmpenv - $ pip install https://www.djangoproject.com/m/releases/1.5/Django-1.5.1-py2.py3-none-any.whl + $ pip install https://www.djangoproject.com/m/releases/$MAJOR_VERSION/Django-$RELEASE_VERSION-py2.py3-none-any.whl $ deactivate This just tests that the tarballs are available (i.e. redirects are up) and @@ -289,24 +307,11 @@ Now you're ready to actually put the release out there. To do this: correct (proper version numbers, no stray ``.pyc`` or other undesirable files). -#. If this is a release that should land on PyPI (i.e. anything except for - a pre-release), register the new package with PyPI by running - ``python setup.py register``. +#. Upload the release packages to PyPI: -#. Upload the sdist you generated a few steps back through the PyPI web - interface. You'll log into PyPI, click "Django" in the right sidebar, - find the release you just registered, and click "files" to upload the - sdist. + .. code-block:: bash - .. note:: - - Why can't we just use ``setup.py sdist upload``? Well, if we do it above - that pushes the sdist to PyPI before we've had a chance to sign, review - and test it. And we can't just ``setup.py upload`` without ``sdist`` - because ``setup.py`` prevents that. Nor can we ``sdist upload`` because - that would generate a *new* sdist that might not match the file we just - signed. Finally, uploading through the web interface is somewhat more - secure: it sends the file over HTTPS. + $ twine upload -s dist/* #. Go to the `Add release page in the admin`__, enter the new release number exactly as it appears in the name of the tarball (Django-.tar.gz). @@ -324,8 +329,8 @@ Now you're ready to actually put the release out there. To do this: others); you can do this using the site's admin. #. Post the release announcement to the |django-announce|, - |django-developers| and |django-users| mailing lists. This should - include links to the announcement blog post and the release notes. + |django-developers|, and |django-users| mailing lists. This should + include links to the announcement blog post. Post-release ============ @@ -337,22 +342,46 @@ You're almost done! All that's left to do now is: example, after releasing 1.5.1, update ``VERSION`` to ``VERSION = (1, 5, 2, 'alpha', 0)``. -#. For the first beta release of a new version (when we create the - ``stable/1.?.x`` git branch), you'll want to create a new - ``DocumentRelease`` object in the ``docs.djangoproject.com`` database for - the new version's docs, and update the ``docs/fixtures/doc_releases.json`` - JSON fixture, so people without access to the production DB can still - run an up-to-date copy of the docs site. - #. Add the release in `Trac's versions list`_ if necessary (and make it the default if it's a final release). Not all versions are declared; take example on previous releases. -#. On the master branch, remove the ``UNDER DEVELOPMENT`` header in the notes - of the release that's just been pushed out. +#. If this was a security release, update :doc:`/releases/security` with + details of the issues addressed. .. _Trac's versions list: https://code.djangoproject.com/admin/ticket/versions +New stable branch tasks +======================= + +There are several items to do in the time following a the creation of a new +stable branch (often following an alpha release). Some of these tasks don't +need to be done by the releaser. + +#. Create a new ``DocumentRelease`` object in the ``docs.djangoproject.com`` + database for the new version's docs, and update the + ``docs/fixtures/doc_releases.json`` JSON fixture, so people without access + to the production DB can still run an up-to-date copy of the docs site. + +#. Create a stub release note for the new major version. Use the stub from the + previous major version or use the previous major version and delete most of + the contents leaving only section headings. + +#. Increase the default PBKDF2 iterations in + ``django.contrib.auth.hashers.PBKDF2PasswordHasher`` by about 20% + (pick a round number). Run the tests, and update the 3 failing + hasher tests with the new values. Make sure this gets noted in the + release notes (see the 1.8 release notes for an example). + +#. Remove features that have reached the end of their deprecation cycle. Each + removal should be done in a separate commit for clarity. In the commit + message, add a "refs #XXXX" to the original ticket where the deprecation + began if possible. + +#. Remove ``.. versionadded::``, ``.. versionadded::``, and ``.. deprecated::`` + annotations in the documentation from two releases ago. For example, in + Django 1.9, notes for 1.7 will be removed. + Notes on setting the VERSION tuple ================================== @@ -373,8 +402,8 @@ be reported as "pre-alpha". Some examples: -* ``(1, 2, 1, 'final', 0)`` --> "1.2.1" +* ``(1, 2, 1, 'final', 0)`` → "1.2.1" -* ``(1, 3, 0, 'alpha', 0)`` --> "1.3 pre-alpha" +* ``(1, 3, 0, 'alpha', 0)`` → "1.3 pre-alpha" -* ``(1, 3, 0, 'beta', 2)`` --> "1.3 beta 2" +* ``(1, 3, 0, 'beta', 2)`` → "1.3 beta 2"