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

61 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 methods for building sphinx docs. Should be reusable between projects. 

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

10 

11# Standard libraries 

12import sys 

13from datetime import datetime 

14from subprocess import check_call 

15 

16# Third party libraries 

17from git import Repo 

18from packaging.version import parse 

19 

20# First party libraries 

21from tsfpga.system_utils import read_file 

22 

23 

24def generate_release_notes(repo_root, release_notes_directory, project_name): 

25 """ 

26 Generate release notes in RST format based on a directory full of release note files. 

27 Will match each file to a git tag. 

28 

29 Arguments: 

30 repo_root (pathlib.Path): Git commands will be executed here. 

31 release_notes_directory (pathlib.Path): Location of release notes files. 

32 project_name (str): Name of project will be used for the gitlab link. 

33 

34 Return: 

35 str: RST code with release notes. 

36 """ 

37 rst = "" 

38 

39 for release, previous_release_git_tag in _get_release_notes_files( 

40 repo_root=repo_root, release_notes_directory=release_notes_directory 

41 ): 

42 heading = f"{release.version} ({release.date})" 

43 rst += heading + "\n" 

44 rst += "-" * len(heading) + "\n" 

45 rst += "\n" 

46 if previous_release_git_tag is not None: 

47 diff_url = ( 

48 f"https://gitlab.com/tsfpga/{project_name}/-/compare/" 

49 f"{previous_release_git_tag}...{release.git_tag}" 

50 ) 

51 rst += f"`Changes since previous release <{diff_url}>`__\n" 

52 rst += "\n" 

53 rst += read_file(release.release_notes_file) 

54 rst += "\n" 

55 

56 return rst 

57 

58 

59def _get_release_notes_files(repo_root, release_notes_directory): 

60 """ 

61 Iterate the release notes. 

62 """ 

63 unreleased_notes_file = release_notes_directory / "unreleased.rst" 

64 

65 release_notes = [] 

66 

67 # Get all versioned release notes files and sort them in order newest -> oldest 

68 for release_notes_file in release_notes_directory.glob("*.rst"): 

69 if not release_notes_file == unreleased_notes_file: 

70 release_notes.append(release_notes_file) 

71 

72 # Sort by parsing the version number in the file name. Newest to oldest. 

73 def sort_key(path): 

74 return parse(path.stem) 

75 

76 release_notes.sort(key=sort_key, reverse=True) 

77 

78 # The "Unreleased" shall be first 

79 release_notes.insert(0, unreleased_notes_file) 

80 

81 repo = Repo(repo_root) 

82 releases = [ 

83 Release(repo=repo, release_notes_file=release_notes_file) 

84 for release_notes_file in release_notes 

85 ] 

86 

87 for idx, release in enumerate(releases): 

88 if idx == len(releases) - 1: 

89 previous_release_git_tag = None 

90 else: 

91 previous_release_git_tag = releases[idx + 1].git_tag 

92 

93 yield release, previous_release_git_tag 

94 

95 

96class Release: 

97 """ 

98 Used to represent a release. 

99 """ 

100 

101 def __init__(self, repo, release_notes_file): 

102 self.release_notes_file = release_notes_file 

103 

104 version = release_notes_file.stem 

105 if version == "unreleased": 

106 self.version = "Unreleased" 

107 self.git_tag = "main" 

108 self.date = "YYYY-MM-DD" 

109 else: 

110 self.version = version 

111 self.git_tag = "v" + self.version 

112 self.date = self.get_git_date_from_tag(repo=repo, tag=self.git_tag) 

113 

114 @staticmethod 

115 def get_git_date_from_tag(repo, tag): 

116 """ 

117 Get a formatted date string, gathered from git log based on tag name. 

118 """ 

119 timestamp = repo.tag(f"refs/tags/{tag}").commit.committed_date 

120 time = datetime.fromtimestamp(timestamp) 

121 return f"{time.day} {time:%B} {time.year}".lower() 

122 

123 

124def build_sphinx(build_path, output_path): 

125 """ 

126 Execute sphinx on command line to build HTML documentation. 

127 

128 Arguments: 

129 build_path (pathlib.Path): The location that contains ``conf.py`` and ``index.rst``. 

130 output_path (pathlib.Path): Where to place the generated HTML. 

131 """ 

132 # Since we set the working directory when making the system call, the paths must be absolute 

133 build_path = build_path.resolve() 

134 output_path = output_path.resolve() 

135 

136 cmd = [ 

137 sys.executable, 

138 "-m", 

139 "sphinx", 

140 # Enable nitpicky mode 

141 "-n", 

142 # Turn errors into warnings 

143 "-W", 

144 # Show full traceback upon error 

145 "-T", 

146 str(build_path), 

147 str(output_path), 

148 ] 

149 check_call(cmd, cwd=build_path) 

150 

151 index_html = output_path / "index.html" 

152 assert index_html.exists(), index_html 

153 print(f"Open with:\nfirefox {index_html} &")