自动化 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.pysetup.cfgMANIFEST.inrequirements.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

像这样设置buildtest 阶段可以一口气处理步骤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 updateCHANGELOG 中写入发行说明 推送到 gitlab

如果您使用 supply the secrets PYPI_USERPYPI_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步骤:

提示输入版本号并检查有效性 更新 __version__.py 文件 更新诗歌使用的 pyproject.toml 文件 在 changelog.md 中搜索 ## WIP 部分并将其替换为当前版本和今天的日期。

python manage.py publish 步骤:

读取当前版本 从更改日志中读取为此版本列出的更改 创建一个 git 标签 推送到github(带标签) 构建并发布到 pypi 创建一个 github 版本,版本号为名称,变更日志中的更改为描述

脚本要求确认每个步骤,以免事情失控,并在需要时提示您输入 github 和 pypi 密码。

【讨论】:

以上是关于自动化 Python 包发布过程的主要内容,如果未能解决你的问题,请参考以下文章

Appium+python自动化测试过程中问题

python自动导入包

Selenium+Python自动化测试环境搭建和搭建过程遇到的问题解决

巧用Python脚本解决自动化图形验证码难题

在 Azure 自动化 Runbook 中安装和导入 Python 包

python自动化持续集成:1.docker技术讲解