Skip to content

Commit

Permalink
Merge pull request #62 from pganssle/wheel_release
Browse files Browse the repository at this point in the history
Add release automation
  • Loading branch information
pganssle authored May 21, 2020
2 parents ebbb76e + 2b2f5f4 commit 0221430
Show file tree
Hide file tree
Showing 8 changed files with 311 additions and 2 deletions.
154 changes: 154 additions & 0 deletions .github/workflows/build-publish.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
# This workflow is used to build all the wheels and source distributions for
# the project and, on tags and releases, upload them. It is enabled on every
# commit to ensure that the wheels can be built on all platforms, but releases
# are only triggered in two situations:
#
# 1. When a tag is created, the workflow will upload the package to
# test.pypi.org.
# 2. When a 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: Build and release

on:
push:
release:
types: [created]

jobs:
build_sdist:
runs-on: 'ubuntu-latest'
name: Build sdist
steps:
- uses: actions/checkout@v2
- name: Setup python
uses: actions/setup-python@v2
with:
python-version: '3.8'
- name: Install dependencies
run: |
python3 -m pip install --upgrade pip
pip3 install tox
- name: Build sdist
run: tox -e build -- -s
- uses: actions/upload-artifact@v2
with:
name: dist
path: dist

build_manylinux_wheels:
runs-on: 'ubuntu-latest'
strategy:
fail-fast: false
matrix:
platform:
- 'manylinux1_x86_64'
- 'manylinux1_i686'
name: Build a ${{ matrix.platform }} for ${{ matrix.python_tag }}
steps:
- uses: actions/checkout@v2
- name: Install dependencies
run: sudo apt-get install docker
- name: Install docker image
run: |
DOCKER_IMAGE="quay.io/pypa/${{ matrix.platform }}"
echo "::set-env name=DOCKER_IMAGE::$DOCKER_IMAGE"
docker pull $DOCKER_IMAGE
- name: Build wheels
env:
PYTHON_TAGS: "cp38-cp38"
PRE_CMD: ${{ matrix.platform == 'manylinux1_i686' && 'linux32' || '' }}
run: |
docker run --rm \
-e PLAT=${{ matrix.platform }} \
-e PYTHON_TAGS=$PYTHON_TAGS \
-v `pwd`:/io $DOCKER_IMAGE \
$PRE_CMD \
/io/scripts/build_manylinux_wheels.sh
- uses: actions/upload-artifact@v2
with:
name: dist
path: dist

build_wheel:
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
python_version: [ '3.8' ]
arch: [ 'x86', 'x64' ]
os:
- 'windows-latest'
- 'macos-latest'
exclude:
- os: 'macos-latest'
arch: 'x86'

name: 'Build wheel: ${{ matrix.os }} ${{ matrix.python_version }} (${{ matrix.arch }})'
steps:
- uses: actions/checkout@v2
- name: Add msbuild to PATH
uses: microsoft/[email protected]
if: matrix.os == 'windows-latest'
- name: Setup python
uses: actions/setup-python@v1
with:
python-version: ${{ matrix.python_version }}
architecture: ${{ matrix.arch }}
- name: Install dependencies
run: |
python -m pip install -U pip
pip install -U tox
- name: Create tox environment
run: tox -e build --notest
- name: Build wheel
run: |
tox -e build -- -b
- uses: actions/upload-artifact@v2
with:
name: dist
path: dist

deploy:
runs-on: 'ubuntu-latest'
needs: [build_sdist, build_wheel, build_manylinux_wheels]
steps:
- uses: actions/checkout@v2
- name: Set up Python
uses: actions/setup-python@v1
with:
python-version: '3.x'
- uses: actions/download-artifact@v2
with:
name: dist
path: dist
- name: Install dependencies
run: |
python3 -m pip install --upgrade pip
pip3 install tox
- name: Check that version and tag matches
if: >-
startsWith(github.ref, 'refs/tags')
run: tox -e check-version-tag
- name: Run twine check
run: tox -e build-check
- name: Publish package
if: >-
(github.event_name == 'push' && startsWith(github.ref, 'refs/tags')) ||
(github.event_name == 'release')
env:
TWINE_USERNAME: "__token__"
run: |
if [[ "$GITHUB_EVENT_NAME" == "push" ]]; then
export TWINE_REPOSITORY_URL="https://test.pypi.org/legacy/"
export TWINE_PASSWORD="${{ secrets.TEST_PYPI_UPLOAD_TOKEN }}"
elif [[ "$GITHUB_EVENT_NAME" == "release" ]]; then
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
1 change: 1 addition & 0 deletions .github/workflows/python-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ jobs:
CFLAGS+=" -Wno-unused-parameter"
CFLAGS+=" -Wno-missing-field-initializers"
export CFLAGS="${CFLAGS}"
TOXENV="build,build-check"
fi
if [[ $TOXENV == "docs" ]]; then
Expand Down
1 change: 1 addition & 0 deletions docs/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ Contents:
:maxdepth: 1

zoneinfo
maintaining


Indices and tables
Expand Down
67 changes: 67 additions & 0 deletions docs/maintaining.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
Maintainer's Guide
==================

Although this was the original implementation of the ``zoneinfo`` module, after
Python 3.9, it is now a backport, and to the extent that there is a "canonical"
repository, the `CPython repository <https://github.com/python/cpython>`_ has a
stronger claim than this one. Accepting outside PRs against this repository is
difficult because we are not set up to collect CLAs for CPython. It is easier
to accept PRs against CPython and import them here if possible.

The code layout is very different between the two, and unfortunately (partially
because of the different layouts, and the different module names), the code has
diverged, so keeping the two in sync is not as simple as copy-pasting one into
the other. For now, the two will need to be kept in sync manually.


Development environment
-----------------------

Maintenance scripts, releases, and tests are orchestrated using |tox|_
environments to manage the requirements of each script. The details of each
environment can be found in the ``tox.ini`` file in the repository root.

The repository also has pre-commit configured to automatically enforce various
code formatting rules on commit. To use it, install `pre-commit
<https://pre-commit.com/>`_ and run ``pre-commit install`` in the repository
root to install the git commit hooks.


Making a release
----------------

Releases are automated via the ``build-release.yml`` GitHub Actions workflow.
The project is built on every push; whenever a *tag* is pushed, the build
artifacts are released to `Test PyPI <https://test.pypi.org>`_, and when a
GitHub release is made, the project is built and released to `PyPI
<https://pypi.org>`_ (this is a workaround for the lack of "draft releases"
on PyPI, and the two actions can be unified when that feature is added).

To make a release:

1. Update the version number in ``src/backports/zoneinfo/_version.py`` and
make a PR (if you want to be cautious, start with a ``.devN`` release
intended only for PyPI).
2. Tag the repository with the current version – you can use the
``scripts/tag_release.sh`` script in the repository root to source the
version automatically from the current module version.
3. Push the tag to GitHub (e.g. ``git push upstream 0.1.0.dev0``). This will
trigger a release to Test PyPI. The PR does not need to be merged at this
point if you are only planning to release to TestPyPI, but any "test only"
tags should be deleted when the process is complete.
4. Wait for the GitHub action to succeed, then check the results on
https://test.pypi.org/project/backports.zoneinfo .
5. If everything looks good, go into the GitHub repository's `"releases" tab
<https://github.com/pganssle/zoneinfo/releases>`_ and click "Draft a new
release"; type the name of the tag into the box, fill out the remainder of
the form, and click "Publish". (Only do this step for non-dev releases).
6. Check that the release action has succeeded, then check that everything
looks OK on https://pypi.org/project/backports.zoneinfo/ .

If there's a problem with the release, make a post release by appending
``.postN`` to the current version, e.g. ``0.1.1`` → ``0.1.1.post0``. If the
problem is sufficiently serious, yank the broken version.

.. Links
.. |tox| replace:: ``tox``
.. _tox: https://tox.readthedocs.io/en/latest/
28 changes: 28 additions & 0 deletions scripts/build_manylinux_wheels.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
#!/bin/bash
set -e -x

function repair_wheel {
wheel=$1
if ! auditwheel show "$wheel"; then
echo "Skipping non-platform wheel $wheel"
else
auditwheel repair "$wheel" --plat "$PLAT" -w /io/wheelhouse
fi
}

cd /io/

for tag in $PYTHON_TAGS; do
PYBIN="/opt/python/$tag/bin/"
${PYBIN}/pip install tox
CFLAGS="-std=c99 -O3" ${PYBIN}/tox -e build -- -b
done

mv dist/ raw_wheels

for whl in raw_wheels/*.whl; do
repair_wheel "$whl"
done

mkdir dist
mv wheelhouse/*.whl dist/
19 changes: 19 additions & 0 deletions scripts/check_tag.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import subprocess
import sys

from backports.zoneinfo import __version__ as VERSION


def get_current_tag():
p = subprocess.run(
["git", "describe", "--tag"], check=True, stdout=subprocess.PIPE
)

return p.stdout.strip().decode()


if __name__ == "__main__":
tag = get_current_tag()
if tag != VERSION:
print(f"Tag does not match version: {tag!r} != {VERSION!r}")
sys.exit(1)
11 changes: 11 additions & 0 deletions scripts/tag_release.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
#!/usr/bin/bash
#
# Script to tag the repository with the current version of the library
set -e

VERSION_LINE=$(grep '__version__ =' 'src/backports/zoneinfo/_version.py' -m 1)
VERSION=$(echo "$VERSION_LINE" | sed 's/__version__ = "\([^"]\+\)"/\1/')
echo "Found version: $VERSION"

git tag -s -m "Version $VERSION" $VERSION || exit "Failed to tag!"
echo "Success"
32 changes: 30 additions & 2 deletions tox.ini
Original file line number Diff line number Diff line change
Expand Up @@ -122,10 +122,38 @@ commands =
[testenv:build]
description = Build a wheel and source distribution
skip_install = True
passenv = CFLAGS
passenv = *
deps =
pep517
commands =
python -m pep517.build {posargs} {toxinidir} -o {toxinidir}/dist

[testenv:build-check]
description = Build a wheel and source distribution
skip_install = True
deps =
twine
depends = build
commands =
twine check dist/*

[testenv:check-version-tag]
description = Ensure that the current version matches the current tag
deps =
commands =
python scripts/check_tag.py

[testenv:release]
description = Make a release; must be called after "build"
skip_install = True
deps =
twine
depends =
build
auditwheel
passenv =
TWINE_*
commands =
python -m pep517.build -s -b {toxinidir} -o {toxinidir}/dist
twine check {toxinidir}/dist/*
twine upload {toxinidir}/dist/* \
{posargs:-r {env:TWINE_REPOSITORY:testpypi} --non-interactive}

0 comments on commit 0221430

Please sign in to comment.