从PyPI手动发布到GitHub Actions全流程

作为Python开发者,将自己的工具包发布到PyPI(Python Package Index)是分享代码的重要方式。然而,手动发布过程繁琐且容易出错。本文将带你了解如何:

  1. 手动将Python包发布到PyPI
  2. 使用GitHub Actions实现自动化构建和发布
  3. 解决发布过程中的常见问题

基于Demo分享本教程:https://github.com/Muieay/magic-tools

第一部分:手动发布到PyPI

1. 创建项目结构

官方推荐的一个标准的Python包结构如下:

my-package/
├── LICENSE
├── README.md
├── pyproject.toml
├── src/
│   └── my_package/
│       ├── __init__.py
│       └── module.py
└── tests/

注意事项:

  1. pypi官方包名通常建议使用短横线(-)作为分隔符。
  2. 在Python 的导入语句要求使用下划线(_)作为模块名的分隔符,因为 Python 标识符不允许使用短横线。
  3. PyPI 包名:my-package
  4. Python 模块名:my_package ,magic_tools

2. 配置pyproject.toml

[build-system]
requires = ["setuptools>=42", "wheel"]
build-backend = "setuptools.build_meta"

[project]
name = "my-awesome-package"
version = "0.1.0"
authors = [
  { name="Your Name", email="your.email@example.com" },
]
description = "An awesome Python package"
readme = "README.md"
requires-python = ">=3.8"
classifiers = [
    "Programming Language :: Python :: 3",
    "License :: OSI Approved :: MIT License",
    "Operating System :: OS Independent",
]

[project.urls]
Homepage = "https://github.com/yourusername/my-package"
Bug Tracker = "https://github.com/yourusername/my-package/issues"

踩坑点:

  1. 项目名称(name )冲突或者相似度过高都会导致无法上传pypi仓库。
  2. version要及时更新,否则会导致项目无法上传。
  3. setuptools 默认会打包 src 目录下的包,或者当前目录下有 __init__.py 文件的目录。
  4. pyproject.toml 多个许可证文件冲突。
[project]
classifiers = [
    "Programming Language :: Python :: 3",
    #  "License :: OSI Approved :: MIT License",  # 移除这个,采用指定许可证文件形式
    "Operating System :: OS Independent",
]
license = {file = "LICENSE"} #多个许可证文件冲突,指定许可证文件

packages = ["magic-tools"]  # 明确指定要打包的包(如非src目录)

3. 构建和发布

在执行发布之前,要先注册pypi账号,配置.pypirc文件环境(此处就不过多赘述)。

# 安装构建工具
pip install build twine

# 构建包
python -m build

# 检查构建结果
twine check dist/*

# 上传到PyPI
twine upload dist/*

第二部分:自动化发布(GitHub Actions)

1.配置PyPI API Token

2. 创建GitHub Actions工作流

此工作流根据推送git tab执行自动化打包发布。

.github/workflows/publish.yml中添加:

name: Upload Python Package

on:
  push:
    tags:
      - "v*"  # 匹配v开头的标签,根据你git tag的命名规则设置

permissions:
  contents: write
  id-token: write

jobs:
  release-build:
    runs-on: ubuntu-latest

    steps:
      - uses: actions/checkout@v4

      - name: Clean previous builds
        run: |
          rm -rf dist/ build/ *.egg-info/

      - uses: actions/setup-python@v5
        with:
          python-version: "3.x"
          
      - name: Install dependencies
        run: |
          python -m pip install --upgrade pip
          python -m pip install -r requirements.txt
          pip install setuptools wheel twine

      - name: Build release distributions
        run: |
          python -m pip install build
          rm -rf dist/ build/ *.egg-info/ release-dists/ release-build/
          python -m build

      - name: Verify built version
        run: |
          ls -l dist/
          unzip -p dist/*.whl */METADATA | grep Version

      - name: Upload distributions
        uses: actions/upload-artifact@v4
        with:
          name: release-dists
          path: dist/

  pypi-publish:
    runs-on: ubuntu-latest
    needs:
      - release-build
    permissions:
      id-token: write

    environment:
      name: pypi
      url: https://pypi.org/project/my-package/${{ github.ref_name }}

    steps:
      - name: Retrieve release distributions
        uses: actions/download-artifact@v4
        with:
          name: release-dists
          path: dist/

      - name: Publish release distributions to PyPI
        uses: pypa/gh-action-pypi-publish@release/v1
        with:
          packages-dir: dist/
          skip-existing: false
          verbose: true

3. 使用可信发布(更安全的方式)

这个方式无需配置PyPI API Token。

在PyPI配置可信发布者:

  1. 将项目推送至Github
  2. 在PyPi设置里找到“受信任的发布者管理”(Trusted Publisher Management)
  3. 填写Github仓库信息和工作流路径

第三部分:自动化发布流程

1. 版本管理最佳实践

magic-tools的demo版本号直接在pyproject.toml中,直接修改即可,下面提供在pyproject.toml中动态获取版本的方法。每种方法见仁见智,适合自己就好。

src/my_package/__init__.py中定义版本号:

__version__ = "0.1.0"

pyproject.toml中动态获取版本:

[tool.setuptools.dynamic]
version = {attr = "my_package.__version__"}

2. 完整发布流程

以下基于第二部分.github/workflows/publish.yml,关键要素第3、第4点。

# 1. 更新版本号
echo "__version__ = '0.1.1'" > src/my_package/__init__.py

# 2. 提交更改
git add .
git commit -m "Bump version to 0.1.1"

# 3. 创建标签
git tag -a v0.1.1 -m "Release version 0.1.1"

# 4. 推送标签触发工作流
git push origin v0.1.1

3. 添加自动发布GitHub Release(可选)

在publish.yml中添加:

- name: Create GitHub Release
  uses: softprops/action-gh-release@v1
  with:
    name: Release ${{ github.ref_name }}
    tag_name: ${{ github.ref_name }}
    body: Auto-generated release
    files: dist/*
  env:
    GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

第四部分:常见问题解决

1. 名称冲突

HTTPError: 400 Bad Request: 
The name 'my-package' is too similar to an existing project.

解决方案:修改包名为更独特的名称,如yourname-package

2. 文件已存在

400 File already exists ('my_package-0.1.1-py3-none-any.whl')

解决方案

  1. 检查PyPI是否已有该版本
  2. 更新版本号重新发布
  3. 删除旧标签重新创建

3. 元数据错误

InvalidDistribution: Invalid distribution metadata: 
unrecognized or malformed field 'license-file'

解决方案:正确配置许可证(先移除classifiers中相关许可证的配置)

[project]
license = {text = "MIT"}
# 或指定许可证文件
license = {file = "LICENSE"}

4. 可信发布问题

Token request failed: invalid-publisher

解决方案

  1. 确保PyPI可信发布配置与GitHub仓库匹配
  2. 检查环境名称和工作流路径
  3. 确保工作流中有permissions: id-token: write

第五部分:高级技巧

1. 多环境发布

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - run: pytest
      
  publish:
    needs: test
    runs-on: ubuntu-latest
    environment: production
    # ...

2. 自动生成变更日志

- name: Generate changelog
  uses: mikepenz/release-changelog-builder-action@v3
  with:
    token: ${{ secrets.GITHUB_TOKEN }}

3. 版本验证

- name: Verify version
  run: |
    TAG_VERSION=${GITHUB_REF#refs/tags/v}
    PY_VERSION=$(python -c "from my_package import __version__; print(__version__)")
    if [ "$TAG_VERSION" != "$PY_VERSION" ]; then
      echo "版本不匹配: tag=v$TAG_VERSION vs package=$PY_VERSION"
      exit 1
    fi

5. pip install 找不到包或版本

可能使用了第三方的镜像源,更新不及时。需切换会官方源:

pip install xxxxxx -i https://pypi.org/simple/

结语

通过GitHub Actions实现Python包的自动化发布,你可以:

  1. 提高效率:从手动发布到一键发布
  2. 减少错误:标准化构建和发布流程
  3. 增强安全性:使用可信发布避免凭证泄露
  4. 保持一致性:确保每次发布都经过相同流程

现在,你可以专注于代码开发,而将发布工作交给自动化流程。每次推送标签时,GitHub Actions都会自动构建并发布新版本到PyPI,让你的用户始终能使用最新版本的包。

小贴士:首次发布建议使用TestPyPI测试完整流程:
twine upload --repository testpypi dist/*

希望本指南能帮助你顺利发布Python包!如果有任何问题,欢迎在评论区讨论。

posted @ 2025-07-10 17:41  Muieay  阅读(34)  评论(0)    收藏  举报