自动化 Python 包发布过程
Posted
技术标签:
【中文标题】自动化 Python 包发布过程【英文标题】:Automating Python package release process 【发布时间】:2019-12-28 21:12:14 【问题描述】:我刚刚开始了一个开源 Python 项目,我希望它有一天会流行起来。目前要发布新版本,我必须做一些事情。
-
测试所有的东西。
编辑
mypackage.VERSION
变量,setup.py
从__init__
导入
使用python setup.py sdist bdist_wheel
构建包和***
将变更日志条目写入CHANGELOG
文件
提交我的更改,回显其中的一些更改日志
将该提交标记为发布,再次复制该更改日志条目。
拖入我构建的文件,以便人们可以从版本中下载它们
使用 Twine 将包推送到 PyPI 上
通过 PyPI 在我的暂存服务器上再次测试。
如果我必须用九个要点来总结我讨厌我的项目的所有内容,我想我们会看到一个非常相似的列表。最重要的是,过去我编了一个新版本号并写了提交/更改日志消息,这非常无聊。
我能否以某种方式自动化这些任务,例如,让 GitHub CI 仅通过我的提交来完成所有事情?
我已经有十年的 Python 经验和一点 CI,但我对打包 Python 和积极与 PyPI 交互还是很陌生。我怀疑我不是唯一一个被这里的手动重复逼疯的人,我只是在寻找可以使这个过程更容易的工具(或服务)。
【问题讨论】:
您是否有特定的 CI?取决于它,答案可能看起来有很大不同。 或者你的意思是 gitlab CI?因为它是开箱即用的。 项目目前在 GitHub 上,没有 CI。它是 FOSS,所以任何免费的 FOSS 都可以。但我也完全可以将它转移到 Gitlab 并使用他们的 CI。这就是我这些天大部分有偿工作的地方。如果一个或另一个让我在这里做的事情变得更容易或更好,那就是我想听到的。 好的。最后一个问题,您的第 9 步测试是否仅来自 pyPI,或者它是否还从第 1 步重新运行测试套件,或者它是否根据下载的 lib 生成工件或其他东西? #9 的目的是测试构建的包是否作为一个包工作。所以是的,目前只是测试我可以pip install
它,这需要一段时间,因为 PyPI 更新 IME 的速度很慢。我猜它可以在#3 之后完成 - 这样可以节省我上传垃圾的时间。进行实际的“是否有效”测试对我来说自动化有点棘手,因为我的“真正的”测试工具是使用这个项目的商业项目(它是一个 Django 库),所以我显然不能在这里包含它。它有自己的测试 CI。
【参考方案1】:
以下是我对您的清单的看法。您可以实现一定范围的自动化,我将尝试提供一个合理的起点,然后提供一些提示,告诉您如何从那里走得更远。
无 CD 的 CI
采用这部分应该已经摆脱了大部分烦人的手动工作,并且您可以根据需要越来越多地自动化。如果你不习惯维护大量的 CI 代码,你应该从这里开始。
您需要的是一个 CI(正如您已经指出的)和一个包管理器。您无法解决的问题是使用 git 推送您的更改和一个新标签,因此第 5 步和第 6 步的部分内容仍然是手动的。
包管理
我将使用poetry 来保持简洁,因为我喜欢它[1],但也有other options。这将处理第 2、3、7、8 步和未列出的第 10 步,“更新我的依赖项并测试它们的兼容性”,一旦出现问题,就会非常烦人。
使用诗歌时的坏消息是您需要将所有打包配置移动到一个新文件pyproject.toml
。好消息是,您不再需要单独的 setup.py
、setup.cfg
、MANIFEST.in
或 requirements.txt
,因为 pyproject.toml
是包装和其他工具的临时标准,诗歌也有walkthrough 关于如何移植所有相关信息。
设置完成后,新的部署工作流程将是:
$ poetry update # update dependencies, may be skipped
$ poetry version # bump version
Bumping version from 1.1.2 to 1.1.3
# finalize git stuff, e.g. add -u, commit -m 'v1.1.3', tag v1.1.3, push
$ poetry publish --build # build and publish to PyPI
Building my_django_lib (1.1.3)
- Building sdist
- Built my_django_lib-1.1.3.tar.gz
- Building wheel
- Built my_django_lib-1.1.3-py3-none-any.whl
Publishing my_django_lib (1.1.3) to PyPI
- Uploading my_django_lib-1.1.3-py3-none-any.whl 100%
- Uploading my_django_lib-1.1.3.tar.gz 100%
这应该已经比您当前所做的要短很多。如果你总是执行完全相同的 git 命令,不害怕自动推送,并妥善保管你的 .gitignore
文件,请随时将类似此函数的内容添加到 ~/.bashrc
并调用它:
git_cord ()
version=$(grep pyproject.toml -e '(?<=^version = ")(.*)(?=")' -Po)
git add -u
git commit -m "$version"
git tag "$version"
git push -u origin "$version"
gitlab-CI 入门
CI 原则上可以处理与部署过程相关的所有事情,包括版本更新和发布。但是第一个要求您的 CI 可以推送到您的 repo(这有烦人的副作用),而后者可以发布到您的 PyPI(这是有风险的,并且使调试 CI 变得很痛苦)。我认为更喜欢手动完成这两个步骤并不少见,因此这种最小的方法将只处理第 1 步和第 9 步。之后可以包括更广泛的测试和构建作业。
CI 的正确设置取决于您打算使用哪一个。 list for github 很长,所以我将重点介绍 gitlab 的内置 CI。它是免费的,几乎没有什么魔力(这使得它具有相当的便携性),并且 CI 运行器的二进制文件是 open, free, and actually documented,因此您可以在本地调试您的 CI 或启动并连接新的运行器(如果免费的运行器不适合它)你。
这是一个小的.gitlab-ci.yml
,您可以将其放入项目根目录以运行测试。管道中的每个作业(跳过设置和安装命令)也应该可以在您的开发环境中执行,保持这种状态可以为维护者提供更好的体验。
image: python:3.7-alpine
stages:
- build
- test
packaging:
stage: build
script:
- pip install poetry
- poetry build
artifacts:
paths:
- dist
pytest:
stage: test
script:
- pip install dist/*.whl
- pip install pytest
- pytest
像这样设置build
和test
阶段可以一口气处理步骤1 和9,同时还针对已安装的包而不是源文件运行测试套件。虽然只有在项目中有 src-layout 时它才能正常工作,这使得本地源无法从项目根目录导入。关于为什么这是一个好主意的一些信息here 和 here。
Poetry 可以创建一个 src-layout 模板,您可以使用 poetry new my_django_lib --src
将代码移动到其中。
更新日志
虽然有来自提交消息的automatically create a changelog 的工具,但keeping a good changelog 是那些从手工处理中受益匪浅的东西之一。所以,我的建议是第 4 步不要自动化。
一种思考方式是手动 CHANGELOG
文件包含与您的用户相关的信息,并且应该只包含新功能、重要错误修复和弃用等信息。
可能对贡献者或插件作者很重要的更细粒度的信息将位于 MR、提交消息或问题讨论中,不应将其放入 CHANGELOG
。您可以尝试以某种方式收集它,但导航这样的AUTOLOG
可能与筛选我刚才提到的主要来源一样麻烦。
所以简而言之,第 5 步和第 6 步的变更日志相关部分可以跳过。
带有 CD 的 CI
添加CD并没有太大变化,只是不再需要手动释放。如果 CI 出现故障、出现错误或您不想等待管道发布修补程序,您仍然可以随诗发布。
这将通过以下方式改变工作流程:
日常工作 写代码(还不能避免) 在提交消息和/或 MR 中记录进度(我更喜欢 MR,即使是我自己的更改,并在合并时压缩所有提交) 推送到 gitlab / 合并 MRs 发布时 创建一个标签,运行poetry version
或者poetry update
在CHANGELOG
中写入发行说明
推送到 gitlab
如果您使用 supply the secrets PYPI_USER
和 PYPI_PASSWORD
,对之前的 .gitlab-ci.yml
文件的此添加应该会立即生效:
stages:
- build
- test
- release
[...] # packaging and pytest unchanged
upload:
stage: release
only:
- tags
# Or alternatively "- /^v\d+\.\d+\.\d+/" if you also use non-release
# tags, the regex only matches tags that look like this: "v1.12.0"
script:
- pip install poetry
- poetry publish -u $PYPI_USER -p $PYPI_PASSWORD dist/*
一些有用的链接:
.gitlab-ci.yml
documentation
list of predefined variables,这是 gitlab CI 的大部分晦涩之处
我的.gitlab-ci.yml
模板中的the long version,带有可能对您有用或可能没用的附加阶段。它需要您的代码的 src 布局。
lint
:type checking、coverage 和 code style
security
:检查 your own code 和 your dependencies 的价值
release.docs
:提供自动创建的文档的公共 gitlab 页面部分 based on your docstrings
build
阶段从poetry.lock
文件创建一个操舵室,可用于稍后安装依赖项以支持 PyPI。这会稍微快一点,节省网络带宽,并且如果您想调试,则断言使用特定版本,但可能有点矫枉过正,需要使用诗歌预发行版。
[1] 除此之外,诗歌还 1)为您处理 virtualenv,2)创建一个散列锁文件以防您需要可重现的构建,以及 3)使贡献更容易,因为您只有克隆存储库后运行“诗歌安装”并准备就绪。
【讨论】:
向任何人(甚至是 repo 主机)提供 PyPI 的 my 凭据似乎... 奇怪。我不是说这是错的,但它让我感到不安。我正在考虑设置一个辅助帐户并授予它在 PyPI 上的发布权限。听起来是多余的还是明智的? 只有 repo 的所有者才能在 UI 中看到秘密变量的值,如果设置为“protected”,则只能在受保护的分支中访问,只有所有者触发的合并才应该能够触发。只要您注意贡献者不要将echo $MY_KEYS
添加到您的on: master
工作中,您的秘密应该 是安全的。再说一次,我在许多大型项目中看到了“ci-users”,它们被用作这种安全屏障。它们有一个发布令牌,但没有存储库的密钥,用于验证管道运行并将警告写入通道
简而言之,这对我来说听起来很明智,即使您会信任 gitlab / github 并提供您的凭据。
@Oli 您可以在 PyPI 上为单个包创建 令牌,然后使用它代替您的登录凭据,如果它让您感觉更好的话。查看here 了解更多信息.【参考方案2】:
我为我的一个开源工具自动化了这个。 这一切都在文件 manage.py 中: https://github.com/tfeldmann/organize/blob/master/manage.py
我的项目使用诗歌来上传到 pypi,所以这看起来有点不同,但应该是一个很好的起点。它还管理变更日志并创建所有版本。
python manage.py version
步骤:
## WIP
部分并将其替换为当前版本和今天的日期。
python manage.py publish
步骤:
脚本要求确认每个步骤,以免事情失控,并在需要时提示您输入 github 和 pypi 密码。
【讨论】:
以上是关于自动化 Python 包发布过程的主要内容,如果未能解决你的问题,请参考以下文章
Selenium+Python自动化测试环境搭建和搭建过程遇到的问题解决