Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Automate cutting new releases #1141

Merged
merged 3 commits into from Jul 6, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
58 changes: 58 additions & 0 deletions .github/workflows/publish.yml
@@ -0,0 +1,58 @@
# This workflow is triggered three ways:
#
# 1. Manually triggering the workflow via the GitHub UI (Actions > Upload
# package) will upload to test.pypi.org without the need to create a tag.
# 2. When a tag is created, the workflow will upload the package to
# test.pypi.org.
# 3. When a GitHub Release is made, the workflow will upload the package to pypi.org.
#
# It is done this way until PyPI has draft reviews, to allow for a two-stage
# upload with a chance for manual intervention before the final publication.
name: Upload package

on:
release:
types: [created]
push:
tags:
- '*'
workflow_dispatch:

jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Set up Python
uses: actions/setup-python@v2
with:
python-version: '3.9'
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install -U tox
- name: Create tox environments
run: |
tox -p -e py,build,release --notest
- name: Run tests
run: |
tox -e py
- name: Build package
run: |
tox -e build
- name: Publish package
env:
TWINE_USERNAME: "__token__"
run: |
if [[ "$GITHUB_EVENT_NAME" == "push" || \
"$GITHUB_EVENT_NAME" == "workflow_dispatch" ]]; then
export TWINE_REPOSITORY_URL="https://test.pypi.org/legacy/"
export TWINE_PASSWORD="${{ secrets.TEST_PYPI_UPLOAD_TOKEN }}"
elif [[ "$GITHUB_EVENT_NAME" == "release" ]]; then
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should either trigger on "release" or on workflow_dispatch, but not on both.

I'm inclined to say that we do it on workflow_dispatch.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note triggering in workflow dispatch will allow us to test things. It will generate dev versions (as there won't be a tag) that will be uploaded to test.pypi.org.

Why do you think this is a bad idea? At the very least as a way to test changes in the publish logic?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I went ahead and made this change myself.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you have it backwards. For the other repos I have using this same workflow, the workflows were designed before "manually trigger an action" was a thing you could do, so I was using "made a GitHub release" as a way to get a manual step I could take in the UI to trigger the "final" release.

The way you've got it wired up, manual triggers and making a GitHub release both trigger the "do a final release" pipeline, which is unnecessary, and whichever one happens second will fail anyway, so I've just changed it so that the manual workflow triggering is the only way to finalize. We can still make GH releases, but we'll do it after everything is published to PyPI.

export TWINE_REPOSITORY="pypi"
export TWINE_PASSWORD="${{ secrets.PYPI_UPLOAD_TOKEN }}"
else
echo "Unknown event name: ${GITHUB_EVENT_NAME}"
exit 1
fi
tox -e release
4 changes: 2 additions & 2 deletions .github/workflows/validate.yml
Expand Up @@ -91,9 +91,9 @@ jobs:
steps:
- uses: actions/checkout@v2
- name: Set up Python
uses: actions/setup-python@v1
uses: actions/setup-python@v2
with:
python-version: 3.7
python-version: "3.9"
- name: Install tox
run: python -m pip install -U tox
- name: Run tox
Expand Down
11 changes: 7 additions & 4 deletions README.rst
Expand Up @@ -139,16 +139,19 @@ It is maintained by:
* Yaron de Leeuw <me@jarondl.net> 2014-2016
* Paul Ganssle <paul@ganssle.io> 2015-

Starting with version 2.4.1, all source and binary distributions will be signed
by a PGP key that has, at the very least, been signed by the key which made the
previous release. A table of release signing keys can be found below:
Starting with version 2.4.1 and running until 2.8.2, all source and binary
distributions will be signed by a PGP key that has, at the very least, been
signed by the key which made the previous release. A table of release signing
keys can be found below:

=========== ============================
Releases Signing key fingerprint
=========== ============================
2.4.1- `6B49 ACBA DCF6 BD1C A206 67AB CD54 FCE3 D964 BEFB`_
2.4.1-2.8.2 `6B49 ACBA DCF6 BD1C A206 67AB CD54 FCE3 D964 BEFB`_
=========== ============================

New releases *may* have signed tags, but binary and source distributions
uploaded to PyPI will no longer have GPG signatures attached.

Contact
=======
Expand Down
68 changes: 41 additions & 27 deletions RELEASING
Expand Up @@ -13,53 +13,67 @@ Release Checklist
interesting to anyone consuming the package (e.g. changes to CI) with
a reference to the Github PR.
[ ] Commit the changes in git and make a pull request.
[ ] Accept the pull request and tag the repository with the release number.
[ ] Add the contents of the NEWS file to the Github release notes for the
release.
[ ] Upload the source and binary distributions with `tox -e release`.
[ ] Follow the "Releasing" steps below


Optional:
----------
[ ] Check that README.rst is up-to-date.
[ ] Check that the documentation builds correctly (cd docs, make html)


Instructions
-----------------------------------------
See the instructions at https://packaging.python.org/en/latest/distributing/
for more details.


Versioning
----------
Try and keep to a semantic versioning scheme (http://semver.org/). The versions
are managed with `setuptools_scm`, so to update the version, simply tag the
relevant commit with the new version number.


Instructions
-----------------------------------------
See the instructions at https://packaging.python.org/en/latest/distributing/
for more details.


Building and Releasing
----------------------
Building and releasing can be done using the `release.py` script, which
automates building, signing and uploading. Since it uses GPG for signing and
for decrypting a stored token, it requires that `gpg` be installed on your
system. Because it has python dependencies, the best way to use the
`release.py` script is to invoke it using `tox`. To build the source and binary
distributions, use:
Releasing is automated via the `publish.yml` GitHub Actions workflow. When a
new tag is pushed to the repository, the project is automatically built and
uploaded to Test PyPI. When the publish action is triggered manually (see
https://docs.github.com/en/actions/managing-workflow-runs/manually-running-a-workflow
for more details), the result is uploaded to PyPI.

To make a release:

tox -e build
1. After having made a PR with all the relevant changes, trigger the "Upload
package" to trigger an upload to Test PyPI. If desired, you can push a
`.dev0` or `.rc0` tag first, so that all uploads will have a prefix for the
*next* version rather than the previous version (e.g. if you are releasing
`3.1.2`, without making a new tag releases will have a version like
`3.1.1+gff8893e.d20220603`; if you push a `3.1.2.dev0` tag first, the version
number will be `3.1.2.dev0`, and subsequent commits will be things like
`3.1.2.dev0+fe9dacc4.d20220603`).

This will build the distributions in `dist/`. Once that is done, you can release
them with:
2. Check the Test PyPI page for `python-dateutil` to ensure that the dev release
worked correctly: https://test.pypi.org/project/python-dateutil/

tox -e release
Dev releases may not appear as the default page, so click "Release history"
and navigate to the release you are trying to check. Make sure that the
metadata looks right and in particular that the `Requires` metadata is
present.

if you have the token stored in your `~/.pypirc` file. If you have stored the
relevant token in an encrypted file, use the `--passfile` argument:
4. If the release failed or was unsatisfactory in some way, make the required
changes and got back to step 1. Pushing a new tag is not necessary.

tox -e release -- --passfile .token.gpg
5. When everything looks good, merge the release PR, pull the result to your
local branch and create a new tag with a non-dev version number,
e.g. `3.1.2`. Push this to the repository, wait for the Test PyPI run to
trigger, and ensure that the upload worked.

The `release` command defaults to uploading to test.pypi.org. To upload to
pypi.org, use the `--release` flag, so putting it all together, we have:
6. Create a new GitHub release with the new entries from `NEWS` in the
description. This will trigger the workflow to build and release the final
version to PyPI.org. Check https://pypi.org/project/python-dateutil to
ensure that everything worked correctly.

tox -e build
tox -e release -- --passfile .token.gpg --release
7. Delete any dev tags created during the testing process from your upstream
and local branches.
81 changes: 0 additions & 81 deletions release.py

This file was deleted.

26 changes: 14 additions & 12 deletions tox.ini
Expand Up @@ -95,21 +95,23 @@ commands =

[testenv:build]
description = Build an sdist and bdist
basepython = python3.7
basepython = python3.9
skip_install = true
passenv = *
deps = click >= 7.0
build[virtualenv] >= 0.3.0
deps = build[virtualenv] >= 0.3.0
commands =
python release.py build
python -m build --wheel --sdist --outdir dist .

[testenv:release]
description = Sign and upload the built distributions to PyPI
basepython = python3.7
skip_install = true
passenv = *
deps = click >= 7.0
twine >= 2.0.0
description = Make a release; must be called after "build"
skip_install = True
deps =
twine
depends =
build
passenv =
TWINE_*
commands =
python release.py sign
python release.py upload {posargs}
twine check {toxinidir}/dist/*
twine upload {toxinidir}/dist/* \
{posargs:-r {env:TWINE_REPOSITORY:testpypi} --non-interactive}