Skip to content

Commit

Permalink
Move developmental release version generation out of CI file
Browse files Browse the repository at this point in the history
  • Loading branch information
movermeyer committed Oct 13, 2023
1 parent d0adbc1 commit 3303a26
Show file tree
Hide file tree
Showing 6 changed files with 132 additions and 31 deletions.
30 changes: 17 additions & 13 deletions .github/workflows/publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ on:
workflow_dispatch:
inputs:
requested_release_tag:
description: 'The tag to use for this developmental release (without `.dev` suffix) (e.g., `v2.0.1`)'
description: "The tag to use for this developmental release (without `.dev` suffix) (e.g., `v2.0.1`)"
required: true

jobs:
Expand All @@ -18,7 +18,7 @@ jobs:
- uses: actions/setup-python@v4
name: Install Python
with:
python-version: '3.12'
python-version: "3.12"

- run: |
pip install packaging
Expand Down Expand Up @@ -62,14 +62,10 @@ jobs:
run: |
[[ ${{ env.version_from_release_tag }} == ${{ env.backports_version }} ]]
- name: Get the latest developmental release for this version from Test PyPI
run: |
curl https://test.pypi.org/pypi/backports-datetime-fromisoformat/json | python -c 'import json, sys; from packaging import version; contents=sys.stdin.read(); parsed = json.loads(contents); new_release_number = version.parse("${{ env.version_from_release_tag }}"); existing_releases = [version.parse(x) for x in parsed["releases"].keys()]; existing_developmental_release_version = max([x for x in existing_releases if x.release == new_release_number.release and x.is_devrelease], default=version.parse("0.0.0")); print(f"test_pypi_developmental_version={existing_developmental_release_version}")' >> $GITHUB_ENV
- name: Generate the developmental release version
# If there is a developmental release in Test PyPI for the version in pyproject.toml, increment the number. Else 1. Save in $GITHUB_ENV
- name: Generate the next developmental release version
# If there is a developmental release in Test PyPI for the requested version, increment the number. Else 1. Save in $GITHUB_ENV
run: |
python -c 'from packaging import version; new = version.parse("${{ env.version_from_release_tag }}"); existing = version.parse("${{ env.test_pypi_developmental_version }}"); dev_number = existing.dev + 1 if existing.is_devrelease and new.release == existing.release else 1; epoch = f"{new.epoch}!" if new.epoch else ""; release = ".".join([str(x) for x in new.release]); pre = f"{new.pre[0]}{new.pre[1]}" if new.pre else ""; post = f".post{new.post}" if new.post else ""; dev = f".dev{dev_number}"; developmental_release_version=f"{epoch}{release}{pre}{post}{dev}"; print(f"developmental_release_version={developmental_release_version}")' >> $GITHUB_ENV
curl https://test.pypi.org/pypi/backports-datetime-fromisoformat/json | python release/developmental_release.py ${{ env.version_from_release_tag }} | sed 's/^/developmental_release_version=/' >> $GITHUB_ENV
- name: Determine which version to use
run: echo "version_to_use=`if [ '${{ github.event_name }}' == 'workflow_dispatch' ]; then echo '${{ env.developmental_release_version }}'; else echo '${{ env.version_from_release_tag }}'; fi`" >> $GITHUB_ENV
Expand All @@ -80,7 +76,6 @@ jobs:
echo 'release event tag `${{ env.release_tag }}`'
echo 'release event version `${{ env.version_from_release_tag }}`'
echo 'Version in pyproject.toml `${{ env.backports_version }}`'
echo 'Developmental release version in Test PyPI `${{ env.test_pypi_developmental_version }}`'
echo 'New developmental version `${{ env.developmental_release_version }}`'
echo 'Version to use `${{ env.version_to_use }}`'
Expand Down Expand Up @@ -182,7 +177,7 @@ jobs:
- uses: actions/setup-python@v4
name: Install Python
with:
python-version: '3.12'
python-version: "3.12"

- uses: actions/download-artifact@v3
with:
Expand Down Expand Up @@ -212,7 +207,8 @@ jobs:
path: dist/*.tar.gz

publish_to_test_pypi:
needs: [pre_build_sanity_check, build_wheels, build_wheels_windows, build_sdist]
needs:
[pre_build_sanity_check, build_wheels, build_wheels_windows, build_sdist]
runs-on: ubuntu-latest

steps:
Expand Down Expand Up @@ -302,7 +298,15 @@ jobs:
publish:
if: github.event_name == 'release'
needs: [pre_build_sanity_check, build_wheels, build_wheels_windows, build_sdist, publish_to_test_pypi, pre_publish_sanity_check]
needs:
[
pre_build_sanity_check,
build_wheels,
build_wheels_windows,
build_sdist,
publish_to_test_pypi,
pre_publish_sanity_check,
]
runs-on: ubuntu-latest

steps:
Expand Down
3 changes: 2 additions & 1 deletion docs/RELEASING.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,4 +59,5 @@ Once you have sucessfully uploaded and tested a developmental release, it's time

## What if something went wrong with the final release?

TODO.
1. Debug and correct the problem.
2. Publish a new release with an incremented version number
43 changes: 26 additions & 17 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,27 +1,25 @@
[project]
name = "backports-datetime-fromisoformat"
version = "2.0.1"
authors = [
{ name="Michael Overmeyer", email="[email protected]" },
]
authors = [{ name = "Michael Overmeyer", email = "[email protected]" }]
description = "Backport of Python 3.11's datetime.fromisoformat"
readme = "README.rst"
requires-python = ">3"
license = {file = "LICENSE"}
license = { file = "LICENSE" }
classifiers = [
'Intended Audience :: Developers',
'License :: OSI Approved :: MIT License',
'Operating System :: OS Independent',
'Programming Language :: Python',
'Programming Language :: Python :: 3',
'Programming Language :: Python :: 3.4',
'Programming Language :: Python :: 3.5',
'Programming Language :: Python :: 3.6',
'Programming Language :: Python :: 3.7',
'Programming Language :: Python :: 3.8',
'Programming Language :: Python :: 3.9',
'Programming Language :: Python :: 3.10',
'Topic :: Software Development :: Libraries :: Python Modules',
'Intended Audience :: Developers',
'License :: OSI Approved :: MIT License',
'Operating System :: OS Independent',
'Programming Language :: Python',
'Programming Language :: Python :: 3',
'Programming Language :: Python :: 3.4',
'Programming Language :: Python :: 3.5',
'Programming Language :: Python :: 3.6',
'Programming Language :: Python :: 3.7',
'Programming Language :: Python :: 3.8',
'Programming Language :: Python :: 3.9',
'Programming Language :: Python :: 3.10',
'Topic :: Software Development :: Libraries :: Python Modules',
]

[project.urls]
Expand All @@ -34,3 +32,14 @@ Changelog = "https://github.com/movermeyer/backports.datetime_fromisoformat/CHAN
[build-system]
requires = ["setuptools"]
build-backend = "setuptools.build_meta"

[tool.pylint.'MESSAGES CONTROL']
max-line-length = 120
disable = "C0114, C0115, C0116, C0301"

[tool.autopep8]
max_line_length = 120
ignore = ["E501"]
in-place = true
recursive = true
aggressive = 3
Empty file added release/__init__.py
Empty file.
47 changes: 47 additions & 0 deletions release/developmental_release.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import argparse
import json
import sys

from packaging.version import parse, Version
from typing import Iterator

def _releases(contents) -> Iterator[Version]:
parsed = json.loads(contents)
existing_releases = [parse(x) for x in parsed["releases"].keys()]
return existing_releases

def non_developmental_version(version: Version):
if not version.is_devrelease:
return version

epoch = f"{version.epoch}!" if version.epoch else ""
release = ".".join([str(x) for x in version.release])
pre = f"{version.pre[0]}{version.pre[1]}" if version.pre else ""
post = f".post{version.post}" if version.post else ""
return parse(f"{epoch}{release}{pre}{post}")


def new_developmental_release_version(new: Version, existing_releases: Iterator[Version]) -> Version:
existing = max([x for x in existing_releases if x.is_devrelease and non_developmental_version(x) == new], default=parse("0.0.0"))
dev_number = existing.dev + 1 if existing.is_devrelease and new.release == existing.release else 1

epoch = f"{new.epoch}!" if new.epoch else ""
release = ".".join([str(x) for x in new.release])
pre = f"{new.pre[0]}{new.pre[1]}" if new.pre else ""
post = f".post{new.post}" if new.post else ""
dev = f".dev{dev_number}"
return parse(f"{epoch}{release}{pre}{post}{dev}")


if __name__ == '__main__':
parser = argparse.ArgumentParser(
description='Generates the next developmental release version number based on existing releases'
)

parser.add_argument('new_version', help='The new version number you want to use (without any `.dev` suffix)')
args = parser.parse_args()

contents = sys.stdin.read()
existing_releases = _releases(contents)
dev_version = new_developmental_release_version(parse(args.new_version), existing_releases)
print(dev_version)
40 changes: 40 additions & 0 deletions release/test_developmental_release.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import itertools
import unittest

from packaging.version import parse, Version
from typing import Iterator

from developmental_release import new_developmental_release_version

def generate_versions() -> Iterator[Version]:
# https://peps.python.org/pep-0440/
epoch_options = ["", "1!"]
release_options = ['2', '2.0', '2.0.0', '2.0.0.0', '2.0.0.0.0']
pre_release_options = ["", 'a1', 'b1', 'rc1']
post_release_options = ["", ".post1"]
dev_options = ["", ".dev1"]
for v in (parse(f"{epoch}{release}{pre_release}{post_release}{dev}") for (epoch, release, pre_release, post_release, dev) in itertools.product(epoch_options, release_options, pre_release_options, post_release_options, dev_options)):
yield v

class TestDevelopmentalVersionGeneration(unittest.TestCase):
def test_no_releases(self):
for version in generate_versions():
if version.is_devrelease:
continue
new_dev_version = new_developmental_release_version(version, [])
self.assertRegex(f"{new_dev_version}", r"\.dev1$", f"Input: {version}, Output: {new_dev_version}")

def test_existing_dev_release(self):
for version in generate_versions():
if version.is_devrelease:
continue
new_dev_version = new_developmental_release_version(version, [parse(f"{version}.dev1")])
self.assertRegex(f"{new_dev_version}", r"\.dev2$", f"Input: {version}, Output: {new_dev_version}")

def test_dev_release_exists_for_more_specific_version(self):
version = parse("2.0.0")
new_dev_version = new_developmental_release_version(version, [parse("2.0.0b1.dev1")])
self.assertRegex(f"{new_dev_version}", r"\.dev1$", f"Input: {version}, Output: {new_dev_version}")

if __name__ == '__main__':
unittest.main()

0 comments on commit 3303a26

Please sign in to comment.