WEBKT

开源项目自动化发布到 PyPI:GitHub Actions 工作流实战与发布日志生成

89 0 0 0

1. 项目初始化与基本结构

2. 创建 GitHub 仓库

3. 设置 PyPI 凭据

4. 配置 GitHub Actions 工作流

5. 发布到 PyPI

6. 生成发布日志

7. 总结与最佳实践

在开源项目的维护过程中,持续集成和持续部署 (CI/CD) 至关重要。它能帮助我们自动化测试、构建、发布等流程,从而提高开发效率和代码质量。对于 Python 项目而言,PyPI (Python Package Index) 是官方的第三方软件包仓库,将项目发布到 PyPI 可以让更多的开发者方便地使用你的代码。本文将以一个模拟的开源项目为例,详细介绍如何使用 GitHub Actions 自动化发布 Python 包到 PyPI,并生成详细的发布日志,面向开源项目维护者,让发布流程更加高效、透明。

1. 项目初始化与基本结构

首先,我们需要创建一个 Python 项目,并设置好基本的项目结构。这里我们创建一个名为 my_package 的项目,其目录结构如下:

my_package/
├── my_package/
│ ├── __init__.py
│ └── my_module.py
├── tests/
│ ├── __init__.py
│ └── test_my_module.py
├── setup.py
├── README.md
├── LICENSE
└── .gitignore
  • my_package/:存放项目源代码的目录。
  • my_package/__init__.py:将 my_package 目录视为一个 Python 包。
  • my_package/my_module.py:一个简单的模块,包含一些函数或类。
  • tests/:存放测试代码的目录。
  • tests/__init__.py:将 tests 目录视为一个 Python 包。
  • tests/test_my_module.py:包含针对 my_module.py 的测试用例。
  • setup.py:用于构建、打包和安装项目的脚本。
  • README.md:项目的说明文档。
  • LICENSE:项目的许可证文件。
  • .gitignore:指定 Git 应该忽略的文件和目录。

my_package/my_module.py 示例:

# my_package/my_module.py
def greet(name):
"""向指定的人打招呼。"""
return f"Hello, {name}!"

tests/test_my_module.py 示例:

# tests/test_my_module.py
import unittest
from my_package import my_module
class TestMyModule(unittest.TestCase):
def test_greet(self):
self.assertEqual(my_module.greet("World"), "Hello, World!")
if __name__ == '__main__':
unittest.main()

setup.py 示例:

# setup.py
import setuptools
with open("README.md", "r") as fh:
long_description = fh.read()
setuptools.setup(
name="my-package-example", # 替换成你的项目名称
version="0.0.1", # 初始版本号
author="Your Name", # 替换成你的名字
author_email="your.email@example.com", # 替换成你的邮箱
description="A small example package", # 替换成你的项目描述
long_description=long_description,
long_description_content_type="text/markdown",
url="https://github.com/your-username/my-package", # 替换成你的 GitHub 仓库地址
packages=setuptools.find_packages(),
classifiers=[
"Programming Language :: Python :: 3",
"License :: OSI Approved :: MIT License",
"Operating System :: OS Independent",
],
python_requires='>=3.6',
)

注意: setup.py 中的 name 字段必须是唯一的,否则无法成功发布到 PyPI。 建议使用 my-package-yourusername 这样的命名方式,避免与其他项目冲突。 替换 author, author_emailurl 为你自己的信息。

2. 创建 GitHub 仓库

将本地项目推送到 GitHub 仓库。如果没有 GitHub 账号,需要先注册一个。创建仓库后,按照 GitHub 提供的指引,将本地代码推送到远程仓库。

3. 设置 PyPI 凭据

为了让 GitHub Actions 能够自动发布包到 PyPI,我们需要在 GitHub 仓库中设置 PyPI 的凭据。有两种方式:

  • 使用 PyPI API Token (推荐): 登录 PyPI,进入 “Account settings”,找到 “API tokens” 部分,创建一个新的 API token。设置 Token 的作用域为 “Entire account” 或 “Limited to a specific project”。 将 Token 添加到 GitHub 仓库的 Secrets 中,命名为 PYPI_API_TOKEN
  • 使用 PyPI 用户名和密码 (不推荐): 虽然可以使用 PyPI 用户名和密码进行认证,但出于安全考虑,强烈建议使用 API Token。

进入 GitHub 仓库的 “Settings” -> “Secrets” -> “Actions”,点击 “New repository secret” 添加一个新的 Secret,Name 设置为 PYPI_API_TOKEN,Value 设置为你的 PyPI API Token。

4. 配置 GitHub Actions 工作流

在项目根目录下创建 .github/workflows 目录,并在该目录下创建一个 YAML 文件,例如 python-publish.yml,用于定义 GitHub Actions 工作流。

.github/workflows/python-publish.yml 示例:

# .github/workflows/python-publish.yml
name: Publish Python Package
on:
release:
types: [published]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Set up Python 3.x
uses: actions/setup-python@v4
with:
python-version: '3.x'
- name: Install dependencies
run: |
python -m pip install --upgrade pip
python -m pip install setuptools wheel twine
- name: Build package
run: python setup.py sdist bdist_wheel
- name: Publish package to PyPI
uses: pypa/gh-action-pypi-publish@release/v1
with:
password: ${{ secrets.PYPI_API_TOKEN }}

配置详解:

  • name:工作流的名称,显示在 GitHub Actions 页面上。
  • on:触发工作流的事件。这里设置为 release,并且 types[published],表示当发布新的 Release 时触发该工作流。
  • jobs:定义一个或多个 Job。这里只定义了一个名为 build 的 Job。
  • runs-on:指定 Job 运行的操作系统。这里设置为 ubuntu-latest,表示使用最新的 Ubuntu 系统。
  • steps:定义 Job 中的步骤。每个步骤执行一个特定的任务。
    • actions/checkout@v3:检出代码到工作流环境中。
    • actions/setup-python@v4:设置 Python 环境。python-version 设置为 '3.x',表示使用 Python 3 的最新版本。
    • Install dependencies:安装构建和发布所需的依赖包,包括 setuptoolswheeltwine
    • Build package:运行 python setup.py sdist bdist_wheel 构建 Python 包。sdist 创建源码包,bdist_wheel 创建 wheel 包。
    • pypa/gh-action-pypi-publish@release/v1:使用 GitHub Actions 官方提供的 PyPI 发布 Action,将构建好的包发布到 PyPI。password 设置为 ${{ secrets.PYPI_API_TOKEN }},表示使用之前在 GitHub Secrets 中设置的 API Token。

5. 发布到 PyPI

当你在 GitHub 仓库中创建一个新的 Release 时,GitHub Actions 会自动触发 python-publish.yml 工作流,执行构建和发布操作。你可以在 GitHub 仓库的 “Actions” 页面上查看工作流的执行状态和日志。如果一切顺利,你的 Python 包就会被成功发布到 PyPI 上。

6. 生成发布日志

为了更好地记录和追踪每次发布的内容,我们需要生成详细的发布日志。这里我们可以使用 Git 的 commit history 和 tags 来生成发布日志。

修改 .github/workflows/python-publish.yml

# .github/workflows/python-publish.yml
name: Publish Python Package
on:
release:
types: [published]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
with:
fetch-depth: 0 # 获取所有提交记录
- name: Set up Python 3.x
uses: actions/setup-python@v4
with:
python-version: '3.x'
- name: Install dependencies
run: |
python -m pip install --upgrade pip
python -m pip install setuptools wheel twine
- name: Build package
run: python setup.py sdist bdist_wheel
- name: Publish package to PyPI
uses: pypa/gh-action-pypi-publish@release/v1
with:
password: ${{ secrets.PYPI_API_TOKEN }}
- name: Generate Release Notes
id: generate_release_notes
run: |
TAG_NAME=${GITHUB_REF#refs/*/} # 获取tag名称
PREVIOUS_TAG=$(git describe --abbrev=0 --tags $TAG_NAME^)
echo "PREVIOUS_TAG=$PREVIOUS_TAG" >> $GITHUB_ENV
echo "TAG_NAME=$TAG_NAME" >> $GITHUB_ENV
# 获取自上次发布以来的提交信息
CHANGELOG=$(git log --pretty=format:"- %s" ${PREVIOUS_TAG}..${TAG_NAME})
echo "changelog<<EOF" >> $GITHUB_ENV
echo "$CHANGELOG" >> $GITHUB_ENV
echo "EOF" >> $GITHUB_ENV
- name: Update Release with Changelog
uses: actions/github-script@v6
with:
script: |
const github = require('@actions/github')
const {PREVIOUS_TAG, TAG_NAME, changelog} = process.env
const release_body = `## Release Notes for ${TAG_NAME}\n\nCommits since ${PREVIOUS_TAG}:\n${changelog}`
const { owner, repo } = github.context.repo;
const { data: release } = await github.getOctokit().rest.repos.getReleaseByTag({
owner, repo, tag: TAG_NAME,
});
await github.getOctokit().rest.repos.updateRelease({
owner, repo, release_id: release.id,
body: release_body
});

修改详解:

  1. fetch-depth: 0: 在 actions/checkout@v3 步骤中添加 fetch-depth: 0,确保获取完整的 Git 历史记录,以便后续生成发布日志。
  2. Generate Release Notes: 添加一个名为 Generate Release Notes 的步骤,用于生成发布日志。
    • 使用 ${GITHUB_REF#refs/*} 获取当前 Release 的 Tag 名称。
    • 使用 git describe --abbrev=0 --tags $TAG_NAME^ 获取上一个 Tag 名称。
    • 使用 git log --pretty=format:"- %s" ${PREVIOUS_TAG}..${TAG_NAME} 获取自上次发布以来的所有提交信息,并格式化为 Markdown 列表。
    • 将获取到的 Tag 名称、上一个 Tag 名称和提交信息存储到环境变量中,以便后续步骤使用。
  3. Update Release with Changelog: 添加一个名为 Update Release with Changelog 的步骤,用于更新 GitHub Release 的描述信息。
    • 使用 actions/github-script@v6 Action,执行 JavaScript 代码。
    • 从环境变量中获取 Tag 名称、上一个 Tag 名称和提交信息。
    • 构建 Release 的描述信息,包括 Tag 名称和提交信息。
    • 使用 GitHub API 获取当前 Release 的 ID。
    • 使用 GitHub API 更新 Release 的描述信息。

工作原理:

  • 当发布一个新的 Release 时,GitHub Actions 会自动触发 python-publish.yml 工作流。
  • Generate Release Notes 步骤会获取当前 Release 的 Tag 名称、上一个 Tag 名称和提交信息,并将这些信息存储到环境变量中。
  • Update Release with Changelog 步骤会从环境变量中获取 Tag 名称、上一个 Tag 名称和提交信息,构建 Release 的描述信息,并使用 GitHub API 更新 Release 的描述信息。

发布日志示例:

假设我们发布了 v0.0.2 版本,并且自 v0.0.1 版本以来有以下提交:

  • Fix bug in greet function
  • Add new feature: farewell function
  • Update documentation

那么生成的发布日志如下:

## Release Notes for v0.0.2
Commits since v0.0.1:
- Fix bug in greet function
- Add new feature: farewell function
- Update documentation

7. 总结与最佳实践

本文详细介绍了如何使用 GitHub Actions 自动化发布 Python 包到 PyPI,并生成详细的发布日志。通过使用 CI/CD,可以大大提高开发效率和代码质量。以下是一些最佳实践:

  • 使用 API Token: 强烈建议使用 PyPI API Token 进行身份验证,而不是用户名和密码。
  • 添加测试: 在发布之前,确保你的代码通过了所有测试。可以在 GitHub Actions 中添加测试步骤,例如使用 pytest 运行测试用例。
  • 代码审查: 在合并代码之前,进行代码审查,以确保代码质量。
  • 版本控制: 使用语义化版本控制 (Semantic Versioning),清晰地标识每个版本的功能和变更。
  • 文档更新: 在发布新版本时,及时更新文档,以便用户了解最新的功能和用法。
  • 自定义发布流程: 根据项目的实际情况,可以自定义发布流程,例如添加代码风格检查、安全扫描等步骤。

通过本文的学习,相信你已经掌握了如何使用 GitHub Actions 自动化发布 Python 包到 PyPI,并生成详细的发布日志。希望这些知识能帮助你更好地维护开源项目,提高开发效率和代码质量。

开源自动化大师 GitHub ActionsPyPI自动化发布

评论点评

打赏赞助
sponsor

感谢您的支持让我们更好的前行

分享

QRcode

https://www.webkt.com/article/9475