| # Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html |
| # For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE |
| # Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt |
| |
| """This script updates towncrier.toml and creates a new newsfile and intermediate |
| folders if necessary. |
| """ |
| from __future__ import annotations |
| |
| import argparse |
| import re |
| from pathlib import Path |
| from subprocess import check_call |
| |
| NEWSFILE_PATTERN = re.compile(r"doc/whatsnew/\d/\d.\d+/index\.rst") |
| NEWSFILE_PATH = "doc/whatsnew/{major}/{major}.{minor}/index.rst" |
| TOWNCRIER_CONFIG_FILE = Path("towncrier.toml") |
| TOWNCRIER_VERSION_PATTERN = re.compile(r"version = \"(\d+\.\d+\.\d+)\"") |
| |
| NEWSFILE_CONTENT_TEMPLATE = """ |
| *************************** |
| What's New in Pylint {major}.{minor} |
| *************************** |
| |
| .. toctree:: |
| :maxdepth: 2 |
| |
| :Release:{major}.{minor} |
| :Date: TBA |
| |
| Summary -- Release highlights |
| ============================= |
| |
| |
| .. towncrier release notes start |
| """ |
| |
| |
| def main() -> None: |
| parser = argparse.ArgumentParser() |
| parser.add_argument("version", help="The new version to set") |
| parser.add_argument( |
| "--dry-run", |
| action="store_true", |
| help="Just show what would be done, don't write anything", |
| ) |
| args = parser.parse_args() |
| |
| if "dev" in args.version: |
| print("'-devXY' will be cut from version in towncrier.toml") |
| match = re.match( |
| r"^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(-?\w+\d*)*", args.version |
| ) |
| if not match: |
| print( |
| "Fatal error - new version did not match the " |
| "expected format (major.minor.patch[.*]). Abort!" |
| ) |
| return |
| major, minor, patch, suffix = match.groups() |
| new_version = f"{major}.{minor}.{patch}" |
| |
| new_newsfile = NEWSFILE_PATH.format(major=major, minor=minor) |
| create_new_newsfile_if_necessary(new_newsfile, major, minor, args.dry_run) |
| patch_towncrier_toml(new_newsfile, new_version, args.dry_run) |
| build_changelog(suffix, args.dry_run) |
| |
| |
| def create_new_newsfile_if_necessary( |
| new_newsfile: str, major: str, minor: str, dry_run: bool |
| ) -> None: |
| new_newsfile_path = Path(new_newsfile) |
| if new_newsfile_path.exists(): |
| return |
| |
| # create new file and add boiler plate content |
| if dry_run: |
| print( |
| f"Dry run enabled - would create file {new_newsfile} " |
| "and intermediate folders" |
| ) |
| return |
| |
| print("Creating new newsfile:", new_newsfile) |
| new_newsfile_path.parent.mkdir(parents=True, exist_ok=True) |
| new_newsfile_path.touch() |
| new_newsfile_path.write_text( |
| NEWSFILE_CONTENT_TEMPLATE.format(major=major, minor=minor), |
| encoding="utf8", |
| ) |
| |
| # tbump does not add and commit new files, so we add it ourselves |
| print("Adding new newsfile to git") |
| check_call(["git", "add", new_newsfile]) |
| |
| |
| def patch_towncrier_toml(new_newsfile: str, version: str, dry_run: bool) -> None: |
| file_content = TOWNCRIER_CONFIG_FILE.read_text(encoding="utf-8") |
| patched_newsfile_path = NEWSFILE_PATTERN.sub(new_newsfile, file_content) |
| new_file_content = TOWNCRIER_VERSION_PATTERN.sub( |
| f'version = "{version}"', patched_newsfile_path |
| ) |
| if dry_run: |
| print("Dry run enabled - this is what I would write:\n") |
| print(new_file_content) |
| return |
| TOWNCRIER_CONFIG_FILE.write_text(new_file_content, encoding="utf-8") |
| |
| |
| def build_changelog(suffix: str | None, dry_run: bool) -> None: |
| if suffix: |
| print("Not a release version, skipping changelog generation") |
| return |
| |
| if dry_run: |
| print("Dry run enabled - not building changelog") |
| return |
| |
| print("Building changelog") |
| check_call(["towncrier", "build", "--yes"]) |
| |
| |
| if __name__ == "__main__": |
| main() |