Coverage for tsfpga/tools/version_number_handler.py: 0%

60 statements  

« prev     ^ index     » next       coverage.py v6.5.0, created at 2022-11-29 20:01 +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://gitlab.com/tsfpga/tsfpga 

7# -------------------------------------------------------------------------------------------------- 

8# A set of tools for versions and releases. Should be reusable between projects. 

9# -------------------------------------------------------------------------------------------------- 

10 

11# Standard libraries 

12import json 

13import re 

14import sys 

15from urllib.request import urlopen 

16 

17# Third party libraries 

18from packaging.version import parse 

19 

20# First party libraries 

21from tsfpga.system_utils import create_file, read_file 

22 

23# Contents of unreleased.rst when it is empty 

24UNRELEASED_EMPTY = "Nothing here yet.\n" 

25 

26 

27class VersionNumberHandler: 

28 

29 """ 

30 Class for handling the version number in __init__.py. 

31 """ 

32 

33 _version_regexp = re.compile(r"\n__version__ = \"(\S+?)\"\n") 

34 

35 def __init__(self, repo, version_file_path): 

36 """ 

37 Arguments: 

38 repo (`git.Repo`): The git repository to work with. 

39 version_file_path (pathlib.Path): The ``__init__.py`` file that shall be modified. 

40 """ 

41 self._repo = repo 

42 self._file_path = version_file_path 

43 

44 def update(self, new_version): 

45 """ 

46 Set a new version number. 

47 

48 Arguments: 

49 new_version (str): Version number. 

50 """ 

51 old_version = self._get_current_version() 

52 self._verify_that_newer_version_number_is_greater_than_older( 

53 older_version=old_version, newer_version=new_version 

54 ) 

55 

56 self._set_new_version(new_version) 

57 

58 def bump_to_prelease(self): 

59 """ 

60 Bump to next version number. E.g. go from 8.0.2 to 8.0.3-dev. 

61 """ 

62 current_version = self._get_current_version() 

63 (major, minor, patch) = current_version.release 

64 new_version = f"{major}.{minor}.{patch + 1}-dev" 

65 self._verify_that_newer_version_number_is_greater_than_older( 

66 older_version=current_version, newer_version=new_version 

67 ) 

68 

69 self._set_new_version(new_version) 

70 

71 @staticmethod 

72 def _verify_that_newer_version_number_is_greater_than_older(older_version, newer_version): 

73 if older_version >= parse(newer_version): 

74 sys.exit(f"New version {newer_version} is not greater than old version {older_version}") 

75 

76 def _get_current_version(self): 

77 data = read_file(self._file_path) 

78 

79 match = self._version_regexp.search(data) 

80 if match is None: 

81 raise RuntimeError(f"Could not find version value in {self._file_path}") 

82 

83 version = match.group(1) 

84 return parse(version) 

85 

86 def _set_new_version(self, new_version): 

87 data = read_file(self._file_path) 

88 

89 updated_data = self._version_regexp.sub(f'\n__version__ = "{new_version}"\n', data) 

90 create_file(self._file_path, updated_data) 

91 

92 # Add file so that it gets included in upcoming commit 

93 self._repo.index.add(str(self._file_path.resolve())) 

94 

95 

96def verify_new_version_number(repo, pypi_project_name, new_version, unreleased_notes_file): 

97 """ 

98 Verify that the new version number is sane for this project. Will check git log and PyPI 

99 release history. 

100 

101 Arguments: 

102 repo (`git.Repo`): The git repository to work with. 

103 pypi_project_name (str): The name of this project on PyPI. 

104 version (str): New version. 

105 unreleased_notes_file (pathlib.Path): Path to the "unreleased.rst" release notes file. 

106 Must not be empty. 

107 """ 

108 if repo.is_dirty(): 

109 sys.exit("Must make release from clean repo") 

110 

111 if read_file(unreleased_notes_file) in ["", UNRELEASED_EMPTY]: 

112 sys.exit(f"The unreleased notes file {unreleased_notes_file} should not be empty") 

113 

114 with urlopen(f"https://pypi.python.org/pypi/{pypi_project_name}/json") as file_handle: 

115 json_data = json.load(file_handle) 

116 if new_version in json_data["releases"]: 

117 sys.exit(f"Release {new_version} already exists in PyPI") 

118 

119 git_tag = f"v{new_version}" 

120 if git_tag in repo.tags: 

121 sys.exit(f"Git release tag already exists: {git_tag}") 

122 

123 return git_tag 

124 

125 

126def commit_and_tag_release(repo, version, git_tag): 

127 """ 

128 Make a git commit with a "release" message, and tag it. 

129 

130 Arguments: 

131 repo (`git.Repo`): The git repository to work with. 

132 version (str): New version. 

133 git_tag (str): New git tag. 

134 """ 

135 make_commit(repo=repo, commit_message=f"Release version {version}") 

136 

137 repo.create_tag(git_tag) 

138 if git_tag not in repo.tags: 

139 sys.exit("Git tag failed") 

140 

141 

142def make_commit(repo, commit_message): 

143 """ 

144 Make a git commit, and check that it worked. 

145 

146 Arguments: 

147 repo (`git.Repo`): The git repository to work with. 

148 commit_message (str): Commit message to use. 

149 """ 

150 repo.index.commit(commit_message) 

151 if repo.is_dirty(): 

152 sys.exit("Git commit failed")