Coverage for src/debputy/builtin_manifest_rules.py: 86%

79 statements  

« prev     ^ index     » next       coverage.py v7.2.7, created at 2024-04-07 12:14 +0200

1import re 

2from typing import Iterable, Tuple, Optional 

3 

4from debputy.architecture_support import DpkgArchitectureBuildProcessValuesTable 

5from debputy.exceptions import PureVirtualPathError, TestPathWithNonExistentFSPathError 

6from debputy.intermediate_manifest import PathType 

7from debputy.manifest_parser.base_types import SymbolicMode, OctalMode, FileSystemMode 

8from debputy.manifest_parser.util import AttributePath 

9from debputy.packages import BinaryPackage 

10from debputy.path_matcher import ( 

11 MATCH_ANYTHING, 

12 MatchRule, 

13 ExactFileSystemPath, 

14 DirectoryBasedMatch, 

15 MatchRuleType, 

16 BasenameGlobMatch, 

17) 

18from debputy.substitution import Substitution 

19from debputy.types import VP 

20from debputy.util import _normalize_path, perl_module_dirs 

21 

22# Imported from dh_fixperms 

23_PERMISSION_NORMALIZATION_SOURCE_DEFINITION = "permission normalization" 

24attribute_path = AttributePath.builtin_path()[ 

25 _PERMISSION_NORMALIZATION_SOURCE_DEFINITION 

26] 

27_STD_FILE_MODE = OctalMode(0o644) 

28_PATH_FILE_MODE = OctalMode(0o755) 

29_HAS_BIN_SHBANG_RE = re.compile(rb"^#!\s*/(?:usr/)?s?bin", re.ASCII) 

30 

31 

32class _UsrShareDocMatchRule(DirectoryBasedMatch): 

33 def __init__(self) -> None: 

34 super().__init__( 

35 MatchRuleType.ANYTHING_BENEATH_DIR, 

36 _normalize_path("usr/share/doc", with_prefix=True), 

37 path_type=PathType.FILE, 

38 ) 

39 

40 def finditer(self, fs_root: VP, *, ignore_paths=None) -> Iterable[VP]: 

41 doc_dir = fs_root.lookup(self._directory) 

42 if doc_dir is None: 

43 return 

44 for path_in_doc_dir in doc_dir.iterdir: 

45 if ignore_paths is not None and ignore_paths(path_in_doc_dir): 45 ↛ 46line 45 didn't jump to line 46, because the condition on line 45 was never true

46 continue 

47 if path_in_doc_dir.is_file: 47 ↛ 48line 47 didn't jump to line 48, because the condition on line 47 was never true

48 yield path_in_doc_dir 

49 for subpath in path_in_doc_dir.iterdir: 

50 if subpath.name == "examples" and subpath.is_dir: 50 ↛ 51line 50 didn't jump to line 51, because the condition on line 50 was never true

51 continue 

52 if ignore_paths is not None: 52 ↛ 59line 52 didn't jump to line 59, because the condition on line 52 was never false

53 yield from ( 

54 f 

55 for f in subpath.all_paths() 

56 if f.is_file and not ignore_paths(f) 

57 ) 

58 else: 

59 yield from (f for f in subpath.all_paths() if f.is_file) 

60 

61 def describe_match_short(self) -> str: 

62 return f"All files beneath {self._directory}/ except .../<pkg>/examples" 

63 

64 def describe_match_exact(self) -> str: 

65 return self.describe_match_short() 

66 

67 

68class _ShebangScriptFiles(MatchRule): 

69 def __init__(self) -> None: 

70 super().__init__(MatchRuleType.GENERIC_GLOB) 

71 

72 def finditer(self, fs_root: VP, *, ignore_paths=None) -> Iterable[VP]: 

73 for p in fs_root.all_paths(): 

74 if not p.is_file or (ignore_paths and ignore_paths(p)): 

75 continue 

76 try: 

77 with p.open(byte_io=True) as fd: 

78 c = fd.read(32) 

79 except (PureVirtualPathError, TestPathWithNonExistentFSPathError): 

80 continue 

81 if _HAS_BIN_SHBANG_RE.match(c): 

82 yield p 

83 

84 @property 

85 def path_type(self) -> Optional[PathType]: 

86 return PathType.FILE 

87 

88 def _full_pattern(self) -> str: 

89 return "built-in - not a valid pattern" 

90 

91 def describe_match_short(self) -> str: 

92 return "All scripts with a absolute #!-line for /(s)bin or /usr/(s)bin" 

93 

94 def describe_match_exact(self) -> str: 

95 return self.describe_match_short() 

96 

97 

98USR_SHARE_DOC_MATCH_RULE = _UsrShareDocMatchRule() 

99SHEBANG_SCRIPTS = _ShebangScriptFiles() 

100del _UsrShareDocMatchRule 

101del _ShebangScriptFiles 

102 

103 

104def builtin_mode_normalization_rules( 

105 dpkg_architecture_variables: DpkgArchitectureBuildProcessValuesTable, 

106 dctrl_bin: BinaryPackage, 

107 substitution: Substitution, 

108) -> Iterable[Tuple[MatchRule, FileSystemMode]]: 

109 yield from ( 

110 ( 

111 MatchRule.from_path_or_glob( 

112 x, 

113 _PERMISSION_NORMALIZATION_SOURCE_DEFINITION, 

114 path_type=PathType.FILE, 

115 ), 

116 _STD_FILE_MODE, 

117 ) 

118 for x in ( 

119 "*.so.*", 

120 "*.so", 

121 "*.la", 

122 "*.a", 

123 "*.js", 

124 "*.css", 

125 "*.scss", 

126 "*.sass", 

127 "*.jpeg", 

128 "*.jpg", 

129 "*.png", 

130 "*.gif", 

131 "*.cmxs", 

132 "*.node", 

133 ) 

134 ) 

135 

136 yield from ( 

137 ( 

138 MatchRule.recursive_beneath_directory( 

139 x, 

140 _PERMISSION_NORMALIZATION_SOURCE_DEFINITION, 

141 path_type=PathType.FILE, 

142 ), 

143 _STD_FILE_MODE, 

144 ) 

145 for x in ( 

146 "usr/share/man", 

147 "usr/include", 

148 "usr/share/applications", 

149 "usr/share/lintian/overrides", 

150 ) 

151 ) 

152 

153 # The dh_fixperms tool recuses for these directories, but probably should not (see #1006927) 

154 yield from ( 

155 ( 

156 MatchRule.from_path_or_glob( 

157 f"{x}/*", 

158 _PERMISSION_NORMALIZATION_SOURCE_DEFINITION, 

159 path_type=PathType.FILE, 

160 ), 

161 _PATH_FILE_MODE, 

162 ) 

163 for x in ( 

164 "usr/bin", 

165 "usr/bin/mh", 

166 "bin", 

167 "usr/sbin", 

168 "sbin", 

169 "usr/games", 

170 "usr/libexec", 

171 "etc/init.d", 

172 ) 

173 ) 

174 

175 yield ( 

176 # Strictly speaking, dh_fixperms does a recursive search but in practice, it does not matter. 

177 MatchRule.from_path_or_glob( 

178 "etc/sudoers.d/*", 

179 _PERMISSION_NORMALIZATION_SOURCE_DEFINITION, 

180 path_type=PathType.FILE, 

181 ), 

182 OctalMode(0o440), 

183 ) 

184 

185 # The reportbug rule 

186 yield ( 

187 ExactFileSystemPath( 

188 substitution.substitute( 

189 _normalize_path("usr/share/bug/{{PACKAGE}}"), 

190 _PERMISSION_NORMALIZATION_SOURCE_DEFINITION, 

191 ) 

192 ), 

193 OctalMode(0o755), 

194 ) 

195 

196 yield ( 

197 MatchRule.recursive_beneath_directory( 

198 "usr/share/bug/{{PACKAGE}}", 

199 _PERMISSION_NORMALIZATION_SOURCE_DEFINITION, 

200 path_type=PathType.FILE, 

201 substitution=substitution, 

202 ), 

203 OctalMode(0o644), 

204 ) 

205 

206 yield ( 

207 ExactFileSystemPath( 

208 substitution.substitute( 

209 _normalize_path("usr/share/bug/{{PACKAGE}}/script"), 

210 _PERMISSION_NORMALIZATION_SOURCE_DEFINITION, 

211 ) 

212 ), 

213 OctalMode(0o755), 

214 ) 

215 

216 yield ( 

217 USR_SHARE_DOC_MATCH_RULE, 

218 OctalMode(0o0644), 

219 ) 

220 

221 yield from ( 

222 ( 

223 BasenameGlobMatch( 

224 "*.pm", 

225 only_when_in_directory=perl_dir, 

226 path_type=PathType.FILE, 

227 recursive_match=True, 

228 ), 

229 SymbolicMode.parse_filesystem_mode( 

230 "a-x", 

231 attribute_path['"*.pm'], 

232 ), 

233 ) 

234 for perl_dir in perl_module_dirs(dpkg_architecture_variables, dctrl_bin) 

235 ) 

236 

237 yield ( 

238 BasenameGlobMatch( 

239 "*.ali", 

240 only_when_in_directory=_normalize_path("usr/lib"), 

241 path_type=PathType.FILE, 

242 recursive_match=True, 

243 ), 

244 SymbolicMode.parse_filesystem_mode( 

245 "a-w", 

246 attribute_path['"*.ali"'], 

247 ), 

248 ) 

249 

250 yield ( 

251 SHEBANG_SCRIPTS, 

252 _PATH_FILE_MODE, 

253 ) 

254 

255 yield ( 

256 MATCH_ANYTHING, 

257 SymbolicMode.parse_filesystem_mode( 

258 "go=rX,u+rw,a-s", 

259 attribute_path["**/*"], 

260 ), 

261 )