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

84 statements  

« prev     ^ index     » next       coverage.py v7.8.2, created at 2025-09-07 09:27 +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) 

21from debputy.plugins.debputy.paths import ( 

22 SYSTEMD_TMPFILES_DIR, 

23 INITRAMFS_HOOK_DIR, 

24 GSETTINGS_SCHEMA_DIR, 

25 SYSTEMD_SYSUSERS_DIR, 

26) 

27from debputy.plugins.debputy.private_api import initialize_via_private_api 

28 

29 

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

31 initialize_via_private_api(api) 

32 declare_manifest_variables(api) 

33 register_packager_provided_files(api) 

34 register_package_metadata_detectors(api) 

35 

36 

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

38 api.manifest_variable( 

39 "path:BASH_COMPLETION_DIR", 

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

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

42 ) 

43 api.manifest_variable( 

44 "path:FISH_COMPLETION_DIR", 

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

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

47 ) 

48 api.manifest_variable( 

49 "path:ZSH_COMPLETION_DIR", 

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

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

52 ) 

53 api.manifest_variable( 

54 "path:GNU_INFO_DIR", 

55 "/usr/share/info", 

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

57 ) 

58 

59 api.manifest_variable( 

60 "token:NL", 

61 "\n", 

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

63 ) 

64 api.manifest_variable( 

65 "token:NEWLINE", 

66 "\n", 

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

68 ) 

69 api.manifest_variable( 

70 "token:TAB", 

71 "\t", 

72 variable_reference_documentation="Literal tab character", 

73 ) 

74 api.manifest_variable( 

75 "token:OPEN_CURLY_BRACE", 

76 "{", 

77 variable_reference_documentation='Literal "{" character', 

78 ) 

79 api.manifest_variable( 

80 "token:CLOSE_CURLY_BRACE", 

81 "}", 

82 variable_reference_documentation='Literal "}" character', 

83 ) 

84 api.manifest_variable( 

85 "token:DOUBLE_OPEN_CURLY_BRACE", 

86 "{{", 

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

88 ) 

89 api.manifest_variable( 

90 "token:DOUBLE_CLOSE_CURLY_BRACE", 

91 "}}", 

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

93 ) 

94 

95 

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

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

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

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

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

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

102 api.metadata_or_maintscript_detector( 

103 "gsettings-dependencies", 

104 detect_gsettings_dependencies, 

105 ) 

106 api.metadata_or_maintscript_detector("xfonts", detect_xfonts) 

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

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

109 api.metadata_or_maintscript_detector( 

110 "translate-capabilities", 

111 translate_capabilities, 

112 ) 

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

114 api.metadata_or_maintscript_detector( 

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

116 auto_depends_arch_any_solink, 

117 ) 

118 

119 

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

121 api.packager_provided_file( 

122 "tmpfiles", 

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

124 reference_documentation=packager_provided_file_reference_documentation( 

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

126 ), 

127 ) 

128 api.packager_provided_file( 

129 "sysusers", 

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

131 reference_documentation=packager_provided_file_reference_documentation( 

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

133 ), 

134 ) 

135 api.packager_provided_file( 

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

137 ) 

138 api.packager_provided_file( 

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

140 ) 

141 api.packager_provided_file( 

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

143 ) 

144 api.packager_provided_file( 

145 "bug-script", 

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

147 default_mode=0o0755, 

148 allow_name_segment=False, 

149 ) 

150 api.packager_provided_file( 

151 "bug-control", 

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

153 allow_name_segment=False, 

154 ) 

155 

156 api.packager_provided_file( 

157 "bug-presubj", 

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

159 allow_name_segment=False, 

160 ) 

161 

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

163 api.packager_provided_file( 

164 "ppp.ip-up", 

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

166 default_mode=0o0755, 

167 ) 

168 api.packager_provided_file( 

169 "ppp.ip-down", 

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

171 default_mode=0o0755, 

172 ) 

173 api.packager_provided_file( 

174 "lintian-overrides", 

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

176 allow_name_segment=False, 

177 ) 

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

179 api.packager_provided_file( 

180 "logcheck.cracking", 

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

182 post_formatting_rewrite=_replace_dot_with_underscore, 

183 ) 

184 api.packager_provided_file( 

185 "logcheck.violations", 

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

187 post_formatting_rewrite=_replace_dot_with_underscore, 

188 ) 

189 api.packager_provided_file( 

190 "logcheck.violations.ignore", 

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

192 post_formatting_rewrite=_replace_dot_with_underscore, 

193 ) 

194 api.packager_provided_file( 

195 "logcheck.ignore.workstation", 

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

197 post_formatting_rewrite=_replace_dot_with_underscore, 

198 ) 

199 api.packager_provided_file( 

200 "logcheck.ignore.server", 

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

202 post_formatting_rewrite=_replace_dot_with_underscore, 

203 ) 

204 api.packager_provided_file( 

205 "logcheck.ignore.paranoid", 

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

207 post_formatting_rewrite=_replace_dot_with_underscore, 

208 ) 

209 

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

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

212 

213 api.packager_provided_file( 

214 "if-pre-up", 

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

216 default_mode=0o0755, 

217 ) 

218 api.packager_provided_file( 

219 "if-up", 

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

221 default_mode=0o0755, 

222 ) 

223 api.packager_provided_file( 

224 "if-down", 

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

226 default_mode=0o0755, 

227 ) 

228 api.packager_provided_file( 

229 "if-post-down", 

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

231 default_mode=0o0755, 

232 ) 

233 

234 api.packager_provided_file( 

235 "cron.hourly", 

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

237 default_mode=0o0755, 

238 ) 

239 api.packager_provided_file( 

240 "cron.daily", 

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

242 default_mode=0o0755, 

243 ) 

244 api.packager_provided_file( 

245 "cron.weekly", 

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

247 default_mode=0o0755, 

248 ) 

249 api.packager_provided_file( 

250 "cron.monthly", 

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

252 default_mode=0o0755, 

253 ) 

254 api.packager_provided_file( 

255 "cron.yearly", 

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

257 default_mode=0o0755, 

258 ) 

259 # cron.d uses 0644 unlike the others 

260 api.packager_provided_file( 

261 "cron.d", 

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

263 reference_documentation=packager_provided_file_reference_documentation( 

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

265 ), 

266 ) 

267 

268 api.packager_provided_file( 

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

270 ) 

271 

272 api.packager_provided_file("modprobe", "/etc/modprobe.d/{name}.conf") 

273 

274 api.packager_provided_file( 

275 "init", 

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

277 default_mode=0o755, 

278 ) 

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

280 

281 for stem in [ 

282 "mount", 

283 "path", 

284 "service", 

285 "socket", 

286 "target", 

287 "timer", 

288 ]: 

289 api.packager_provided_file( 

290 stem, 

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

292 reference_documentation=packager_provided_file_reference_documentation( 

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

294 ), 

295 ) 

296 

297 for stem in [ 

298 "path", 

299 "service", 

300 "socket", 

301 "target", 

302 "timer", 

303 ]: 

304 api.packager_provided_file( 

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

306 ) 

307 

308 api.packager_provided_file( 

309 "udev", 

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

311 default_priority=60, 

312 ) 

313 

314 api.packager_provided_file( 

315 "gsettings-override", 

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

317 default_priority=10, 

318 ) 

319 

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

321 api.packager_provided_file( 

322 "changelog", 

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

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

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

326 allow_name_segment=False, 

327 packageless_is_fallback_for_all_packages=True, 

328 reference_documentation=packager_provided_file_reference_documentation( 

329 description=textwrap.dedent( 

330 """\ 

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

332 

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

334 packages. 

335 

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

337 

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

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

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

341 """ 

342 ), 

343 format_documentation_uris=[ 

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

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

346 "man:dch(1)", 

347 ], 

348 ), 

349 ) 

350 api.packager_provided_file( 

351 "copyright", 

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

353 allow_name_segment=False, 

354 packageless_is_fallback_for_all_packages=True, 

355 reference_documentation=packager_provided_file_reference_documentation( 

356 description=textwrap.dedent( 

357 """\ 

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

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

360 

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

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

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

364 license. 

365 

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

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

368 provide a package specific version for that package. 

369 """ 

370 ), 

371 format_documentation_uris=[ 

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

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

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

375 ], 

376 ), 

377 ) 

378 api.packager_provided_file( 

379 "NEWS", 

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

381 allow_name_segment=False, 

382 packageless_is_fallback_for_all_packages=True, 

383 reference_documentation=packager_provided_file_reference_documentation( 

384 description=textwrap.dedent( 

385 """\ 

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

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

388 the package. 

389 

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

391 """ 

392 ), 

393 format_documentation_uris=[ 

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

395 "man:dch(1)", 

396 ], 

397 ), 

398 ) 

399 api.packager_provided_file( 

400 "README.Debian", 

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

402 allow_name_segment=False, 

403 ) 

404 api.packager_provided_file( 

405 "TODO", 

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

407 allow_name_segment=False, 

408 ) 

409 # From dh-python / dh_python3 

410 # api.packager_provided_file( 

411 # "bcep", 

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

413 # allow_name_segment=False, 

414 # ) 

415 

416 

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

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