mcpField
P
python3-publish-release-pipeline

python3-publish-release-pipeline

github.com/jamie-bitflight云与 DevOps35 次安装
代码仓库

关于 python3-publish-release-pipeline

用于配置 Python 包 CI/CD 发布流水线的技能,可识别 GitHub Actions 或 GitLab CI,并提供测试、检查、发布、版本管理和发布文档的配置指导。

主要功能

  1. 01配置 GitHub Actions 和 GitLab CI
  2. 02测试与代码检查工作流
  3. 03配置 PyPI 发布
  4. 04通过 Git 标签管理版本
  5. 05编写发布流程文档
  6. 06Trusted Publishing 指引

适用场景

  1. 01自动化发布 Python 包
  2. 02设置 PyPI 发布流程
  3. 03通过 TestPyPI 测试发布
  4. 04记录基于标签的发布流程

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/jamie-bitflight/claude_skills --skill python3-publish-release-pipeline

安装后在 Claude Code 中输入/python3-publish-release-pipeline确认已加载。

详细介绍

<pipeline_target>$ARGUMENTS</pipeline_target>

Python 发布流水线配置

该模型负责配置 CI/CD 流水线,以自动发布 Python 软件包。

参数

<pipeline_target/>

如果未提供参数,则从仓库中检测(查找 .github/ 或 .gitlab-ci.yml)。

说明

应用通用架构、类型标注、测试或 CLI 规则时,请加载并遵循 /python-engineering:standards-for-python-development 中的标准。

  1. 检测 CI 平台(GitHub Actions 或 GitLab CI)
  2. 创建工作流文件,用于测试、Lint 和发布
  3. 编写 PyPI 令牌密钥的配置文档
  4. 使用 git 标签设置版本管理
  5. 编写发布流程文档

GitHub Actions 流水线

目录结构
.github/
├── workflows/
│   ├── ci.yml           # Run on every push/PR
│   ├── release.yml      # Run on version tags
│   └── docs.yml         # Optional: documentation
└── dependabot.yml       # Optional: dependency updates
CI 工作流(ci.yml)
name: CI

on:
  push:
    branches: [main]
  pull_request:
    branches: [main]

jobs:
  lint:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Install uv
        uses: astral-sh/setup-uv@v4
        with:
          enable-cache: true

      - name: Set up Python
        run: uv python install 3.11

      - name: Install dependencies
        run: uv sync --all-extras

      - name: Run ruff
        run: |
          uv run ruff check --no-fix src/ tests/
          uv run ruff format --check src/ tests/

      - name: Run type check (ty default; swap to mypy if hooks/CI run mypy — not merely [tool.mypy])
        run: uv run ty check src/ tests/

  test:
    runs-on: ubuntu-latest
    strategy:
      fail-fast: false
      matrix:
        python-version: ["3.11", "3.12", "3.13"]

    steps:
      - uses: actions/checkout@v4

      - name: Install uv
        uses: astral-sh/setup-uv@v4
        with:
          enable-cache: true

      - name: Set up Python ${{ matrix.python-version }}
        run: uv python install ${{ matrix.python-version }}

      - name: Install dependencies
        run: uv sync --all-extras

      - name: Run tests
        run: uv run pytest tests/ -v --cov=src --cov-report=xml

      - name: Upload coverage
        uses: codecov/codecov-action@v4
        with:
          files: coverage.xml
          fail_ci_if_error: false
发布工作流(release.yml)
name: Release

on:
  push:
    tags:
      - "v*.*.*"

permissions:
  contents: write
  id-token: write  # Required for trusted publishing

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: astral-sh/setup-uv@v4
      - run: uv python install 3.11
      - run: uv sync --all-extras
      - run: uv run ruff check --no-fix src/ tests/
      - run: uv run ruff format --check src/ tests/
      - run: uv run ty check src/ tests/
      - run: uv run pytest tests/ --cov=src --cov-report=term-missing

  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Install uv
        uses: astral-sh/setup-uv@v4

      - name: Set up Python
        run: uv python install 3.11

      - name: Build package
        run: uv build

      - name: Upload artifacts
        uses: actions/upload-artifact@v4
        with:
          name: dist
          path: dist/

  # Option 1: Trusted Publishing (Recommended)
  publish-pypi:
    # Publish only an artifact from this release workflow after its tests pass.
    needs: [build, test]
    runs-on: ubuntu-latest
    environment:
      name: pypi
      url: https://pypi.org/p/your-package
    steps:
      - name: Download artifacts
        uses: actions/download-artifact@v4
        with:
          name: dist
          path: dist/

      - name: Publish to PyPI
        uses: pypa/gh-action-pypi-publish@release/v1
        # No token needed with trusted publishing!

  # Option 2: Token-based Publishing (Alternative)
  # publish-pypi:
  #   needs: [build, test]
  #   runs-on: ubuntu-latest
  #   steps:
  #     - name: Download artifacts
  #       uses: actions/download-artifact@v4
  #       with:
  #         name: dist
  #         path: dist/
  #
  #     - name: Publish to PyPI
  #       uses: pypa/gh-action-pypi-publish@release/v1
  #       with:
  #         password: ${{ secrets.PYPI_API_TOKEN }}

  github-release:
    needs: [build, publish-pypi]
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Download artifacts
        uses: actions/download-artifact@v4
        with:
          name: dist
          path: dist/

      - name: Create GitHub Release
        uses: softprops/action-gh-release@v2
        with:
          files: dist/*
          generate_release_notes: true

GitLab CI 流水线

.gitlab-ci.yml
stages:
  - lint
  - test
  - build
  - publish

variables:
  UV_CACHE_DIR: "$CI_PROJECT_DIR/.uv-cache"
  PIP_CACHE_DIR: "$CI_PROJECT_DIR/.pip-cache"

default:
  image: python:3.11-slim
  before_script:
    - pip install uv
    - uv sync --all-extras
  cache:
    key: "${CI_JOB_NAME}"
    paths:
      - .uv-cache/
      - .pip-cache/
      - .venv/

lint:
  stage: lint
  script:
    - uv run ruff check --no-fix src/ tests/
    - uv run ruff format --check src/ tests/
    - uv run ty check src/ tests/

test:
  stage: test
  parallel:
    matrix:
      - PYTHON_VERSION: ["3.11", "3.12", "3.13"]
  image: python:${PYTHON_VERSION}-slim
  script:
    - uv run pytest tests/ -v --cov=src --cov-report=xml --junitxml=report.xml
  coverage: '/TOTAL.*\s+(\d+%)/'
  artifacts:
    reports:
      junit: report.xml
      coverage_report:
        coverage_format: cobertura
        path: coverage.xml

build:
  stage: build
  script:
    - uv build
  artifacts:
    paths:
      - dist/
    expire_in: 1 week

# Publish to PyPI on tags
publish:pypi:
  stage: publish
  rules:
    - if: $CI_COMMIT_TAG =~ /^v\d+\.\d+\.\d+$/
  script:
    - uv publish --token $PYPI_TOKEN
  environment:
    name: pypi
    url: https://pypi.org/project/your-package

# Publish to GitLab Package Registry on tags
publish:gitlab:
  stage: publish
  rules:
    - if: $CI_COMMIT_TAG =~ /^v\d+\.\d+\.\d+$/
  script:
    - |
      TWINE_PASSWORD=${CI_JOB_TOKEN} \
      TWINE_USERNAME=gitlab-ci-token \
      uv publish --publish-url ${CI_API_V4_URL}/projects/${CI_PROJECT_ID}/packages/pypi
  environment:
    name: gitlab-registry

可信发布设置(PyPI)

为什么使用可信发布?
  • 无需管理 API 令牌
  • 无需轮换密钥
  • 不会泄露
  • 限定用于特定工作流
设置步骤
  1. 前往 PyPI → 你的项目 → Publishing

  2. 添加 GitHub 发布者:

    • Owner:your-username
    • Repository:your-repo
    • Workflow name:release.yml
    • Environment:pypi(可选,但建议设置)
  3. 创建 GitHub 环境:

    • Settings → Environments → New environment:pypi
    • 添加保护规则(可选):
      • 必需的审核者
      • 仅限标签
替代方案:API 令牌

如果无法使用可信发布:

  1. 创建 PyPI 令牌:

    • PyPI → Account Settings → API tokens
    • 创建限定用于你的项目的令牌
  2. 添加到仓库密钥:

    • GitHub:Settings → Secrets → PYPI_API_TOKEN
    • GitLab:Settings → CI/CD → Variables → PYPI_TOKEN

版本管理

手动设置版本(pyproject.toml)

打标签前,在 pyproject.toml 中更新版本:

[project]
version = "1.2.3"
从 Git 标签动态获取版本

使用 hatch-vcs:

[build-system]
requires = ["hatchling", "hatch-vcs"]
build-backend = "hatchling.build"

[project]
dynamic = ["version"]

[tool.hatch.version]
source = "vcs"

[tool.hatch.build.hooks.vcs]
version-file = "src/my_package/_version.py"
发布工作流
# 1. Update CHANGELOG.md

# 2. Commit changes
git add CHANGELOG.md pyproject.toml
git commit -m "Prepare release v1.2.3"

# 3. Create annotated tag
git tag -a v1.2.3 -m "Release v1.2.3"

# 4. Push with tags
git push origin main
git push origin v1.2.3

使用 TestPyPI 进行测试

GitHub Actions TestPyPI 作业
publish-testpypi:
  needs: build
  runs-on: ubuntu-latest
  environment:
    name: testpypi
    url: https://test.pypi.org/p/your-package
  steps:
    - uses: actions/download-artifact@v4
      with:
        name: dist
        path: dist/

    - uses: pypa/gh-action-pypi-publish@release/v1
      with:
        repository-url: https://test.pypi.org/legacy/
手动上传到 TestPyPI
# Build
uv build

# Upload to TestPyPI
uv publish --publish-url https://test.pypi.org/legacy/ --token $TESTPYPI_TOKEN

# Test install
uv pip install --index-url https://test.pypi.org/simple/ your-package

必需的配置文件

发布所需的最少文件
your-package/
├── pyproject.toml          # Package metadata and build config
├── README.md               # Required by PyPI
├── LICENSE                 # Required for distribution
├── src/
│   └── your_package/
│       ├── __init__.py
│       └── py.typed        # PEP 561 marker
└── .github/
    └── workflows/
        ├── ci.yml
        └── release.yml
pyproject.toml 检查清单
- [ ] [build-system] with requires and build-backend
- [ ] [project] with name, version, description
- [ ] readme = "README.md"
- [ ] license specified
- [ ] requires-python = ">=3.11"
- [ ] authors with name and email
- [ ] classifiers (Development Status, License, Python versions)
- [ ] dependencies list
- [ ] [project.urls] with Documentation, Issues, Source
- [ ] [project.scripts] if CLI tool

安全最佳实践

  1. 优先使用可信发布,而非 API 令牌
  2. 将令牌限定用于特定项目(而非整个账户)
  3. 使用带保护规则的环境
  4. 使用完整 SHA 或版本标签固定 Action 版本
  5. 仔细审核工作流变更
  6. 为 main 启用分支保护

故障排除

构建失败
# Check package metadata
uv run python -m build --no-isolation
uvx twine check dist/*
上传失败
# Never print any portion of a publishing credential.
# Verify authentication only by a non-secret-bearing API/tool response.

# Check package name availability
curl https://pypi.org/pypi/your-package/json
版本已存在

PyPI 不允许重新上传相同版本。请递增版本号并创建新标签。


参考资料

评论

支持 Markdown