从PyPI手动发布到GitHub Actions全流程
作为Python开发者,将自己的工具包发布到PyPI(Python Package Index)是分享代码的重要方式。然而,手动发布过程繁琐且容易出错。本文将带你了解如何:
- 手动将Python包发布到PyPI
- 使用GitHub Actions实现自动化构建和发布
- 解决发布过程中的常见问题
基于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/
注意事项:
- pypi官方包名通常建议使用短横线(-)作为分隔符。
- 在Python 的导入语句要求使用下划线(_)作为模块名的分隔符,因为 Python 标识符不允许使用短横线。
- PyPI 包名:my-package。
- 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"
踩坑点:
- 项目名称(name )冲突或者相似度过高都会导致无法上传pypi仓库。
- version要及时更新,否则会导致项目无法上传。
- setuptools 默认会打包 src目录下的包,或者当前目录下有__init__.py文件的目录。
- 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配置可信发布者:
- 将项目推送至Github
- 在PyPi设置里找到“受信任的发布者管理”(Trusted Publisher Management)
- 填写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')
解决方案:
- 检查PyPI是否已有该版本
- 更新版本号重新发布
- 删除旧标签重新创建
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
解决方案:
- 确保PyPI可信发布配置与GitHub仓库匹配
- 检查环境名称和工作流路径
- 确保工作流中有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包的自动化发布,你可以:
- 提高效率:从手动发布到一键发布
- 减少错误:标准化构建和发布流程
- 增强安全性:使用可信发布避免凭证泄露
- 保持一致性:确保每次发布都经过相同流程
现在,你可以专注于代码开发,而将发布工作交给自动化流程。每次推送标签时,GitHub Actions都会自动构建并发布新版本到PyPI,让你的用户始终能使用最新版本的包。
小贴士:首次发布建议使用TestPyPI测试完整流程:
twine upload --repository testpypi dist/*
希望本指南能帮助你顺利发布Python包!如果有任何问题,欢迎在评论区讨论。
 
                    
                
 
                
            
         浙公网安备 33010602011771号
浙公网安备 33010602011771号