Git + Python 项目工作流最佳实践:pre-commit、CI、CHANGELOG 自动化

🔧 Git + Python 项目工作流最佳实践:pre-commit、CI、CHANGELOG 自动化


🌸你好呀!我是 lbb小魔仙
🌟 感谢陪伴~ 小白博主在线求友
🌿 跟着小白学Linux/Java/Python
📖 专栏汇总:
《Linux》专栏 | 《Java》专栏 | 《Python》专栏

  • [🔧 Git + Python 项目工作流最佳实践:pre-commit、CI、CHANGELOG 自动化](#🔧 Git + Python 项目工作流最佳实践:pre-commit、CI、CHANGELOG 自动化)
    • 摘要
    • 内容
    • 一、为什么需要工程化工作流?
      • [1.1 团队协作的痛点](#1.1 团队协作的痛点)
      • [1.2 理想的工作流](#1.2 理想的工作流)
    • [二、🔗 pre-commit:本地提交的第一道防线](#二、🔗 pre-commit:本地提交的第一道防线)
      • [2.1 pre-commit 是什么?](#2.1 pre-commit 是什么?)
      • [2.2 安装与初始化](#2.2 安装与初始化)
      • [2.3 推荐的 Python 项目配置](#2.3 推荐的 Python 项目配置)
      • [2.4 各钩子的职责说明](#2.4 各钩子的职责说明)
      • [2.5 跳过钩子的正确姿势](#2.5 跳过钩子的正确姿势)
      • [2.6 CI 中验证 pre-commit](#2.6 CI 中验证 pre-commit)
    • [三、📋 Conventional Commits:让提交信息有意义](#三、📋 Conventional Commits:让提交信息有意义)
      • [3.1 标准格式](#3.1 标准格式)
      • [3.2 优秀 vs 糟糕的提交信息](#3.2 优秀 vs 糟糕的提交信息)
      • [3.3 使用 commitizen 规范提交](#3.3 使用 commitizen 规范提交)
      • [3.4 自动化校验](#3.4 自动化校验)
    • [四、📝 CHANGELOG 自动化](#四、📝 CHANGELOG 自动化)
      • [4.1 使用 git-cliff 生成 CHANGELOG](#4.1 使用 git-cliff 生成 CHANGELOG)
      • [4.2 配置文件](#4.2 配置文件)
      • [4.3 使用方式](#4.3 使用方式)
      • [4.4 生成效果示例](#4.4 生成效果示例)
      • [4.5 版本发布工作流整合](#4.5 版本发布工作流整合)
    • [五、🤖 CI/CD 集成](#五、🤖 CI/CD 集成)
      • [5.1 Python 项目 CI 模板(GitHub Actions)](#5.1 Python 项目 CI 模板(GitHub Actions))
      • [5.2 工作流说明](#5.2 工作流说明)
      • [5.3 自动化依赖更新(Dependabot)](#5.3 自动化依赖更新(Dependabot))
    • [六、🔒 安全与凭证管理](#六、🔒 安全与凭证管理)
      • [6.1 使用 .gitignore 防止误提交](#6.1 使用 .gitignore 防止误提交)
      • [6.2 使用 pre-commit 检测凭证泄露](#6.2 使用 pre-commit 检测凭证泄露)
      • [6.3 Git Hooks 防御链](#6.3 Git Hooks 防御链)
    • [七、🏗️ Branch Strategy 与工作流](#七、🏗️ Branch Strategy 与工作流)
      • [7.1 推荐结构:GitHub Flow(简化版)](#7.1 推荐结构:GitHub Flow(简化版))
      • [7.2 PR 模板](#7.2 PR 模板)
    • [八、📦 pyproject.toml 统一配置](#八、📦 pyproject.toml 统一配置)
    • [九、🚀 快速初始化项目脚本](#九、🚀 快速初始化项目脚本)
    • 十、常见问题与最佳实践
      • [10.1 pre-commit 运行太慢怎么办?](#10.1 pre-commit 运行太慢怎么办?)
      • [10.2 如何回退被 pre-commit 修改的代码?](#10.2 如何回退被 pre-commit 修改的代码?)
      • [10.3 CI 和本地环境不一致怎么办?](#10.3 CI 和本地环境不一致怎么办?)
      • [10.4 多项目如何管理 pre-commit 配置?](#10.4 多项目如何管理 pre-commit 配置?)
    • 十一、总结

摘要

代码风格不统一、调试代码被误提交、CI 反复失败、CHANGELOG 靠手动整理------这些团队协作中的常见痛点,根源在于缺乏工程化的自动化工作流。本文从 pre-commit 本地钩子配置开始,系统讲解 Git + Python 项目的全链路工程化实践,涵盖 Conventional Commits 规范提交、commitizen 交互式工具、git-cliff CHANGELOG 自动生成、GitHub Actions CI/CD 流水线配置、Dependabot 依赖更新、安全凭证检测、分支策略和 PR 模板规范等内容,附一键项目初始化脚本,帮助团队建立从本地到线上的全自动质量保障体系。

🚀 个人主页有点流鼻涕 · CSDN

💬 座右铭 : "向光而行,沐光而生。"


内容

一、为什么需要工程化工作流?

1.1 团队协作的痛点

在实际项目中,下面这些场景你是否遇到过?

  • 代码风格不统一:有人用 2 空格缩进,有人用 4 空格,有人用 Tab;字符串引号单双混用
  • 调试代码被提交print()breakpoint() 进入生产分支
  • CI 反复失败:本地明明能跑,推到 CI 就红,原因是依赖没锁死或格式没检查
  • CHANGELOG 靠记忆:发版时翻 commit log 手动整理,漏掉重要变更
  • 安全凭证泄露.env 或密钥文件被误提交到仓库

1.2 理想的工作流

复制代码
本地开发 → pre-commit 钩子自动检查 → 提交 → CI 自动验证 → Code Review → Merge → 自动生成 CHANGELOG
  ▲                                                                                               │
  └────────────────────────────────────── 持续迭代 ────────────────────────────────────────────────┘

🔑 核心理念 :把一切能自动化的事情都交给机器。人的精力应该集中在代码逻辑架构设计上,而不是格式、漏测、版本号这些重复劳动。


二、🔗 pre-commit:本地提交的第一道防线

2.1 pre-commit 是什么?

pre-commit 是一个多语言框架,用于管理 Git 钩子。它允许你在每次 git commit 之前自动执行一系列检查和格式化操作。

与传统 Git 钩子的区别

特性 手动 .git/hooks/pre-commit pre-commit 框架
是否版本控制 .git/ 目录不被跟踪 ✅ 通过 .pre-commit-config.yaml 管理
团队成员同步 手动复制脚本 ✅ 自动安装相同版本
钩子来源 自己写脚本 ✅ 社区 3000+ 现成钩子
语言无关 通常 Shell ✅ 支持 Python/Node/Ruby/Go 等
环境隔离 全局环境 ✅ 每个钩子独立运行

2.2 安装与初始化

bash 复制代码
# 安装 pre-commit
pip install pre-commit

# 在项目根目录创建配置文件
touch .pre-commit-config.yaml

# 安装 Git 钩子(会在 .git/hooks/ 中注册)
pre-commit install

# (可选)对已有仓库运行一次全量检查
pre-commit run --all-files

2.3 推荐的 Python 项目配置

yaml 复制代码
# .pre-commit-config.yaml
repos:
  # === 通用基础检查 ===
  - repo: https://github.com/pre-commit/pre-commit-hooks
    rev: v4.6.0
    hooks:
      - id: trailing-whitespace       # 修剪行尾多余空格
      - id: end-of-file-fixer         # 确保文件末尾有换行
      - id: check-yaml                # 检查 YAML 文件语法
      - id: check-json                # 检查 JSON 文件语法
      - id: check-toml                # 检查 TOML 文件语法
      - id: check-added-large-files   # 阻止大文件被提交(默认 500KB)
        args: ["--maxkb=1024"]
      - id: check-merge-conflict      # 检查是否有未解决的合并冲突
      - id: detect-private-key        # 检查是否意外提交了私钥
      - id: check-case-conflict       # 检查是否存在大小写冲突的文件名(Windows/macOS 相关)
      - id: mixed-line-ending         # 统一换行符(跨平台相关)

  # === Python 格式化(自动修复)===
  - repo: https://github.com/psf/black
    rev: 24.4.2
    hooks:
      - id: black
        args: ["--line-length=88", "--target-version=py312"]
        language_version: python3.12

  # === import 排序(自动修复)===
  - repo: https://github.com/PyCQA/isort
    rev: 5.13.2
    hooks:
      - id: isort
        args: ["--profile=black", "--line-length=88"]

  # === 代码静态检查 ===
  - repo: https://github.com/PyCQA/flake8
    rev: 7.1.0
    hooks:
      - id: flake8
        args: ["--max-line-length=88", "--extend-ignore=E203,W503"]
        additional_dependencies: [flake8-docstrings]

  # === 类型检查(可选,运行时较慢)===
  - repo: https://github.com/pre-commit/mirrors-mypy
    rev: v1.10.0
    hooks:
      - id: mypy
        args: ["--strict", "--ignore-missing-imports"]
        additional_dependencies: [types-requests, types-pyyaml]
        # 类型检查较慢,设为手动触发
        stages: [manual]

  # === 安全扫描 ===
  - repo: https://github.com/Yelp/detect-secrets
    rev: v1.4.0
    hooks:
      - id: detect-secrets
        args: ["--baseline", ".secrets.baseline"]
        # 基线文件管理:detect-secrets scan > .secrets.baseline

  # === Dockerfile 检查(如果有 Docker)===
  - repo: https://github.com/hadolint/hadolint
    rev: v2.12.0
    hooks:
      - id: hadolint-docker

2.4 各钩子的职责说明

钩子 类型 作用 能否自动修复
trailing-whitespace 格式 删除行尾多余空格
end-of-file-fixer 格式 确保文件末尾空行
check-added-large-files 安全 阻止 >500KB 的文件 ❌ 阻止提交
detect-private-key 安全 检测私钥文件 ❌ 阻止提交
black 格式 Python 代码格式化
isort 格式 自动排序 import
flake8 质量 代码规范检查 ❌ 提示修改
mypy 类型 静态类型检查 ❌ 提示修改
detect-secrets 安全 检测凭证泄露 ❌ 阻止提交

2.5 跳过钩子的正确姿势

bash 复制代码
# 紧急情况(不推荐滥用)
git commit --no-verify -m "fix: hotfix for production issue"

# 只跳过特定钩子
SKIP=flake8,black git commit -m "feat: wip"

# 运行特定钩子
pre-commit run black --all-files

# 只对暂存的文件运行
pre-commit run --show-diff-on-failure

⚠️ 重要--no-verify 只应在生产热修复等极少数紧急场景使用。滥用它会破坏整个质量保障体系。

2.6 CI 中验证 pre-commit

在 CI 中运行 pre-commit,确保即使有人本地跳过了钩子,CI 也会拦住:

yaml 复制代码
# .github/workflows/ci.yml
name: CI

on: [push, pull_request]

jobs:
  lint:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-python@v5
        with:
          python-version: "3.12"
      - name: Install pre-commit
        run: pip install pre-commit
      - name: Run pre-commit
        run: pre-commit run --all-files --show-diff-on-failure

三、📋 Conventional Commits:让提交信息有意义

3.1 标准格式

一个规范的提交信息包含以下结构:

复制代码
<type>(<scope>): <subject>

<body>

<footer>

Type 类型标准(Angular 规范):

Type 含义 是否出现在 CHANGELOG 版本影响
feat 新功能 次版本号+1
fix Bug 修复 修订号+1
docs 文档变更 -
style 代码格式变更 -
refactor 重构 -
perf 性能优化 -
test 测试相关 -
build 构建/依赖变更 -
ci CI 配置变更 -
chore 杂项 -
revert 回滚 -

3.2 优秀 vs 糟糕的提交信息

text 复制代码
# ❌ 糟糕(看不出意图)
git commit -m "fix bug"
git commit -m "update"
git commit -m "改了"

# ❌ 过于冗长
git commit -m "修复了当用户输入为空字符串时程序崩溃的问题,
之前因为没做空值校验导致...(写了 500 字)"

# ✅ 优秀的提交
git commit -m "fix: 处理用户名为空时的输入验证异常

当用户名字段为空字符串时,validate_username() 会抛出
ValueError 而非返回 False,导致 HTTP 500。

- 在入口处增加空字符串前置校验
- 补充对应的单元测试用例
- Closes #1234"

3.3 使用 commitizen 规范提交

bash 复制代码
# 安装
pip install commitizen

# 交互式生成规范提交信息
cz commit

# 输出示例
# ? Select the type of change you're committing: feat
# ? What is the scope of this change: auth
# ? Write a short description: 添加 JWT Token 刷新接口
# ? Provide a longer description: ...
# ? Are there any breaking changes: No

3.4 自动化校验

在 pre-commit 中增加对提交信息的校验:

yaml 复制代码
# 添加到 .pre-commit-config.yaml
  - repo: https://github.com/commitizen-tools/commitizen
    rev: v3.27.0
    hooks:
      - id: commitizen
        stages: [commit-msg]

四、📝 CHANGELOG 自动化

4.1 使用 git-cliff 生成 CHANGELOG

git-cliff 是一个基于 Conventional Commits 自动生成 CHANGELOG 的工具。

bash 复制代码
# 安装
cargo install git-cliff

# 或使用 pip
pip install git-cliff

4.2 配置文件

toml 复制代码
# cliff.toml
[changelog]
header = "# 📋 CHANGELOG\n\n此文件记录了项目的所有重要变更。\n"
body = """
{% for group, commits in commits | group_by(attribute="group") %}
  ### {{ group | upper_first }}
  {% for commit in commits %}
    - {{ commit.message | upper_first }}\
      {% if commit.breaking %} (⚠️ 破坏性变更){% endif %}\
      {% if commit.scope %} ({{ commit.scope }}){% endif %}
  {% endfor %}
{% endfor %}
"""
footer = "---\n*自动生成于 {{ timestamp }}*\n"
# 禁止词
trim = true

[git]
conventional_commits = true
filter_unconventional = true

# 按类型分组
[commit_parser]
message = "^(:?feat|fix|docs|perf|revert|breaking):\\s?(.*)"
body = ".*"

[links]
# 可选的链接配置(GitHub Issues)
# reference = "https://github.com/你的用户名/仓库名/issues/{id}"

4.3 使用方式

bash 复制代码
# 基于所有标签生成(完整 CHANGELOG)
git-cliff -o CHANGELOG.md

# 只生成最新未发布的变更
git-cliff --unreleased -o CHANGELOG.md

# 筛选特定版本范围
git-cliff --tag v1.0.0..v1.1.0 -o CHANGELOG.md

# 追加到已有 CHANGELOG
git-cliff --prepend CHANGELOG.md

4.4 生成效果示例

markdown 复制代码
# 📋 CHANGELOG

### Features
- 添加 JWT Token 刷新接口 (auth)
- 支持批量导出功能 (export)
- 新增数据看板组件 (dashboard)

### Bug Fixes
- 修复用户名为空时的输入验证异常 (auth)
- 修复分页组件在数据不足时的显示异常 (ui)

### Performance
- 优化大列表渲染性能,减少 60% 重绘 (perf)

### ⚠️ Breaking Changes
- API 响应格式从 XML 变更为 JSON (api)

---
*自动生成于 2025-01-15*

4.5 版本发布工作流整合

bash 复制代码
#!/bin/bash
# release.sh - 一键发布脚本

set -e

# 1. 确认当前分支
BRANCH=$(git rev-parse --abbrev-ref HEAD)
if [ "$BRANCH" != "main" ] && [ "$BRANCH" != "master" ]; then
    echo "❌ 请在 main/master 分支上执行发布"
    exit 1
fi

# 2. 检查是否有未提交的变更
if [ -n "$(git status --porcelain)" ]; then
    echo "❌ 存在未提交的变更,请先提交或暂存"
    exit 1
fi

# 3. 读取当前版本号(假设使用 semver)
CURRENT_VERSION=$(git describe --tags --abbrev=0 2>/dev/null || echo "0.0.0")
echo "📦 当前版本: $CURRENT_VERSION"

# 4. 基于最近的 commit 类型自动升级版本
#    feat → minor, fix → patch, breaking → major
LATEST_COMMIT=$(git log -1 --pretty=%B)
if echo "$LATEST_COMMIT" | grep -q "BREAKING\|!"; then
    # major bump
    NEW_VERSION=$(echo "$CURRENT_VERSION" | awk -F. '{print $1+1".0.0"}')
elif echo "$LATEST_COMMIT" | grep -q "^feat"; then
    # minor bump
    NEW_VERSION=$(echo "$CURRENT_VERSION" | awk -F. '{print $1"."$2+1".0"}')
else
    # patch bump
    NEW_VERSION=$(echo "$CURRENT_VERSION" | awk -F. '{print $1"."$2"."$3+1}')
fi

echo "🎯 新版本: $NEW_VERSION"

# 5. 生成 CHANGELOG
git cliff --tag "$NEW_VERSION" --prepend CHANGELOG.md

# 6. 创建 tag 并推送
git add CHANGELOG.md
git commit -m "chore: release v$NEW_VERSION"
git tag -a "v$NEW_VERSION" -m "Release v$NEW_VERSION"
git push && git push --tags

echo "✅ 发布完成!版本 v$NEW_VERSION"

五、🤖 CI/CD 集成

5.1 Python 项目 CI 模板(GitHub Actions)

yaml 复制代码
# .github/workflows/ci.yml
name: Python CI

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

jobs:
  lint:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-python@v5
        with:
          python-version: "3.12"
      - name: Run pre-commit
        uses: pre-commit/action@v3.0.1

  test:
    needs: lint
    runs-on: ubuntu-latest
    strategy:
      matrix:
        python-version: ["3.11", "3.12"]
        os: [ubuntu-latest, windows-latest]

    steps:
      - uses: actions/checkout@v4

      - name: Set up Python ${{ matrix.python-version }}
        uses: actions/setup-python@v5
        with:
          python-version: ${{ matrix.python-version }}
          cache: "pip"

      - name: Install dependencies
        run: |
          python -m pip install --upgrade pip
          pip install -e ".[dev]"

      - name: Run tests with coverage
        run: |
          pytest --cov=src --cov-report=xml --cov-report=term-missing

      - name: Upload coverage
        uses: codecov/codecov-action@v4
        with:
          file: ./coverage.xml
          fail_ci_if_error: false

  security:
    needs: lint
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-python@v5
        with:
          python-version: "3.12"

      - name: Install bandit
        run: pip install bandit

      - name: Run bandit security scan
        run: bandit -r src/ -f json -o bandit_report.json || true

      - name: Upload security report
        uses: actions/upload-artifact@v4
        with:
          name: bandit-report
          path: bandit_report.json

5.2 工作流说明

Job 依赖 并行策略 超时设置
lint 单版本 5 min
test lint ✅ 多 Python 版本 × 多 OS 15 min
security lint ✅ 单次扫描 5 min

设计原则

  • lint 最快执行:格式问题不值得跑完整测试
  • test 矩阵化:确保在所有目标环境通过
  • 安全扫描独立:不影响测试流程,发现问题时不会阻塞其他检查
  • lint 失败则跳过 test:节省 CI 资源

5.3 自动化依赖更新(Dependabot)

yaml 复制代码
# .github/dependabot.yml
version: 2
updates:
  - package-ecosystem: "pip"
    directory: "/"
    schedule:
      interval: "weekly"
      day: "monday"
    open-pull-requests-limit: 10
    labels:
      - "dependencies"
      - "automated"
    # 对 dev 依赖分组(减少 PR 数量)
    groups:
      dev-dependencies:
        patterns:
          - "pytest*"
          - "flake8"
          - "black"
          - "isort"
          - "mypy"
          - "pre-commit"
  - package-ecosystem: "github-actions"
    directory: "/"
    schedule:
      interval: "monthly"

六、🔒 安全与凭证管理

6.1 使用 .gitignore 防止误提交

gitignore 复制代码
# .gitignore
# === 环境与凭证 ===
.env
.env.*
*.key
*.pem
credentials.json
service-account.json

# === 虚拟环境 ===
.venv/
venv/
env/

# === Python 构建产物 ===
__pycache__/
*.py[cod]
*.egg-info/
dist/
build/

# === IDE ===
.idea/
.vscode/
*.swp
*.swo

# === 操作系统 ===
.DS_Store
Thumbs.db

# === 其他 ===
*.log
.coverage
htmlcov/

6.2 使用 pre-commit 检测凭证泄露

yaml 复制代码
- repo: https://github.com/Yelp/detect-secrets
  rev: v1.4.0
  hooks:
    - id: detect-secrets
      args: ["--baseline", ".secrets.baseline"]

首次使用:

bash 复制代码
# 扫描当前项目,生成基线文件
detect-secrets scan > .secrets.baseline

# 检查基线中是否有误报,手动编辑 .secrets.baseline 标记
# 将 "is_secret": true 改为 false

# 提交基线文件
git add .secrets.baseline

6.3 Git Hooks 防御链

复制代码
本地修改 → 检测到文件变更
         ↓
    .gitignore 过滤敏感文件
         ↓
    pre-commit 钩子
      ├─ detect-secrets → 扫描凭证
      ├─ check-added-large-files → 阻止大文件
      └─ detect-private-key → 检测私钥
         ↓
    提交成功 → 推送到远程
         ↓
    GitHub Secret Scanning(自动)
         ↓
    CI 中的安全扫描(bandit / truffleHog)

七、🏗️ Branch Strategy 与工作流

7.1 推荐结构:GitHub Flow(简化版)

复制代码
main ─── feat-A ── feat-A ── feat-A ── merge ──────────── main ── ...
         ↑ 创建        ↑ 开发        ↑ PR         ↑ 合并部署
bash 复制代码
# 完整流程
git checkout -b feat/user-authentication

# 多次小提交(每次 pre-commit 都检查)
git commit -m "feat: 添加用户模型"
git commit -m "feat: 实现 JWT 生成逻辑"
git commit -m "feat: 添加登录接口"

# 推送到远程,创建 PR
git push -u origin feat/user-authentication

# PR 通过后,在 GitHub 上 squash merge
# 然后删除远程分支
git branch -d feat/user-authentication
git push origin --delete feat/user-authentication

7.2 PR 模板

markdown 复制代码
<!-- .github/PULL_REQUEST_TEMPLATE.md -->

## 📝 描述

请简要描述本次 PR 的内容和动机。

Closes #(issue)

## 🎯 变更类型

- [ ] feat: 新功能
- [ ] fix: Bug 修复
- [ ] refactor: 重构
- [ ] perf: 性能优化
- [ ] docs: 文档
- [ ] test: 测试
- [ ] chore: 杂项

## ✅ 检查清单

- [ ] 我的代码遵循项目代码规范
- [ ] 我已经运行了 `pre-commit run --all-files`
- [ ] 我已经添加/更新了相关测试
- [ ] 所有测试均已通过
- [ ] 我已经更新了相关文档
- [ ] 我已经在 CHANGELOG 中添加了变更记录

## 🧪 测试说明

如何验证本次变更?

八、📦 pyproject.toml 统一配置

把工具配置集中到 pyproject.toml,避免项目根目录散落配置文件:

toml 复制代码
# pyproject.toml
[build-system]
requires = ["setuptools>=68.0"]
build-backend = "setuptools.backends._legacy:_Backend"

[project]
name = "my-project"
version = "0.1.0"
requires-python = ">=3.11"
dependencies = [
    "requests>=2.31",
    "pydantic>=2.0",
]

[project.optional-dependencies]
dev = [
    "pytest>=8.0",
    "pytest-cov>=5.0",
    "black>=24.0",
    "isort>=5.13",
    "flake8>=7.0",
    "mypy>=1.10",
    "pre-commit>=3.7",
    "commitizen>=3.27",
]

# === Black 配置 ===
[tool.black]
line-length = 88
target-version = ["py312"]
include = '\.pyi?$'
extend-exclude = '''
/(
    \.eggs
  | \.git
  | \.mypy_cache
  | \.tox
  | \.venv
  | _build
  | buck-out
  | build
  | dist
)/
'''

# === isort 配置 ===
[tool.isort]
profile = "black"
line_length = 88
multi_line_output = 3
include_trailing_comma = true
force_grid_wrap = 0
use_parentheses = true
ensure_newline_before_comments = true

# === flake8 配置 ===
[tool.flake8]
max-line-length = 88
extend-ignore = ["E203", "W503"]
max-complexity = 10
docstring-convention = "google"

# === mypy 配置 ===
[tool.mypy]
python_version = "3.12"
strict = true
ignore_missing_imports = true
disallow_untyped_defs = true
disallow_any_unimported = false
warn_return_any = true
warn_unused_configs = true

# === pytest 配置 ===
[tool.pytest.ini_options]
minversion = "8.0"
testpaths = ["tests"]
python_files = ["test_*.py"]
addopts = "-v --tb=short --strict-markers"
markers = [
    "slow: 慢速测试(默认跳过)",
    "integration: 集成测试(需要外部服务)",
]

# === coverage 配置 ===
[tool.coverage.run]
source = ["src"]
omit = ["tests/*", "**/__init__.py"]

[tool.coverage.report]
exclude_lines = [
    "pragma: no cover",
    "def __repr__",
    "if __name__ == .__main__.",
    "raise AssertionError",
    "raise NotImplementedError",
]

九、🚀 快速初始化项目脚本

结合本文所有内容,这里提供一个一键初始化脚本:

python 复制代码
#!/usr/bin/env python3
"""快速初始化 Python 项目的标准化工程结构"""

import subprocess
import sys
from pathlib import Path

PROJECT_NAME = sys.argv[1] if len(sys.argv) > 1 else "my-python-project"

# 目录结构
DIRS = [
    "src",
    "src/your_package",
    "tests",
    "docs",
    "scripts",
]

# 核心文件
FILES = {
    "README.md": f"# {PROJECT_NAME}\n\n项目描述\n",
    ".gitignore": (
        ".env\n.venv/\n__pycache__/\n*.pyc\ndist/\nbuild/\n"
        "*.egg-info/\n.idea/\n.vscode/\n*.log\n"
    ),
    "pyproject.toml": (
        "[build-system]\n"
        'requires = ["setuptools>=68.0"]\n'
        'build-backend = "setuptools.build_meta"\n\n'
        "[project]\n"
        f'name = "{PROJECT_NAME}"\n'
        'version = "0.1.0"\n'
        'requires-python = ">=3.11"\n'
        'dependencies = []\n\n'
        "[project.optional-dependencies]\n"
        'dev = [\n'
        '    "pytest>=8.0",\n'
        '    "pytest-cov>=5.0",\n'
        '    "black>=24.0",\n'
        '    "isort>=5.13",\n'
        '    "flake8>=7.0",\n'
        '    "mypy>=1.10",\n'
        '    "pre-commit>=3.7",\n'
        '    "commitizen>=3.27",\n'
        "]\n"
    ),
}

# 创建目录
for d in DIRS:
    Path(d).mkdir(parents=True, exist_ok=True)

# 创建文件
for path, content in FILES.items():
    Path(path).write_text(content)

# 初始化 git
subprocess.run(["git", "init"])
subprocess.run(["git", "add", "."])

# 安装 pre-commit
subprocess.run(["pip", "install", "pre-commit"])

# 生成 .pre-commit-config.yaml(可在此基础上增加其他钩子)
pre_commit_config = """
repos:
  - repo: https://github.com/pre-commit/pre-commit-hooks
    rev: v4.6.0
    hooks:
      - id: trailing-whitespace
      - id: end-of-file-fixer
      - id: check-yaml
      - id: check-added-large-files
      - id: detect-private-key
  - repo: https://github.com/psf/black
    rev: 24.4.2
    hooks:
      - id: black
  - repo: https://github.com/PyCQA/isort
    rev: 5.13.2
    hooks:
      - id: isort
  - repo: https://github.com/PyCQA/flake8
    rev: 7.1.0
    hooks:
      - id: flake8
  - repo: https://github.com/commitizen-tools/commitizen
    rev: v3.27.0
    hooks:
      - id: commitizen
        stages: [commit-msg]
"""

Path(".pre-commit-config.yaml").write_text(pre_commit_config.strip())
subprocess.run(["pre-commit", "install"])

print(f"✅ 项目 {PROJECT_NAME} 初始化完成!")
print("包含:pyproject.toml / pre-commit / Git / 目录结构")

十、常见问题与最佳实践

10.1 pre-commit 运行太慢怎么办?

bash 复制代码
# 只检查暂存的文件(默认行为)
git commit

# 更新钩子版本可以跳过缓存
pre-commit autoupdate

# 将耗时的检查(如 mypy)设为手动
# 在配置中添加 stages: [manual]
# 仅某些提交时运行:
pre-commit run --hook-stage manual mypy

10.2 如何回退被 pre-commit 修改的代码?

bash 复制代码
# pre-commit 自动修改后,如果不想提交:
git checkout -- <file>

# 或者查看具体修改:
git diff

10.3 CI 和本地环境不一致怎么办?

yaml 复制代码
# 方案一:在 CI 中也运行 pre-commit
# 方案二:使用相同的 Python 版本和依赖版本
# 方案三:锁定依赖版本(推荐)

# requirements.txt 或
# pyproject.toml 中锁定版本
dependencies = [
    "requests==2.31.0",
    "pydantic==2.5.0",
]

10.4 多项目如何管理 pre-commit 配置?

pre-commit 支持从远端仓库引用配置:

yaml 复制代码
# 中央仓库维护一套通用配置
repos:
  - repo: https://github.com/your-org/pre-commit-configs
    rev: v1.0.0
    hooks:
      - id: org-python-hooks

十一、总结

完整工作流全景图

复制代码
                        ┌──────────────────────┐
                        │   代码编写阶段         │
                        │  - 遵循项目结构         │
                        │  - 编写测试             │
                        └────────┬─────────────┘
                                 ↓
                        ┌──────────────────────┐
                        │   git add             │
                        └────────┬─────────────┘
                                 ↓
                    ┌──────────────────────────┐
                    │   pre-commit 自动运行      │
                    │   ├─ black 格式化          │
                    │   ├─ isort 排序           │
                    │   ├─ flake8 检查          │
                    │   ├─ detect-secrets       │
                    │   └─ 其他钩子             │
                    └────────┬─────────────────┘
                             ↓ (通过/失败)
                    ┌──────────────────────────┐
                    │   git commit              │
                    │   └─ commitizen 规范信息   │
                    └────────┬─────────────────┘
                             ↓
                    ┌──────────────────────────┐
                    │   git push                │
                    └────────┬─────────────────┘
                             ↓
                    ┌──────────────────────────┐
                    │   CI 自动运行              │
                    │   ├─ lint (pre-commit)     │
                    │   ├─ test (pytest)         │
                    │   ├─ security 扫描         │
                    │   └─ coverage 报告         │
                    └────────┬─────────────────┘
                             ↓ (通过)
                    ┌──────────────────────────┐
                    │   PR Review / Merge        │
                    └────────┬─────────────────┘
                             ↓
                    ┌──────────────────────────┐
                    │   版本发布                 │
                    │   ├─ git tag              │
                    │   ├─ git-cliff CHANGELOG   │
                    │   └─ 部署                  │
                    └──────────────────────────┘

核心收益

环节 自动化前 自动化后
代码格式 Code Review 时被人指出来 commit 时自动修复
import 顺序 手动整理,每次提交很乱 isort 自动排序
安全凭证 不小心提交到仓库才被发现 pre-commit 直接拦住
提交信息 随心所欲,CHANGELOG 难产 commitizen 规范化
CHANGELOG 发版前手动翻 commit log git-cliff 自动生成
CI 流程 手动触发,配置分散 GitHub Actions 一键完成

📌 如果本文对你有帮助,欢迎点赞收藏,关注博主获取更多 Python + AI 实战教程!
📕个人领域 :Linux/C++/java/AI

🚀 个人主页有点流鼻涕 · CSDN

💬 座右铭 : "向光而行,沐光而生。"

相关推荐
_Jimmy_1 小时前
AI应用开发工程师面试题库
人工智能·python·深度学习·机器学习·知识图谱
用户8356290780511 小时前
Python 实现 Excel 命名范围(Named Range)的创建与管理
后端·python
dreamer_83992 小时前
AI智能合同比对系统:从零搭建实战教程
人工智能·python
AOwhisky3 小时前
Python 学习笔记(第十五期)——运维自动化(下·后篇):堡垒机实战——paramiko高阶篇
运维·python·学习·云原生·自动化·运维开发
观察员3 小时前
带你用一个 Python 图书系统,通关封装继承多态
python
林泽毅4 小时前
PyTRIO快速入门(一):概念、推理与训练
人工智能·python·深度学习·机器学习·语言模型
admin and root4 小时前
「移动安全」安卓APP 反编译&frida脱壳技巧分享
android·开发语言·python·web安全·微信小程序·移动安全·攻防演练
午安~婉5 小时前
Git中SSH连接
前端·git·gitee
雪的季节5 小时前
Python基础5-18
开发语言·python
学术小李5 小时前
基于Pytorch,如何用CUDA自己写算子?(一)
人工智能·pytorch·python