Coverage for lobster/tools/core/online_report_nogit/online_report_nogit.py: 51%

47 statements  

« prev     ^ index     » next       coverage.py v7.10.7, created at 2026-09-22 04:43 +0000

1# LOBSTER - Lightweight Open BMW Software Traceability Evidence Report 

2# Copyright (C) 2025 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) 

3# 

4# This program is free software: you can redistribute it and/or modify 

5# it under the terms of the GNU Affero General Public License as 

6# published by the Free Software Foundation, either version 3 of the 

7# License, or (at your option) any later version. 

8# 

9# This program is distributed in the hope that it will be useful, but 

10# WITHOUT ANY WARRANTY; without even the implied warranty of 

11# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 

12# Affero General Public License for more details. 

13# 

14# You should have received a copy of the GNU Affero General Public 

15# License along with this program. If not, see 

16# <https://www.gnu.org/licenses/>. 

17 

18import argparse 

19import os 

20import sys 

21 

22from dataclasses import dataclass 

23from typing import Iterable, Optional, Sequence 

24from urllib.parse import quote 

25 

26from lobster.common.items import Item 

27from lobster.common.location import File_Reference, Github_Reference 

28from lobster.common.report import Report 

29from lobster.common.meta_data_tool_base import MetaDataToolBase 

30 

31 

32@dataclass 

33class RepoInfo: 

34 """ 

35 Data class to hold repository information. 

36 

37 Attributes: 

38 remote_url (str): The root URL of the GitHub repository. 

39 root (str): The local path to the root of the repository. 

40 commit (str): The commit hash to use when building a URL to a file. 

41 """ 

42 remote_url: str 

43 root: str 

44 commit: str 

45 

46 

47def _file_ref_to_github_ref( 

48 file_ref: File_Reference, 

49 repo_info: RepoInfo, 

50 paths_must_exist: bool, 

51) -> Github_Reference: 

52 """ 

53 Convert a File_Reference to a Github_Reference. 

54 

55 Args: 

56 file_ref (File_Reference): The file reference to convert (can be a directory). 

57 repo_data (RepoData): The repository meta information to use for the conversion. 

58 paths_must_exist (bool): If True, then a sanity check is performed. If the path 

59 is is not a directory and not a file, then a FileNotFoundError is raised. 

60 

61 Returns: 

62 Github_Reference: The converted GitHub reference. 

63 """ 

64 if (not paths_must_exist) \ 

65 or os.path.isfile(file_ref.filename) or os.path.isdir(file_ref.filename): 

66 return Github_Reference( 

67 gh_root=repo_info.remote_url, 

68 filename=quote( 

69 os.path.relpath( 

70 os.path.realpath(file_ref.filename), 

71 os.path.realpath(repo_info.root), 

72 ).replace(os.sep, "/") 

73 ), 

74 line=file_ref.line, 

75 commit=repo_info.commit, 

76 ) 

77 raise FileNotFoundError(f"File '{file_ref.filename}' does not exist.") 

78 

79 

80def _update_items(items: Iterable[Item], repo_info: RepoInfo, paths_must_exist: bool): 

81 for item in items: 

82 if isinstance(item.location, File_Reference): 

83 item.location = _file_ref_to_github_ref( 

84 item.location, 

85 repo_info, 

86 paths_must_exist, 

87 ) 

88 

89 

90def apply_github_urls( 

91 in_file: str, 

92 out_file: str, 

93 repository_info: RepoInfo, 

94 paths_must_exist: bool = True): 

95 """ 

96 Reads a report file, converts all file references to GitHub references, 

97 and saves the report. 

98 

99 Args: 

100 file (str): Path to the input LOBSTER report file. 

101 out_file (str): Output file for the updated LOBSTER report. 

102 repo_data (RepoData): object containing remote URL, root path, and commit hash. 

103 paths_must_exist (bool): If True, then a sanity check is performed. If the path 

104 is is not a directory and not a file, then a FileNotFoundError is raised. 

105 """ 

106 report = Report() 

107 report.load_report(in_file) 

108 _update_items(report.items.values(), repository_info, paths_must_exist) 

109 report.write_report(out_file) 

110 

111 

112class OnlineReportNogitTool(MetaDataToolBase): 

113 def __init__(self): 

114 super().__init__( 

115 name="lobster-online-report-nogit", 

116 description="Update file locations in LOBSTER report to GitHub references.", 

117 official=True, 

118 ) 

119 ap = self._argument_parser 

120 ap.add_argument(dest="report", 

121 metavar="LOBSTER_REPORT", 

122 help="Path to the input LOBSTER report file.") 

123 ap.add_argument("--repo-root", required=True, 

124 help="Local path to the root of the repository.") 

125 ap.add_argument("--remote-url", required=True, 

126 help="GitHub repository root URL.") 

127 ap.add_argument("--commit", required=True, 

128 help="Git commit hash to use for the references.") 

129 ap.add_argument("--out", required=True, metavar="OUTPUT_FILE", 

130 help="Output file for the updated LOBSTER report." 

131 "It can be the same as the input file in order to " 

132 "overwrite the input file.",) 

133 

134 def _run_impl(self, options: argparse.Namespace) -> int: 

135 try: 

136 apply_github_urls( 

137 in_file=options.report, 

138 repository_info=RepoInfo( 

139 remote_url=options.remote_url, 

140 root=options.repo_root, 

141 commit=options.commit, 

142 ), 

143 out_file=options.out, 

144 ) 

145 print(f"LOBSTER report {options.out} created, using remote URL references.") 

146 except FileNotFoundError as e: 

147 print( 

148 f"Error: {e}\n" 

149 f"Note: Relative paths are resolved with respect to the " 

150 f"current working directory '{os.getcwd()}'.", 

151 file=sys.stderr, 

152 ) 

153 return 1 

154 return 0 

155 

156 

157def main(args: Optional[Sequence[str]] = None) -> int: 

158 return OnlineReportNogitTool().run(args)