Certbot 打包指南:发布流程、PGP 签名与发行版维护者实战要点

发布时间:2026/9/19 13:33:00
Certbot 打包指南:发布流程、PGP 签名与发行版维护者实战要点 Certbot 打包指南发布流程、PGP 签名与发行版维护者实战要点【免费下载链接】certbotCertbot is EFFs tool to obtain certs from Lets Encrypt and (optionally) auto-enable HTTPS on your server. It can also act as a client for any other CA that uses the ACME protocol.项目地址: https://gitcode.com/gh_mirrors/ce/certbotCertbot 是 EFFElectronic Frontier Foundation出品的 ACME 协议客户端用于从 Lets Encrypt 等 ACME CA 自动获取证书并启用 HTTPS。本文以仓库中的 packaging.rst 为核心系统讲解 Certbot 官方的发布机制、PGP 签名体系以及面向 Linux 发行版/第三方仓库维护者的打包注意事项并深入到tools/下的发布脚本源码帮助读者掌握如何发布 Certbot与如何为你的发行版正确打包 Certbot两套完整方案。一、项目结构与可发布的软件包Certbot 仓库采用多包multi-packagemonorepo 结构每个子包都有独立的pyproject.toml与setup.py可独立构建、独立上传 PyPI。从 tools/_release.sh 的SUBPKGS定义可以看出官方实际发布的完整软件包清单核心包certbot、acme插件包certbot-apache、certbot-nginxDNS 插件包certbot-dns-cloudflare、certbot-dns-digitalocean、certbot-dns-dnsimple、certbot-dns-dnsmadeeasy、certbot-dns-gehirn、certbot-dns-google、certbot-dns-linode、certbot-dns-luadns、certbot-dns-nsone、certbot-dns-ovh、certbot-dns-rfc2136、certbot-dns-route53、certbot-dns-sakuracloud这些包都会以 wheel 与源码包sdist两种形态发布到 PyPI对应的项目主页分别是acme、certbot、certbot-apache、certbot-nginx以及上述全部 DNS 插件。其中acme是 ACME 协议客户端库certbot依赖acme这一点在 certbot/setup.py 中体现得很直接install_requires明确写了facme{version}——即 Certbot 对 acme 的最小版本要求总是等于当前 Certbot 自身版本从而保证接口一致性。为什么不打包 certbot-compatibility-test发布清单中刻意不包含certbot-compatibility-test。从源码看有两个原因见 tools/_release.sh 中的注释它仅面向 Certbot 开发者内部使用不面向最终用户包名以test结尾打包后会导致pytest在收集测试时误将其当作测试目录执行而该目录下又没有真正的单元测试从而引发混乱。二、官方发布流程与脚本体系2.1 版本号与 Git 标签Certbot 使用 Semantic Versioning语义化版本作为版本标识例如v0.11.1。每次发布对应一个格式为v版本号的 Git 标签例如v1.21.0。发布分支的命名约定是candidate-版本号例如candidate-4.24.0。2.2 发布脚本tools/release.sh官方发布过程由 tools/release.sh 驱动其工作流程可以拆解为前置校验CheckVersion校验两个版本参数RELEASE_VERSION与NEXT_VERSION是否符合1.2.3格式检查是否存在 OpenPGP 卡片除非设置了RELEASE_GPG_KEY环境变量显式指定密钥确认系统已安装script命令用于记录发布日志。安全护栏若设置了SNAP_BUILD环境变量则拒绝执行——因为该变量会导致插件 wheel 在构建时不声明对 Certbot 的依赖从而破坏依赖关系。环境备份将旧的releases/目录与log文件重命名备份。执行核心脚本调用tools/_release.sh RELEASE_VERSION NEXT_VERSION并通过script命令把全过程输出写入日志。运行方式在仓库根目录执行./tools/release.sh RELEASE_VERSION NEXT_VERSION # 例如发布 4.24.0、下一个开发版本为 4.25.0 ./tools/release.sh 4.24.0 4.25.02.3 核心构建脚本tools/_release.sh真正的构建逻辑在 tools/_release.sh 中关键步骤包括环境清理检测并退出任何激活中的 Python 虚拟环境避免污染构建。分支管理若当前不在candidate-version分支上自动git switch -c创建该分支。PGP 密钥选择若未设置RELEASE_GPG_KEY脚本会在 OpenPGP 卡片上查找四把受信任的发布密钥见下文签名一节找到后用于后续所有签名操作并导出GPG_TTY$(tty)解决 pinentry 交互问题。干净克隆git clone . $root克隆一份全新副本再构建确保产物不被本地残留文件污染。变更日志生成调用towncrier build --version $version --yes把newsfragments/目录下的变更片段合并进 CHANGELOG 并提交。版本号写入SetVersion函数统一将SUBPKGS_NO_CERTBOT、certbot-compatibility-test、certbot-ci、letstest各setup.py中的version字段以及 certbot/src/certbot/init.py 中的__version__更新为目标版本。构建产物用python -m build --installer uv为每个子包构建 sdist 与 wheel全部汇总到packages/目录。校验和与签名对全部*.tar.gz生成SHA256SUMS文件并用gpg -u $RELEASE_GPG_KEY --detach-sign --armor --digest-algo sha256对其进行分离签名。自举验证创建全新虚拟环境通过--find-links .从本地构建产物安装全部子包版本精确锁定为pkgversion确保发布产物本身可安装随后生成 CLI 帮助快照写入 certbot/docs/cli-help.txt 与 acme/docs/jws-help.txt生成时设置CERTBOT_DOCS1以使用文档专用的 User-Agent 样例值。提交与打标签使用发布密钥对 Release $version 提交进行 GPG 签名并以--local-user $RELEASE_GPG_KEY --sign方式创建签名的vversion标签随后从 Git 索引中移除构建产物目录并提交 Bump version to $nextversion 完成下一个开发版本号NEXT_VERSION.dev0的推进。2.4 发布收尾tools/finish_release.py构建与上传 PyPI 之后由 tools/finish_release.py 完成发布收尾主要包括四件事Snap 渠道晋升将certbot及全部 DNS 插件共 18 个 snapALL_SNAPS [certbot] PLUGIN_SNAPS从 beta 渠道晋升到 stable 渠道脚本要求已通过snapcraft login登录并通过snapcraft status校验每个 snap 在指定版本下恰好有 3 个架构SNAP_ARCH_COUNT 3的 revision。GitHub 分支同步将candidate-version分支推送到远端并创建合并回main的 PR非补丁版本第三段版本号为 0会额外创建1.2.x形式的次要版本分支补丁版本则创建point-candidate-x.y.z分支并合并回.x分支。社区公告生成通过gh release view vversion抓取发布说明打印格式化后的论坛公告文本。测试模式提供--skip-snaps、--skip-github-sync、--test-version A.B.C三个参数方便在两次发布之间安全演练snap 晋升与公告打印是幂等操作。运行方式python tools/finish_release.py [--skip-snaps] [--skip-github-sync] [--test-version 1.2.3]三、PGP 签名体系与校验自1.21.0起Certbot 的所有发布包由以下四把 PGP 密钥之一进行加密签名公钥可在各大主流密钥服务器上获取密钥指纹说明BF6BCFC89E90747B9A680FD7B6029E8500F7DB16当前发布密钥86379B4F0AF371B50CD9E5FF3402831161D1D280当前发布密钥20F201346BF8F3F455A73F9A780CC99432A28621当前发布密钥F2871B4152AE13C49519111F447BF683AA3B26C3当前发布密钥1.21.0 之前的版本则由旧密钥A2CFB51FA275A7286234E7B24D17C995CD9775F2签名该公钥至今仍可在主要密钥服务器上找到便于校验旧版软件包。这一四选一的密钥体系与发布脚本深度绑定tools/_release.sh在未显式设置RELEASE_GPG_KEY时会在 OpenPGP 卡片上遍历TRUSTED_KEYS列表一旦gpg --card-status命中四把密钥中的任意一把即采用之找不到则报错退出。这意味着只有持有受信任密钥的维护者才能完成一次正式发布从流程上保证了供应链可信。四、面向发行版/第三方维护者的打包注意事项packaging.rst专门为 Linux 发行版打包者与自建仓库维护者列出了五条要点以下是逐条解读与源码佐证。4.1 使用打标签的发布版本不要用 main请务必使用官方发布的 tagged release如v1.21.0而不是main分支快照。main处于持续开发状态包含未发布的功能与尚未冻结的接口直接打包会导致版本混乱、依赖破裂也无法获得正确的签名。官方发布的版本号、签名、变更日志都是围绕 tag 组织的。4.2 不要打包 certbot-compatibility-test该包仅供 Certbot 开发者内部使用正如第二节所述打包它还会干扰pytest的测试收集。发行版应将其排除在产物之外。4.3 使用 python -m pytest 运行测试对打包好的源码运行测试时请使用python -m pytest不要直接运行pytest命令。原因在于python -m pytest会把当前目录加入PYTHONPATH而直接执行pytest可执行文件时PYTHONPATH的处理方式不同可能导致本地模块无法被测试运行器找到从而出现测试找不到被测模块的假失败。4.4 在包中集成自动化续期如果你的发行版希望开箱即用地提供证书自动续期功能需要做三件事调度certbot renew -q将certbot renew -q加入 crontab 或 systemd timer。Certbot 的 renew 子命令会检查所有已签发的证书仅对临近过期的证书执行续期。加入随机时间偏移为每台机器设置一个随机的续期执行时间偏移避免大量客户端在同一时刻并发访问 Lets Encrypt 服务器造成负载尖峰。例如 cron 中不要统一写死0 0 * * *而应在小时/分钟字段中引入随机值或由 systemd timer 使用随机延迟。在所有 Certbot 调用中带上--preconfigured-renewal适用于 Certbot 1.9.0该标志可通过命令行或cli.ini配置文件全局设置作用是让 Certbot 知道自动续期已由包管理者预先配置好从而调整其交互输出——它不会再输出证书不会被自动续期之类的误导性提示。--preconfigured-renewal的源码级佐证该标志定义在 certbot/src/certbot/_internal/cli/init.py 中是一个actionstore_true的隐藏选项helpargparse.SUPPRESS默认值来自flag_default(preconfigured_renewal)而 certbot/src/certbot/_internal/constants.py 中其默认值为False。在实际行为上certbot/src/certbot/_internal/main.py 的_report_successful_renewal逻辑会据此决定是否输出NEXT STEPS中的自动续期提示当preconfigured_renewal为假时会提示用户Certbot can automatically renew the certificate in the background, but you may need to take steps to enable that functionality为真时则提示Certbot has set up a scheduled task to automatically renew this certificate in the background见_report_new_cert。certbot的 Snap 包正是通过 certbot/src/certbot/_internal/snap_config.py 的prepare_env自动追加--preconfigured-renewal来实现这一机制的可作为发行版打包的参考范本。配置示例发行版可以在全局配置文件例如 Debian 系的/etc/letsencrypt/cli.ini参考仓库中的 certbot/examples/cli.ini中写入preconfigured-renewal true该文件中同样可以设置email、authenticator、rsa-key-size等常规选项这些选项会自动应用于系统上所有证书的签发与续期调用。4.5 jws 是 acme 的内部调试脚本无需打包jws是acme模块内部的命令行脚本主要用于调试 JWSJSON Web Signature签名流程不面向终端用户因此不必随发行版打包。它的典型用法是管道式签名/验签echo foo | jws sign | jws verify其帮助文本快照可在 acme/docs/jws-help.txt 中查看。它之所以存在是因为 ACME 协议的所有请求都需要 JWS 签名开发者可以用它快速验证 JWS 逻辑。4.6 与上游保持沟通补丁优先贡献回主线最后一条建议主动与 Certbot 团队保持联系。维护者非常乐于为打包便利性做出变更如果你发现必须对源码打补丁才能打包不要在下游维护补丁否则每次上游升级都要重新移植而是直接在上游仓库提交 PR 合入主线。这样既减轻发行版维护负担也让所有用户受益。五、FAQ 与常见问题Q为什么仓库里acme/setup.py的版本是5.8.0.dev0这类带.dev0的版本A.dev0是发布脚本在Bump version to $nextversion步骤写入的下一个开发版本号见 tools/_release.sh表示当前main处于5.8.0的开发期尚未正式发布正式发布时才会写入干净的x.y.z版本号。Q如何验证下载到的 Certbot 包确实是官方签发的A从主流密钥服务器导入上文四把公钥或旧版的A2CFB51FA275A7286234E7B24D17C995CD9775F2然后对随包发布的SHA256SUMS.asc执行gpg --verify SHA256SUMS.asc SHA256SUMS再用sha256sum -c SHA256SUMS校验各归档文件完整性。Q发行版测试为什么必须用python -m pytestA见 4.3 节——pytest可执行文件与python -m pytest在PYTHONPATH处理上的差异会导致本地模块查找失败这是打包者最容易踩的坑之一。六、参考资源打包指南原文certbot/docs/packaging.rst发布脚本入口tools/release.sh发布脚本核心构建tools/_release.sh发布收尾脚本tools/finish_release.pyCertbot 打包配置certbot/setup.py、certbot/pyproject.toml--preconfigured-renewal实现certbot/src/certbot/_internal/cli/init.py、certbot/src/certbot/_internal/main.py、certbot/src/certbot/_internal/snap_config.pyCLI 全局配置示例certbot/examples/cli.ini【免费下载链接】certbotCertbot is EFFs tool to obtain certs from Lets Encrypt and (optionally) auto-enable HTTPS on your server. It can also act as a client for any other CA that uses the ACME protocol.项目地址: https://gitcode.com/gh_mirrors/ce/certbot创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考