Skip to content

Commit 58a4af3

Browse files
Upgrade the release script using what was done in astroid
1 parent 31afab2 commit 58a4af3

File tree

1 file changed

+119
-40
lines changed

1 file changed

+119
-40
lines changed

script/bump_changelog.py

Lines changed: 119 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -4,76 +4,155 @@
44
"""
55
This script permits to upgrade the changelog in astroid or pylint when releasing a version.
66
"""
7+
# pylint: disable=logging-fstring-interpolation
78
import argparse
9+
import enum
10+
import logging
811
from datetime import datetime
912
from pathlib import Path
13+
from typing import List
1014

1115
DEFAULT_CHANGELOG_PATH = Path("ChangeLog")
12-
err = "in the changelog, fix that first!"
13-
TBA_ERROR_MSG = "More than one release date 'TBA' %s" % err
14-
NEW_VERSION_ERROR_MSG = "The text for this version '{version}' did not exists %s" % err
15-
NEXT_VERSION_ERROR_MSG = (
16-
"The text for the next version '{version}' already exists %s" % err
17-
)
1816

17+
RELEASE_DATE_TEXT = "Release date: TBA"
18+
WHATS_NEW_TEXT = "What's New in Pylint"
1919
TODAY = datetime.now()
20-
WHATS_NEW_TEXT = "What's New in astroid"
2120
FULL_WHATS_NEW_TEXT = WHATS_NEW_TEXT + " {version}?"
22-
RELEASE_DATE_TEXT = "Release Date: TBA"
23-
NEW_RELEASE_DATE_MESSAGE = "Release Date: {}".format(TODAY.strftime("%Y-%m-%d"))
21+
NEW_RELEASE_DATE_MESSAGE = "Release date: {}".format(TODAY.strftime("%Y-%m-%d"))
2422

2523

2624
def main() -> None:
2725
parser = argparse.ArgumentParser(__doc__)
2826
parser.add_argument("version", help="The version we want to release")
27+
parser.add_argument(
28+
"-v", "--verbose", action="store_true", default=False, help="Logging or not"
29+
)
2930
args = parser.parse_args()
30-
if "dev" not in args.version:
31-
version = args.version
32-
next_version = get_next_version(version)
33-
run(version, next_version)
31+
if args.verbose:
32+
logging.basicConfig(level=logging.DEBUG)
33+
logging.debug(f"Launching bump_changelog with args: {args}")
34+
if "dev" in args.version:
35+
return
36+
with open(DEFAULT_CHANGELOG_PATH) as f:
37+
content = f.read()
38+
content = transform_content(content, args.version)
39+
with open(DEFAULT_CHANGELOG_PATH, "w") as f:
40+
f.write(content)
41+
3442

43+
class VersionType(enum.Enum):
44+
MAJOR = 0
45+
MINOR = 1
46+
PATCH = 2
3547

36-
def get_next_version(version: str) -> str:
48+
49+
def get_next_version(version: str, version_type: VersionType) -> str:
3750
new_version = version.split(".")
38-
patch = new_version[2]
39-
reminder = None
40-
if "-" in patch:
41-
patch, reminder = patch.split("-")
42-
patch = str(int(patch) + 1)
43-
new_version[2] = patch if reminder is None else f"{patch}-{reminder}"
51+
part_to_increase = new_version[version_type.value]
52+
if "-" in part_to_increase:
53+
part_to_increase = part_to_increase.split("-")[0]
54+
for i in range(version_type.value, 3):
55+
new_version[i] = "0"
56+
new_version[version_type.value] = str(int(part_to_increase) + 1)
4457
return ".".join(new_version)
4558

4659

47-
def run(version: str, next_version: str) -> None:
48-
with open(DEFAULT_CHANGELOG_PATH) as f:
49-
content = f.read()
50-
content = transform_content(content, version, next_version)
51-
with open(DEFAULT_CHANGELOG_PATH, "w") as f:
52-
f.write(content)
60+
def get_next_versions(version: str, version_type: VersionType) -> List[str]:
61+
62+
if version_type == VersionType.PATCH:
63+
# "2.6.1" => ["2.6.2"]
64+
return [get_next_version(version, VersionType.PATCH)]
65+
if version_type == VersionType.MINOR:
66+
# "2.6.0" => ["2.7.0", "2.6.1"]
67+
assert version.endswith(".0"), f"{version} does not look like a minor version"
68+
else:
69+
# "3.0.0" => ["3.1.0", "3.0.1"]
70+
assert version.endswith(".0.0"), f"{version} does not look like a major version"
71+
next_minor_version = get_next_version(version, VersionType.MINOR)
72+
next_patch_version = get_next_version(version, VersionType.PATCH)
73+
logging.debug(f"Getting the new version for {version} - {version_type.name}")
74+
return [next_minor_version, next_patch_version]
5375

5476

55-
def transform_content(content: str, version: str, next_version: str) -> str:
56-
wn_new_version = FULL_WHATS_NEW_TEXT.format(version=version)
57-
wn_next_version = FULL_WHATS_NEW_TEXT.format(version=next_version)
77+
def get_version_type(version: str) -> VersionType:
78+
if version.endswith("0.0"):
79+
version_type = VersionType.MAJOR
80+
elif version.endswith("0"):
81+
version_type = VersionType.MINOR
82+
else:
83+
version_type = VersionType.PATCH
84+
return version_type
85+
86+
87+
def get_whats_new(
88+
version: str, add_date: bool = False, change_date: bool = False
89+
) -> str:
90+
whats_new_text = FULL_WHATS_NEW_TEXT.format(version=version)
91+
result = [whats_new_text, "=" * len(whats_new_text)]
92+
if add_date and change_date:
93+
result += [NEW_RELEASE_DATE_MESSAGE]
94+
elif add_date:
95+
result += [RELEASE_DATE_TEXT]
96+
elif change_date:
97+
raise ValueError("Can't use change_date=True with add_date=False")
98+
logging.debug(
99+
f"version='{version}', add_date='{add_date}', change_date='{change_date}': {result}"
100+
)
101+
return "\n".join(result)
102+
103+
104+
def get_all_whats_new(version: str, version_type: VersionType) -> str:
105+
result = ""
106+
for version_ in get_next_versions(version, version_type=version_type):
107+
result += get_whats_new(version_, add_date=True) + "\n" * 4
108+
return result
109+
110+
111+
def transform_content(content: str, version: str) -> str:
112+
version_type = get_version_type(version)
113+
next_version = get_next_version(version, version_type)
114+
old_date = get_whats_new(version, add_date=True)
115+
new_date = get_whats_new(version, add_date=True, change_date=True)
116+
next_version_with_date = get_all_whats_new(version, version_type)
117+
do_checks(content, next_version, version, version_type)
118+
index = content.find(old_date)
119+
logging.debug(f"Replacing\n'{old_date}'\nby\n'{new_date}'\n")
120+
content = content.replace(old_date, new_date)
121+
end_content = content[index:]
122+
content = content[:index]
123+
logging.debug(f"Adding:\n'{next_version_with_date}'\n")
124+
content += next_version_with_date + end_content
125+
return content
126+
127+
128+
def do_checks(content, next_version, version, version_type):
129+
err = "in the changelog, fix that first!"
130+
NEW_VERSION_ERROR_MSG = (
131+
"The text for this version '{version}' did not exists %s" % err
132+
)
133+
NEXT_VERSION_ERROR_MSG = (
134+
"The text for the next version '{version}' already exists %s" % err
135+
)
136+
wn_next_version = get_whats_new(next_version)
137+
wn_this_version = get_whats_new(version)
58138
# There is only one field where the release date is TBA
59-
assert content.count(RELEASE_DATE_TEXT) == 1, TBA_ERROR_MSG
139+
if version_type in [VersionType.MAJOR, VersionType.MINOR]:
140+
assert (
141+
content.count(RELEASE_DATE_TEXT) <= 1
142+
), f"There should be only one release date 'TBA' ({version}) {err}"
143+
else:
144+
next_minor_version = get_next_version(version, VersionType.MINOR)
145+
assert (
146+
content.count(RELEASE_DATE_TEXT) <= 2
147+
), f"There should be only two release dates 'TBA' ({version} and {next_minor_version}) {err}"
60148
# There is already a release note for the version we want to release
61-
assert content.count(wn_new_version) == 1, NEW_VERSION_ERROR_MSG.format(
149+
assert content.count(wn_this_version) == 1, NEW_VERSION_ERROR_MSG.format(
62150
version=version
63151
)
64152
# There is no release notes for the next version
65153
assert content.count(wn_next_version) == 0, NEXT_VERSION_ERROR_MSG.format(
66154
version=next_version
67155
)
68-
index = content.find(WHATS_NEW_TEXT)
69-
content = content.replace(RELEASE_DATE_TEXT, NEW_RELEASE_DATE_MESSAGE)
70-
end_content = content[index:]
71-
content = content[:index]
72-
content += wn_next_version + "\n"
73-
content += "=" * len(wn_next_version) + "\n"
74-
content += RELEASE_DATE_TEXT + "\n" * 4
75-
content += end_content
76-
return content
77156

78157

79158
if __name__ == "__main__":

0 commit comments

Comments
 (0)