Coverage for src/debputy/plugins/debputy/debputy_plugin.py: 100%

86 statements  

« prev     ^ index     » next       coverage.py v7.8.2, created at 2026-04-19 20:37 +0000

1import textwrap 

2 

3from debputy.plugin.api import ( 

4 DebputyPluginInitializer, 

5 packager_provided_file_reference_documentation, 

6) 

7from debputy.plugins.debputy.metadata_detectors import ( 

8 detect_systemd_tmpfiles, 

9 detect_kernel_modules, 

10 detect_icons, 

11 detect_gsettings_dependencies, 

12 detect_xfonts, 

13 detect_initramfs_hooks, 

14 detect_systemd_sysusers, 

15 detect_pycompile_files, 

16 translate_capabilities, 

17 pam_auth_update, 

18 auto_depends_arch_any_solink, 

19 detect_commands, 

20 detect_built_using, 

21) 

22from debputy.plugins.debputy.paths import ( 

23 SYSTEMD_TMPFILES_DIR, 

24 INITRAMFS_HOOK_DIR, 

25 GSETTINGS_SCHEMA_DIR, 

26 SYSTEMD_SYSUSERS_DIR, 

27) 

28from debputy.plugins.debputy.private_api import initialize_via_private_api 

29from debputy.util import PackageTypeSelector 

30 

31 

32def initialize_debputy_features(api: DebputyPluginInitializer) -> None: 

33 initialize_via_private_api(api) 

34 declare_manifest_variables(api) 

35 register_packager_provided_files(api) 

36 register_package_metadata_detectors(api) 

37 

38 

39def declare_manifest_variables(api: DebputyPluginInitializer) -> None: 

40 api.manifest_variable( 

41 "path:BASH_COMPLETION_DIR", 

42 "/usr/share/bash-completion/completions", 

43 variable_reference_documentation="Directory to install bash completions into", 

44 ) 

45 api.manifest_variable( 

46 "path:FISH_COMPLETION_DIR", 

47 "/usr/share/fish/vendor_completions.d", 

48 variable_reference_documentation="Directory to install fish completions into", 

49 ) 

50 api.manifest_variable( 

51 "path:ZSH_COMPLETION_DIR", 

52 "/usr/share/zsh/vendor-completions", 

53 variable_reference_documentation="Directory to install zsh completions into", 

54 ) 

55 api.manifest_variable( 

56 "path:GNU_INFO_DIR", 

57 "/usr/share/info", 

58 variable_reference_documentation="Directory to install GNU INFO files into", 

59 ) 

60 

61 api.manifest_variable( 

62 "token:NL", 

63 "\n", 

64 variable_reference_documentation="Literal newline (linefeed) character", 

65 ) 

66 api.manifest_variable( 

67 "token:NEWLINE", 

68 "\n", 

69 variable_reference_documentation="Literal newline (linefeed) character", 

70 ) 

71 api.manifest_variable( 

72 "token:TAB", 

73 "\t", 

74 variable_reference_documentation="Literal tab character", 

75 ) 

76 api.manifest_variable( 

77 "token:OPEN_CURLY_BRACE", 

78 "{", 

79 variable_reference_documentation='Literal "{" character', 

80 ) 

81 api.manifest_variable( 

82 "token:CLOSE_CURLY_BRACE", 

83 "}", 

84 variable_reference_documentation='Literal "}" character', 

85 ) 

86 api.manifest_variable( 

87 "token:DOUBLE_OPEN_CURLY_BRACE", 

88 "{{", 

89 variable_reference_documentation='Literal "{{" character - useful to avoid triggering a substitution', 

90 ) 

91 api.manifest_variable( 

92 "token:DOUBLE_CLOSE_CURLY_BRACE", 

93 "}}", 

94 variable_reference_documentation='Literal "}}" string - useful to avoid triggering a substitution', 

95 ) 

96 

97 

98def register_package_metadata_detectors(api: DebputyPluginInitializer) -> None: 

99 api.metadata_or_maintscript_detector("systemd-tmpfiles", detect_systemd_tmpfiles) 

100 api.metadata_or_maintscript_detector("systemd-sysusers", detect_systemd_sysusers) 

101 api.metadata_or_maintscript_detector("list-commands", detect_commands) 

102 api.metadata_or_maintscript_detector("kernel-modules", detect_kernel_modules) 

103 api.metadata_or_maintscript_detector("icon-cache", detect_icons) 

104 api.metadata_or_maintscript_detector( 

105 "gsettings-dependencies", 

106 detect_gsettings_dependencies, 

107 ) 

108 api.metadata_or_maintscript_detector("xfonts", detect_xfonts) 

109 api.metadata_or_maintscript_detector("initramfs-hooks", detect_initramfs_hooks) 

110 api.metadata_or_maintscript_detector("pycompile-files", detect_pycompile_files) 

111 api.metadata_or_maintscript_detector( 

112 "translate-capabilities", 

113 translate_capabilities, 

114 ) 

115 api.metadata_or_maintscript_detector("pam-auth-update", pam_auth_update) 

116 api.metadata_or_maintscript_detector( 

117 "auto-depends-arch-any-solink", 

118 auto_depends_arch_any_solink, 

119 ) 

120 api.metadata_or_maintscript_detector( 

121 "built-using-relations", 

122 detect_built_using, 

123 package_types=PackageTypeSelector.DEB | PackageTypeSelector.UDEB, 

124 ) 

125 

126 

127def register_packager_provided_files(api: DebputyPluginInitializer) -> None: 

128 api.packager_provided_file( 

129 "tmpfiles", 

130 f"{SYSTEMD_TMPFILES_DIR}/{ name} .conf", 

131 reference_documentation=packager_provided_file_reference_documentation( 

132 format_documentation_uris=["man:tmpfiles.d(5)"] 

133 ), 

134 ) 

135 api.packager_provided_file( 

136 "sysusers", 

137 f"{SYSTEMD_SYSUSERS_DIR}/{ name} .conf", 

138 reference_documentation=packager_provided_file_reference_documentation( 

139 format_documentation_uris=["man:sysusers.d(5)"] 

140 ), 

141 ) 

142 api.packager_provided_file( 

143 "bash-completion", "/usr/share/bash-completion/completions/{name}" 

144 ) 

145 api.packager_provided_file( 

146 "fish-completion", "/usr/share/fish/vendor_completions.d/{name}" 

147 ) 

148 api.packager_provided_file( 

149 "zsh-completion", "/usr/share/zsh/vendor-completions/{name}" 

150 ) 

151 api.packager_provided_file( 

152 "bug-script", 

153 "./usr/share/bug/{name}/script", 

154 default_mode=0o0755, 

155 allow_name_segment=False, 

156 package_types=PackageTypeSelector.DEB, 

157 ) 

158 api.packager_provided_file( 

159 "bug-control", 

160 "/usr/share/bug/{name}/control", 

161 allow_name_segment=False, 

162 package_types=PackageTypeSelector.DEB, 

163 ) 

164 

165 api.packager_provided_file( 

166 "bug-presubj", 

167 "/usr/share/bug/{name}/presubj", 

168 allow_name_segment=False, 

169 package_types=PackageTypeSelector.DEB, 

170 ) 

171 

172 api.packager_provided_file("pam", "/usr/lib/pam.d/{name}") 

173 api.packager_provided_file( 

174 "ppp.ip-up", 

175 "/etc/ppp/ip-up.d/{name}", 

176 default_mode=0o0755, 

177 ) 

178 api.packager_provided_file( 

179 "ppp.ip-down", 

180 "/etc/ppp/ip-down.d/{name}", 

181 default_mode=0o0755, 

182 ) 

183 api.packager_provided_file( 

184 "lintian-overrides", 

185 "/usr/share/lintian/overrides/{name}", 

186 allow_name_segment=False, 

187 package_types=PackageTypeSelector.DEB, 

188 ) 

189 api.packager_provided_file("logrotate", "/etc/logrotate.d/{name}") 

190 api.packager_provided_file( 

191 "logcheck.cracking", 

192 "/etc/logcheck/cracking.d/{name}", 

193 post_formatting_rewrite=_replace_dot_with_underscore, 

194 ) 

195 api.packager_provided_file( 

196 "logcheck.violations", 

197 "/etc/logcheck/violations.d/{name}", 

198 post_formatting_rewrite=_replace_dot_with_underscore, 

199 ) 

200 api.packager_provided_file( 

201 "logcheck.violations.ignore", 

202 "/etc/logcheck/violations.ignore.d/{name}", 

203 post_formatting_rewrite=_replace_dot_with_underscore, 

204 ) 

205 api.packager_provided_file( 

206 "logcheck.ignore.workstation", 

207 "/etc/logcheck/ignore.d.workstation/{name}", 

208 post_formatting_rewrite=_replace_dot_with_underscore, 

209 ) 

210 api.packager_provided_file( 

211 "logcheck.ignore.server", 

212 "/etc/logcheck/ignore.d.server/{name}", 

213 post_formatting_rewrite=_replace_dot_with_underscore, 

214 ) 

215 api.packager_provided_file( 

216 "logcheck.ignore.paranoid", 

217 "/etc/logcheck/ignore.d.paranoid/{name}", 

218 post_formatting_rewrite=_replace_dot_with_underscore, 

219 ) 

220 

221 api.packager_provided_file("mime", "/usr/lib/mime/packages/{name}") 

222 api.packager_provided_file("sharedmimeinfo", "/usr/share/mime/packages/{name}.xml") 

223 

224 api.packager_provided_file( 

225 "if-pre-up", 

226 "/etc/network/if-pre-up.d/{name}", 

227 default_mode=0o0755, 

228 ) 

229 api.packager_provided_file( 

230 "if-up", 

231 "/etc/network/if-up.d/{name}", 

232 default_mode=0o0755, 

233 ) 

234 api.packager_provided_file( 

235 "if-down", 

236 "/etc/network/if-down.d/{name}", 

237 default_mode=0o0755, 

238 ) 

239 api.packager_provided_file( 

240 "if-post-down", 

241 "/etc/network/if-post-down.d/{name}", 

242 default_mode=0o0755, 

243 ) 

244 

245 api.packager_provided_file( 

246 "cron.hourly", 

247 "/etc/cron.hourly/{name}", 

248 default_mode=0o0755, 

249 ) 

250 api.packager_provided_file( 

251 "cron.daily", 

252 "/etc/cron.daily/{name}", 

253 default_mode=0o0755, 

254 ) 

255 api.packager_provided_file( 

256 "cron.weekly", 

257 "/etc/cron.weekly/{name}", 

258 default_mode=0o0755, 

259 ) 

260 api.packager_provided_file( 

261 "cron.monthly", 

262 "./etc/cron.monthly/{name}", 

263 default_mode=0o0755, 

264 ) 

265 api.packager_provided_file( 

266 "cron.yearly", 

267 "/etc/cron.yearly/{name}", 

268 default_mode=0o0755, 

269 ) 

270 # cron.d uses 0644 unlike the others 

271 api.packager_provided_file( 

272 "cron.d", 

273 "/etc/cron.d/{name}", 

274 reference_documentation=packager_provided_file_reference_documentation( 

275 format_documentation_uris=["man:crontab(5)"] 

276 ), 

277 ) 

278 

279 api.packager_provided_file( 

280 "initramfs-hook", f"{INITRAMFS_HOOK_DIR}/{ name} ", default_mode=0o0755 

281 ) 

282 

283 api.packager_provided_file("modprobe", "/usr/lib/modprobe.d/{name}.conf") 

284 

285 api.packager_provided_file( 

286 "init", 

287 "/etc/init.d/{name}", 

288 default_mode=0o755, 

289 ) 

290 api.packager_provided_file("default", "/etc/default/{name}") 

291 

292 for stem in [ 

293 "mount", 

294 "path", 

295 "service", 

296 "socket", 

297 "target", 

298 "timer", 

299 ]: 

300 api.packager_provided_file( 

301 stem, 

302 f"/usr/lib/systemd/system/{ name} .{stem}", 

303 reference_documentation=packager_provided_file_reference_documentation( 

304 format_documentation_uris=[f"man:systemd.{stem}(5)"] 

305 ), 

306 ) 

307 

308 for stem in [ 

309 "path", 

310 "service", 

311 "socket", 

312 "target", 

313 "timer", 

314 ]: 

315 api.packager_provided_file( 

316 f"@{stem}", f"/usr/lib/systemd/system/{ name} @.{stem}" 

317 ) 

318 

319 api.packager_provided_file( 

320 "udev", 

321 "/usr/lib/udev/rules.d/{priority:02}-{name}.rules", 

322 default_priority=60, 

323 ) 

324 

325 api.packager_provided_file( 

326 "gsettings-override", 

327 f"{GSETTINGS_SCHEMA_DIR}/{ priority:02} _{ name} .gschema.override", 

328 default_priority=10, 

329 ) 

330 

331 # Special-cases that will probably not be a good example for other plugins 

332 api.packager_provided_file( 

333 "changelog", 

334 # The "changelog.Debian" gets renamed to "changelog" for native packages elsewhere. 

335 # Also, the changelog trimming is also done elsewhere. 

336 "/usr/share/doc/{name}/changelog.Debian", 

337 allow_name_segment=False, 

338 packageless_is_fallback_for_all_packages=True, 

339 package_types=PackageTypeSelector.DEB, 

340 reference_documentation=packager_provided_file_reference_documentation( 

341 description=textwrap.dedent("""\ 

342 This file is the changelog of the package and is mandatory. 

343 

344 The changelog contains the version of the source package and is mandatory for all 

345 packages. 

346 

347 Use `dch --create` to create the changelog. 

348 

349 In theory, the binary package can have a different changelog than the source 

350 package (by having `debian/binary-package.changelog`). However, it is generally 

351 not useful and leads to double administration. It has not been used in practice. 

352 """), 

353 format_documentation_uris=[ 

354 "man:deb-changelog(5)", 

355 "https://www.debian.org/doc/debian-policy/ch-source.html#debian-changelog-debian-changelog", 

356 "man:dch(1)", 

357 ], 

358 ), 

359 ) 

360 api.packager_provided_file( 

361 "copyright", 

362 "/usr/share/doc/{name}/copyright", 

363 allow_name_segment=False, 

364 packageless_is_fallback_for_all_packages=True, 

365 package_types=PackageTypeSelector.DEB, 

366 reference_documentation=packager_provided_file_reference_documentation( 

367 description=textwrap.dedent("""\ 

368 This file documents the license and copyright information of the binary package. 

369 Packages aimed at the Debian archive (and must derivatives thereof) must have this file. 

370 

371 For packages not aimed at Debian, the file can still be useful to convey the license 

372 terms of the package (which is often a requirement in many licenses). However, it is 

373 not a strict *technical* requirement. Whether it is a legal requirement depends on 

374 license. 

375 

376 Often, the same file can be used for all packages. In the extremely rare case where 

377 one binary package has a "vastly different" license than the other packages, you can 

378 provide a package specific version for that package. 

379 """), 

380 format_documentation_uris=[ 

381 "https://www.debian.org/doc/debian-policy/ch-source.html#copyright-debian-copyright", 

382 "https://www.debian.org/doc/debian-policy/ch-docs.html#s-copyrightfile", 

383 "https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/", 

384 ], 

385 ), 

386 ) 

387 api.packager_provided_file( 

388 "NEWS", 

389 "/usr/share/doc/{name}/NEWS.Debian", 

390 allow_name_segment=False, 

391 packageless_is_fallback_for_all_packages=True, 

392 package_types=PackageTypeSelector.DEB, 

393 reference_documentation=packager_provided_file_reference_documentation( 

394 description=textwrap.dedent("""\ 

395 Important news that should be shown to the user/admin when upgrading. If a system has 

396 apt-listchanges installed, then contents of this file will be shown prior to upgrading 

397 the package. 

398 

399 Uses a similar format to that of debian/changelog (create with `dch --news --create`). 

400 """), 

401 format_documentation_uris=[ 

402 "https://www.debian.org/doc/manuals/developers-reference/best-pkging-practices.en.html#supplementing-changelogs-with-news-debian-files", 

403 "man:dch(1)", 

404 ], 

405 ), 

406 ) 

407 api.packager_provided_file( 

408 "README.Debian", 

409 "/usr/share/doc/{name}/README.Debian", 

410 allow_name_segment=False, 

411 package_types=PackageTypeSelector.DEB, 

412 ) 

413 api.packager_provided_file( 

414 "TODO", 

415 "/usr/share/doc/{name}/TODO.Debian", 

416 allow_name_segment=False, 

417 package_types=PackageTypeSelector.DEB, 

418 ) 

419 # From dh-python / dh_python3 

420 # api.packager_provided_file( 

421 # "bcep", 

422 # "/usr/share/python3/bcep/{name}", 

423 # allow_name_segment=False, 

424 # ) 

425 

426 

427def _replace_dot_with_underscore(x: str) -> str: 

428 return x.replace(".", "_")