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

86 statements  

« prev     ^ index     » next       coverage.py v7.8.2, created at 2026-01-16 17:20 +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 ) 

157 api.packager_provided_file( 

158 "bug-control", 

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

160 allow_name_segment=False, 

161 ) 

162 

163 api.packager_provided_file( 

164 "bug-presubj", 

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

166 allow_name_segment=False, 

167 ) 

168 

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

170 api.packager_provided_file( 

171 "ppp.ip-up", 

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

173 default_mode=0o0755, 

174 ) 

175 api.packager_provided_file( 

176 "ppp.ip-down", 

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

178 default_mode=0o0755, 

179 ) 

180 api.packager_provided_file( 

181 "lintian-overrides", 

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

183 allow_name_segment=False, 

184 ) 

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

186 api.packager_provided_file( 

187 "logcheck.cracking", 

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

189 post_formatting_rewrite=_replace_dot_with_underscore, 

190 ) 

191 api.packager_provided_file( 

192 "logcheck.violations", 

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

194 post_formatting_rewrite=_replace_dot_with_underscore, 

195 ) 

196 api.packager_provided_file( 

197 "logcheck.violations.ignore", 

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

199 post_formatting_rewrite=_replace_dot_with_underscore, 

200 ) 

201 api.packager_provided_file( 

202 "logcheck.ignore.workstation", 

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

204 post_formatting_rewrite=_replace_dot_with_underscore, 

205 ) 

206 api.packager_provided_file( 

207 "logcheck.ignore.server", 

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

209 post_formatting_rewrite=_replace_dot_with_underscore, 

210 ) 

211 api.packager_provided_file( 

212 "logcheck.ignore.paranoid", 

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

214 post_formatting_rewrite=_replace_dot_with_underscore, 

215 ) 

216 

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

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

219 

220 api.packager_provided_file( 

221 "if-pre-up", 

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

223 default_mode=0o0755, 

224 ) 

225 api.packager_provided_file( 

226 "if-up", 

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

228 default_mode=0o0755, 

229 ) 

230 api.packager_provided_file( 

231 "if-down", 

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

233 default_mode=0o0755, 

234 ) 

235 api.packager_provided_file( 

236 "if-post-down", 

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

238 default_mode=0o0755, 

239 ) 

240 

241 api.packager_provided_file( 

242 "cron.hourly", 

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

244 default_mode=0o0755, 

245 ) 

246 api.packager_provided_file( 

247 "cron.daily", 

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

249 default_mode=0o0755, 

250 ) 

251 api.packager_provided_file( 

252 "cron.weekly", 

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

254 default_mode=0o0755, 

255 ) 

256 api.packager_provided_file( 

257 "cron.monthly", 

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

259 default_mode=0o0755, 

260 ) 

261 api.packager_provided_file( 

262 "cron.yearly", 

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

264 default_mode=0o0755, 

265 ) 

266 # cron.d uses 0644 unlike the others 

267 api.packager_provided_file( 

268 "cron.d", 

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

270 reference_documentation=packager_provided_file_reference_documentation( 

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

272 ), 

273 ) 

274 

275 api.packager_provided_file( 

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

277 ) 

278 

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

280 

281 api.packager_provided_file( 

282 "init", 

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

284 default_mode=0o755, 

285 ) 

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

287 

288 for stem in [ 

289 "mount", 

290 "path", 

291 "service", 

292 "socket", 

293 "target", 

294 "timer", 

295 ]: 

296 api.packager_provided_file( 

297 stem, 

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

299 reference_documentation=packager_provided_file_reference_documentation( 

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

301 ), 

302 ) 

303 

304 for stem in [ 

305 "path", 

306 "service", 

307 "socket", 

308 "target", 

309 "timer", 

310 ]: 

311 api.packager_provided_file( 

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

313 ) 

314 

315 api.packager_provided_file( 

316 "udev", 

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

318 default_priority=60, 

319 ) 

320 

321 api.packager_provided_file( 

322 "gsettings-override", 

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

324 default_priority=10, 

325 ) 

326 

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

328 api.packager_provided_file( 

329 "changelog", 

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

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

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

333 allow_name_segment=False, 

334 packageless_is_fallback_for_all_packages=True, 

335 reference_documentation=packager_provided_file_reference_documentation( 

336 description=textwrap.dedent( 

337 """\ 

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

339 

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

341 packages. 

342 

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

344 

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

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

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

348 """ 

349 ), 

350 format_documentation_uris=[ 

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

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

353 "man:dch(1)", 

354 ], 

355 ), 

356 ) 

357 api.packager_provided_file( 

358 "copyright", 

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

360 allow_name_segment=False, 

361 packageless_is_fallback_for_all_packages=True, 

362 reference_documentation=packager_provided_file_reference_documentation( 

363 description=textwrap.dedent( 

364 """\ 

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

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

367 

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

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

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

371 license. 

372 

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

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

375 provide a package specific version for that package. 

376 """ 

377 ), 

378 format_documentation_uris=[ 

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

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

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

382 ], 

383 ), 

384 ) 

385 api.packager_provided_file( 

386 "NEWS", 

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

388 allow_name_segment=False, 

389 packageless_is_fallback_for_all_packages=True, 

390 reference_documentation=packager_provided_file_reference_documentation( 

391 description=textwrap.dedent( 

392 """\ 

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

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

395 the package. 

396 

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

398 """ 

399 ), 

400 format_documentation_uris=[ 

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

402 "man:dch(1)", 

403 ], 

404 ), 

405 ) 

406 api.packager_provided_file( 

407 "README.Debian", 

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

409 allow_name_segment=False, 

410 ) 

411 api.packager_provided_file( 

412 "TODO", 

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

414 allow_name_segment=False, 

415 ) 

416 # From dh-python / dh_python3 

417 # api.packager_provided_file( 

418 # "bcep", 

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

420 # allow_name_segment=False, 

421 # ) 

422 

423 

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

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