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

80 statements  

« prev     ^ index     » next       coverage.py v7.6.0, created at 2025-01-27 13:59 +0000

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, resolve_perl_config 

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 always true

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 "usr/share/themes", 

151 ) 

152 ) 

153 

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

155 yield from ( 

156 ( 

157 MatchRule.from_path_or_glob( 

158 f"{x}/*", 

159 _PERMISSION_NORMALIZATION_SOURCE_DEFINITION, 

160 path_type=PathType.FILE, 

161 ), 

162 _PATH_FILE_MODE, 

163 ) 

164 for x in ( 

165 "usr/bin", 

166 "usr/bin/mh", 

167 "bin", 

168 "usr/sbin", 

169 "sbin", 

170 "usr/games", 

171 "usr/libexec", 

172 "etc/init.d", 

173 ) 

174 ) 

175 

176 yield ( 

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

178 MatchRule.from_path_or_glob( 

179 "etc/sudoers.d/*", 

180 _PERMISSION_NORMALIZATION_SOURCE_DEFINITION, 

181 path_type=PathType.FILE, 

182 ), 

183 OctalMode(0o440), 

184 ) 

185 

186 # The reportbug rule 

187 yield ( 

188 ExactFileSystemPath( 

189 substitution.substitute( 

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

191 _PERMISSION_NORMALIZATION_SOURCE_DEFINITION, 

192 ) 

193 ), 

194 OctalMode(0o755), 

195 ) 

196 

197 yield ( 

198 MatchRule.recursive_beneath_directory( 

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

200 _PERMISSION_NORMALIZATION_SOURCE_DEFINITION, 

201 path_type=PathType.FILE, 

202 substitution=substitution, 

203 ), 

204 OctalMode(0o644), 

205 ) 

206 

207 yield ( 

208 ExactFileSystemPath( 

209 substitution.substitute( 

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

211 _PERMISSION_NORMALIZATION_SOURCE_DEFINITION, 

212 ) 

213 ), 

214 OctalMode(0o755), 

215 ) 

216 

217 yield ( 

218 USR_SHARE_DOC_MATCH_RULE, 

219 OctalMode(0o0644), 

220 ) 

221 

222 perl_config_data = resolve_perl_config(dpkg_architecture_variables, dctrl_bin) 

223 

224 yield from ( 

225 ( 

226 BasenameGlobMatch( 

227 "*.pm", 

228 only_when_in_directory=_normalize_path(perl_dir), 

229 path_type=PathType.FILE, 

230 recursive_match=True, 

231 ), 

232 _STD_FILE_MODE, 

233 ) 

234 for perl_dir in (perl_config_data.vendorlib, perl_config_data.vendorarch) 

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 OctalMode(0o444), 

245 ) 

246 

247 yield ( 

248 SHEBANG_SCRIPTS, 

249 _PATH_FILE_MODE, 

250 ) 

251 

252 yield ( 

253 MATCH_ANYTHING, 

254 SymbolicMode.parse_filesystem_mode( 

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

256 attribute_path["**/*"], 

257 ), 

258 )