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
« prev ^ index » next coverage.py v7.6.0, created at 2025-01-27 13:59 +0000
1import re
2from typing import Iterable, Tuple, Optional
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
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)
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 )
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)
61 def describe_match_short(self) -> str:
62 return f"All files beneath {self._directory}/ except .../<pkg>/examples"
64 def describe_match_exact(self) -> str:
65 return self.describe_match_short()
68class _ShebangScriptFiles(MatchRule):
69 def __init__(self) -> None:
70 super().__init__(MatchRuleType.GENERIC_GLOB)
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
84 @property
85 def path_type(self) -> Optional[PathType]:
86 return PathType.FILE
88 def _full_pattern(self) -> str:
89 return "built-in - not a valid pattern"
91 def describe_match_short(self) -> str:
92 return "All scripts with a absolute #!-line for /(s)bin or /usr/(s)bin"
94 def describe_match_exact(self) -> str:
95 return self.describe_match_short()
98USR_SHARE_DOC_MATCH_RULE = _UsrShareDocMatchRule()
99SHEBANG_SCRIPTS = _ShebangScriptFiles()
100del _UsrShareDocMatchRule
101del _ShebangScriptFiles
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 )
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 )
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 )
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 )
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 )
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 )
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 )
217 yield (
218 USR_SHARE_DOC_MATCH_RULE,
219 OctalMode(0o0644),
220 )
222 perl_config_data = resolve_perl_config(dpkg_architecture_variables, dctrl_bin)
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 )
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 )
247 yield (
248 SHEBANG_SCRIPTS,
249 _PATH_FILE_MODE,
250 )
252 yield (
253 MATCH_ANYTHING,
254 SymbolicMode.parse_filesystem_mode(
255 "go=rX,u+rw,a-s",
256 attribute_path["**/*"],
257 ),
258 )