Skip to content

Commit b391458

Browse files
authored
Merge pull request #20 from python/version_file
Change version bumping scheme
2 parents ba1680f + fc53618 commit b391458

11 files changed

+260
-7
lines changed

.pre-commit-config.yaml

-1
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@ repos:
1616
rev: v2.4.0
1717
hooks:
1818
- id: trailing-whitespace
19-
- id: end-of-file-fixer
2019
- id: debug-statements
2120

2221
- repo: https://github.com/asottile/setup-cfg-fmt

MANIFEST.in

+1
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
11
recursive-include src/tzdata *
2+
include VERSION

VERSION

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
2020.1rc0

bump_version.py

+111
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
import argparse
2+
import io
3+
import pathlib
4+
5+
import parver
6+
7+
REPO_ROOT = pathlib.Path(__file__).parent
8+
VERSION = REPO_ROOT / pathlib.Path("VERSION")
9+
10+
11+
def get_current_version() -> parver.Version:
12+
with open(VERSION, "rt") as f:
13+
return parver.Version.parse(f.read().strip())
14+
15+
16+
def write_version(version: parver.Version):
17+
with open(VERSION, "wt") as f:
18+
f.write(str(version))
19+
20+
21+
def update_package_version(version: parver.Version):
22+
new_init = io.StringIO()
23+
version_set = False
24+
init = REPO_ROOT / "src" / "tzdata" / "__init__.py"
25+
with open(init, "rt") as f:
26+
for line in f:
27+
if not version_set and line.startswith("__version__"):
28+
line = f'__version__ = "{version}"\n'
29+
version_set = True
30+
new_init.write(line)
31+
32+
if not version_set:
33+
raise ValueError("Version not found in __init__.py!")
34+
35+
new_init.seek(0)
36+
37+
with open(init, "wt") as f:
38+
f.write(new_init.read())
39+
40+
41+
def bump_version(version: parver.Version, args) -> parver.Version:
42+
if args.release:
43+
return version.base_version()
44+
45+
if args.dev:
46+
if args.to is not None:
47+
return version.replace(dev=args.to)
48+
else:
49+
return version.bump_dev()
50+
51+
version = version.replace(dev=None)
52+
53+
if args.post:
54+
if args.to is not None:
55+
return version.replace(post=args.to)
56+
else:
57+
return version.bump_post()
58+
59+
if args.rc:
60+
if version.is_postrelease:
61+
version = version.replace(post=None)
62+
63+
if args.to is not None:
64+
return version.replace(pre_tag="rc", pre=args.to)
65+
else:
66+
return version.bump_pre("rc")
67+
68+
69+
def main(args):
70+
original_version = get_current_version()
71+
bumped_version = bump_version(original_version, args)
72+
73+
print(f"{original_version}{bumped_version}")
74+
if not args.dry_run:
75+
write_version(bumped_version)
76+
update_package_version(bumped_version)
77+
78+
79+
if __name__ == "__main__":
80+
parser = argparse.ArgumentParser(description="Manipulate the package version")
81+
82+
group = parser.add_mutually_exclusive_group(required=True)
83+
84+
group.add_argument("--rc", action="store_true", help="Bump the release candidate")
85+
group.add_argument("--dev", action="store_true", help="Bump the dev version")
86+
group.add_argument(
87+
"--release",
88+
action="store_true",
89+
help="Bump from release candidate / dev to release",
90+
)
91+
group.add_argument(
92+
"--post", action="store_true", help="Bump the post release version"
93+
)
94+
parser.add_argument(
95+
"--to",
96+
type=int,
97+
default=None,
98+
help="Set the specified component to a specific number",
99+
)
100+
parser.add_argument(
101+
"--dry-run",
102+
action="store_true",
103+
help="Preview what the new version will be without writing any files.",
104+
)
105+
106+
args = parser.parse_args()
107+
108+
if args.to is not None and args.release:
109+
raise ValueError("Cannot combine --to and --release")
110+
111+
main(args)

docs/index.rst

+2-1
Original file line numberDiff line numberDiff line change
@@ -117,9 +117,10 @@ to package names:
117117
.. _zoneinfo: https://docs.python.org/3/library/zoneinfo.html
118118

119119
.. toctree::
120-
:maxdepth: 2
120+
:maxdepth: 1
121121
:caption: Contents:
122122

123+
maintaining
123124

124125

125126
Indices and tables

docs/maintaining.rst

+115
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
Maintainer's Guide
2+
==================
3+
4+
The ``tzdata`` repo is intended to be very low-maintenance and highly
5+
automated. This document serves as a reference guide for various maintenance
6+
actions that a maintainer may need to take. End users do not need to be
7+
concerned with the contents of this document.
8+
9+
Requirements
10+
------------
11+
12+
Maintenance scripts are orchestrated using |tox|_ environments to manage the
13+
requirements of each script. The details of each environment can be found in
14+
the ``tox.ini`` file in the repository root.
15+
16+
The repository also has pre-commit configured to automatically enforce various
17+
code formatting rules on commit. To use it, install `pre-commit
18+
<https://pre-commit.com/>`_ and run ``pre-commit install`` in the repository
19+
root to install the git commit hooks.
20+
21+
Updating to new tz releases
22+
---------------------------
23+
24+
When the ``update`` environment is run, it will automatically detect whether
25+
the current version of the IANA time zone database in the repository matches
26+
the latest release available from `iana.org
27+
<https://www.iana.org/time-zones>`_. If a new release is available, ``tox -e
28+
update`` will download and install it into the repository.
29+
30+
After running ``tox -e update``, the base version should be set to the current
31+
version of the upstream database, translated into :pep:`440`. The package
32+
version will start as a release candidate (``rc0``), and will need to be bumped
33+
to a full release before the final upload to PyPI. For example, when updating
34+
from 2019c to 2020a::
35+
36+
$ tox -e update -- --version=2020a
37+
...
38+
$ git diff VERSION
39+
-2019.3
40+
+2020.1rc0
41+
42+
Once this is done, commit all the changed or added files and make a pull
43+
request.
44+
45+
Updating the version
46+
--------------------
47+
48+
The canonical location for the version is the ``VERSION`` file in the
49+
repository root, and it is updated in two ways:
50+
51+
1. The "base version" (e.g. 2020.1) is set by ``tox -e update``.
52+
2. The additional markers such as pre (release candidate), post and dev are
53+
managed by ``tox -e bump_version``.
54+
55+
The version follows the scheme::
56+
57+
YYYY.minor[rcX][.postY][.devZ]
58+
59+
Bumping any component removes all values to its right as well, so for example,
60+
if the base version were ``20201rc1.post2.dev0``::
61+
62+
$ tox -e bump -- --dev --dry-run
63+
...
64+
2020.1rc1.post2.dev0 → 2020.1rc1.post2.dev1
65+
66+
$ tox -e bump -- --post --dry-run
67+
...
68+
2020.1rc1.post2.dev0 → 2020.1rc1.post3
69+
70+
$ tox -e bump -- --rc --dry-run
71+
...
72+
2020.1rc1.post2.dev0 → 2020.1rc2
73+
74+
To remove all additional markers and get a simple "release" version, use
75+
``--release``::
76+
77+
$ tox -e bump -- --release
78+
...
79+
2020.1rc1.post2.dev0 → 2020.1
80+
81+
For more information on how to use ``bump_version``, run ``tox -e bump_version
82+
-- -help``.
83+
84+
Making a release
85+
----------------
86+
87+
Release automation is done via the ``publish.yml`` GitHub Actions workflow,
88+
which is triggered whenever a new tag is pushed and whenever a new GitHub
89+
release is made. When a new tag is pushed, the project is built and released to
90+
`Test PyPI <https://test.pypi.org>`_, and when a GitHub release is made, the
91+
project is built and released to `PyPI <https://pypi.org>`_.
92+
93+
To make a release:
94+
95+
1. Tag the repository with the current version – you can use the
96+
``./tag_release.sh`` script in the repository root to source the version
97+
automatically from the current ``VERSION`` file.
98+
2. Wait for the GitHub action to succeed, then check the results on
99+
https://test.pypi.org/project/tzdata/ .
100+
3. If everything looks good, go into the GitHub repository's `"releases" tab
101+
<https://github.com/python/tzdata/releases>`_ and click "Create a new
102+
release"; type the name of the tag into the box, fill out the remainder of
103+
the form, and click "Publish".
104+
4. Check that the release action has succeeded, then check that everything looks
105+
OK on https://pypi.org/project/tzdata/ .
106+
107+
If there's a problem with the release, use ``tox -e bump -- --post`` to create
108+
a post release, and if it's sufficiently serious, yank the broken version.
109+
110+
It is recommended to start with a release candidate first, since even Test PyPI
111+
is immutable and each release burns a version number.
112+
113+
.. Links
114+
.. |tox| replace:: ``tox``
115+
.. _tox: https://tox.readthedocs.io/en/latest/

setup.cfg

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[metadata]
22
name = tzdata
3-
version = attr:tzdata.__version__
3+
version = file: VERSION
44
description = Provider of IANA time zone data
55
long_description = file: README.rst
66
long_description_content_type = text/x-rst

src/tzdata/__init__.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# IANA versions like 2020a are not valid PEP 440 identifiers; the recommended
2-
# way to translate the version is to use YYYY.n where `n` is a 1-based index.
3-
__version__ = "2020.1"
2+
# way to translate the version is to use YYYY.n where `n` is a 0-based index.
3+
__version__ = "2020.1rc0"
44

55
# This exposes the original IANA version number.
66
IANA_VERSION = "2020a"

tag_release.sh

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
#!/usr/bin/bash
2+
set -e
3+
VERSION=$(<VERSION)
4+
5+
echo "Tagging version $VERSION"
6+
git tag -s -m "Version $VERSION" $VERSION || exit "Failed to tag!"
7+
echo "Succeess"

tox.ini

+12
Original file line numberDiff line numberDiff line change
@@ -18,17 +18,29 @@ skip_install = True
1818
deps =
1919
requests
2020
click
21+
parver
2122
commands =
2223
python update.py {posargs}
2324

25+
[testenv:bump]
26+
description = Bump the current package version
27+
skip_install = True
28+
deps =
29+
parver
30+
commands =
31+
python bump_version.py {posargs}
32+
33+
2434
[testenv:pytype]
2535
description = Run typechecking
2636
basepython = python3.7
2737
skip_install = True
2838
deps =
2939
pytype
40+
parver
3041
commands =
3142
pytype {posargs} update.py
43+
pytype {posargs} bump_version.py
3244

3345
[testenv:format]
3446
description = Run auto formatters

update.py

+8-2
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
import typing
1111

1212
import click
13-
13+
import parver
1414
import requests
1515

1616
IANA_LATEST_LOCATION = "https://www.iana.org/time-zones/repository/tzdata-latest.tar.gz"
@@ -105,7 +105,10 @@ def create_package(
105105
version: str, zonenames: typing.List[str], zoneinfo_dir: pathlib.Path
106106
):
107107
"""Creates the tzdata package."""
108-
package_version = translate_version(version)
108+
# Start out at rc0
109+
base_version = parver.Version.parse(translate_version(version))
110+
rc_version = base_version.replace(pre_tag="rc", pre=0)
111+
package_version = str(rc_version)
109112

110113
# First remove the existing package contents
111114
target_dir = PKG_BASE / "tzdata"
@@ -126,6 +129,9 @@ def create_package(
126129
with open(target_dir / "__init__.py", "w") as f_out:
127130
f_out.write(contents)
128131

132+
with open(REPO_ROOT / "VERSION", "w") as f:
133+
f.write(package_version)
134+
129135
# Generate the "zones" file as a newline-delimited list
130136
with open(target_dir / "zones", "w") as f:
131137
f.write("\n".join(zonenames))

0 commit comments

Comments
 (0)