Coverage for tsfpga/tools/version_number_handler.py: 0%
62 statements
« prev ^ index » next coverage.py v7.3.2, created at 2023-12-10 20:51 +0000
« prev ^ index » next coverage.py v7.3.2, created at 2023-12-10 20:51 +0000
1# --------------------------------------------------------------------------------------------------
2# Copyright (c) Lukas Vik. All rights reserved.
3#
4# This file is part of the tsfpga project, a project platform for modern FPGA development.
5# https://tsfpga.com
6# https://github.com/tsfpga/tsfpga
7# --------------------------------------------------------------------------------------------------
8# A set of tools for versions and releases. Should be reusable between projects.
9# --------------------------------------------------------------------------------------------------
11# Standard libraries
12import json
13import re
14import sys
15from pathlib import Path
16from urllib.request import urlopen
18# Third party libraries
19from git.repo import Repo
20from packaging.version import Version, parse
22# First party libraries
23from tsfpga.system_utils import create_file, read_file
25# Contents of unreleased.rst when it is empty
26UNRELEASED_EMPTY = "Nothing here yet.\n"
29class VersionNumberHandler:
31 """
32 Class for handling the version number in __init__.py.
33 """
35 _version_regexp = re.compile(r"\n__version__ = \"(\S+?)\"\n")
37 def __init__(self, repo: Repo, version_file_path: Path) -> None:
38 """
39 Arguments:
40 repo: The git repository to work with.
41 version_file_path: The ``__init__.py`` file that shall be modified.
42 """
43 self._repo = repo
44 self._file_path = version_file_path
46 def update(self, new_version: str) -> None:
47 """
48 Set a new version number.
50 Arguments:
51 new_version: New version number as a string, e.g. "2.3.1".
52 """
53 old_version = self._get_current_version()
54 self._verify_that_newer_version_number_is_greater_than_older(
55 older_version=old_version, newer_version=new_version
56 )
58 self._set_new_version(new_version)
60 def bump_to_prelease(self) -> None:
61 """
62 Bump to next version number. E.g. go from 8.0.2 to 8.0.3-dev.
63 """
64 current_version = self._get_current_version()
65 (current_major, current_minor, current_patch) = current_version.release
67 new_version = f"{current_major}.{current_minor}.{current_patch + 1}-dev"
68 self._verify_that_newer_version_number_is_greater_than_older(
69 older_version=current_version, newer_version=new_version
70 )
72 self._set_new_version(new_version)
74 @staticmethod
75 def _verify_that_newer_version_number_is_greater_than_older(
76 older_version: Version, newer_version: str
77 ) -> None:
78 if older_version >= parse(newer_version):
79 sys.exit(f"New version {newer_version} is not greater than old version {older_version}")
81 def _get_current_version(self) -> Version:
82 data = read_file(self._file_path)
84 match = self._version_regexp.search(data)
85 if match is None:
86 raise RuntimeError(f"Could not find version value in {self._file_path}")
88 version = match.group(1)
89 return parse(version)
91 def _set_new_version(self, new_version: str) -> None:
92 data = read_file(self._file_path)
94 updated_data = self._version_regexp.sub(f'\n__version__ = "{new_version}"\n', data)
95 create_file(self._file_path, updated_data)
97 # Add file so that it gets included in upcoming commit
98 self._repo.index.add(str(self._file_path.resolve()))
101def verify_new_version_number(
102 repo: Repo, pypi_project_name: str, new_version: str, unreleased_notes_file: Path
103) -> str:
104 """
105 Verify that the new version number is sane for this project. Will check git log and PyPI
106 release history.
108 Arguments:
109 repo: The git repository to work with.
110 pypi_project_name: The name of this project on PyPI.
111 version: New version.
112 unreleased_notes_file: Path to the "unreleased.rst" release notes file.
113 Must not be empty.
115 Return:
116 The name of the git tag that corresponds to the new version number.
117 """
118 if repo.is_dirty():
119 sys.exit("Must make release from clean repo")
121 if read_file(unreleased_notes_file) in ["", UNRELEASED_EMPTY]:
122 sys.exit(f"The unreleased notes file {unreleased_notes_file} should not be empty")
124 with urlopen(f"https://pypi.python.org/pypi/{pypi_project_name}/json") as file_handle:
125 json_data = json.load(file_handle)
126 if new_version in json_data["releases"]:
127 sys.exit(f"Release {new_version} already exists in PyPI")
129 git_tag = f"v{new_version}"
130 if git_tag in repo.tags:
131 sys.exit(f"Git release tag already exists: {git_tag}")
133 return git_tag
136def commit_and_tag_release(repo: Repo, version: str, git_tag: str) -> None:
137 """
138 Make a git commit with a "release" message, and tag it.
140 Arguments:
141 repo: The git repository to work with.
142 version: New version.
143 git_tag: New git tag.
144 """
145 make_commit(repo=repo, commit_message=f"Release version {version}")
147 repo.create_tag(git_tag)
148 if git_tag not in repo.tags:
149 sys.exit("Git tag failed")
152def make_commit(repo: Repo, commit_message: str) -> None:
153 """
154 Make a git commit, and check that it worked.
156 Arguments:
157 repo (`git.Repo`): The git repository to work with.
158 commit_message (str): Commit message to use.
159 """
160 repo.index.commit(commit_message)
161 if repo.is_dirty():
162 sys.exit("Git commit failed")