# leaf-publisher
https://img.shields.io/badge/python-3.7+-blue.svg
https://img.shields.io/badge/License-MIT-yellow.svg
https://img.shields.io/pypi/v/leaf-publisher

leaf-publisher 是一个自动化 PyPI 发布工具，旨在让 Python 包的发布流程变得简单、智能且可重复。它能够自动扫描依赖、推断版本、生成变更日志、运行测试、构建分发包并上传，整个过程只需一个命令。

Leaf publisher is an automated PyPI publishing tool designed to make the publishing process of Python packages simple, intelligent, and repeatable. It can automatically scan dependencies, infer versions, generate change logs, run tests, build distribution packages, and upload them with just one command.

## ✨ 特性 feature
### 🔍 自动依赖扫描：基于 AST 分析项目中的 import 语句，自动识别第三方依赖，并获取当前环境的安装版本。 

### 📦 多来源依赖：支持从 poetry.lock、Pipfile、requirements.txt 导入依赖，或回退到 AST 扫描。

### 🚀 智能版本推断：根据 Git 提交历史（遵循约定式提交）自动建议下一个语义化版本号。

### 📝 自动生成 CHANGELOG：从 Git 提交记录提取信息，生成符合 Keep a Changelog 格式的 CHANGELOG.md。

### 🧪 测试集成：发布前可选运行测试（默认 pytest），失败则中止发布。

### ⚡ 缓存机制：缓存依赖扫描结果，大幅提升重复执行速度（可通过 --no-cache 禁用）。

### 🔐 私有仓库支持：可指定私有 PyPI 仓库 URL，并支持 token 或用户名+密码认证。

### 📄 许可证生成：内置多种常见许可证模板（MIT、Apache-2.0、GPL-3.0、LGPL-3.0、BSD、MPL-2.0、CC0、Unlicense），一键生成 LICENSE 文件。

### 🤝 智能合并：当已有 pyproject.toml 时，会询问如何合并新依赖（保留、合并、替换），绝不覆盖用户配置。

### ✅ 交互式或全自动：既适合手动发布时的交互确认，也支持 --yes 全自动模式，便于 CI/CD 集成。

## 🛠 安装
### 从源码安装（推荐体验最新功能）
从 PyPI 安装

bash
````
pip install leaf-publisher
````
## 🚀 快速开始
在您的 Python 项目根目录执行：

bash
````
lpub .
````
### 工具将：

1.检查 README.md 是否存在。

2.扫描依赖并与现有 pyproject.toml 合并（交互式）。

3.构建分发包（生成 dist/）。

4.询问是否上传，输入 PyPI token 后完成发布。

### 如果您希望全自动（例如在 CI 中）：

bash
````
lpub . --yes --test --infer-version --changelog
````

### 📖 命令行选项
text
````
usage: lpub [-h] [--no-build] [--skip-publish] [--yes] [--repository REPOSITORY]
            [--no-deps] [--no-scan-deps] [--dep-version-policy {minimum,exact,compatible}]
            [--use-git-version] [--install-local] [--editable] [--infer-version]
            [--changelog] [--test] [--test-command TEST_COMMAND] [--no-cache]
            [--deps-from {auto,scan,pipfile,poetry}] [--repository-url REPOSITORY_URL]
            [--username USERNAME] [--license LICENSE_ID] [--license-file LICENSE_FILE]
            [--version]
            [project_dir]
````
选项	说明

project_dir	项目根目录（默认当前目录）

--no-build	跳过构建步骤

--skip-publish	跳过上传步骤

--yes, -y	自动确认所有交互（包括发布）

--repository	PyPI 仓库名称（默认从配置或 pypi）

--no-deps	不处理依赖（禁用合并、扫描等）

--no-scan-deps	禁用依赖扫描（仅创建 pyproject.toml 时生效）

--dep-version-policy	依赖版本策略：minimum (>=), exact (==), compatible (~=)

--use-git-version	从 Git 标签获取版本（仅新建 pyproject.toml）

--install-local	发布前将包安装到本地 Python 环境

--editable	以可编辑模式安装（与 --install-local 配合）

--infer-version	根据 Git 提交记录推断并更新版本号

--changelog	自动生成/更新 CHANGELOG.md

--test	运行测试（默认 pytest）

--test-command	自定义测试命令（例如 "pytest tests/"）

--no-cache	禁用依赖扫描缓存

--deps-from	依赖来源：auto（默认）、scan、pipfile、poetry

--repository-url	私有仓库 URL（覆盖 --repository）

--username	PyPI 用户名（用于密码认证）

--license	使用内置许可证（如 MIT、GPL-3.0）

--license-file	使用自定义许可证文件路径

--version	显示版本信息

## ⚙️ 配置文件

您可以在项目的 pyproject.toml 中添加 [tool.leaf-publisher] 表来设置默认行为，例如：

toml
````
[tool.leaf-publisher]
auto_yes = false           # 是否默认自动确认
repository = "pypi"        # 默认仓库名称
skip_build = false         # 是否默认跳过构建
skip_publish = false       # 是否默认跳过发布
auto_merge_requirements = true  # 是否自动合并 requirements.txt
````
命令行参数优先级高于配置文件。

## 🔍 功能详解
### 依赖扫描与合并
扫描项目中所有 .py 文件（排除常见虚拟环境目录），提取 import 语句，排除 Python 标准库。

获取当前环境中已安装的版本，根据 --dep-version-policy 生成依赖字符串。

若项目存在 poetry.lock 或 Pipfile，可通过 --deps-from 优先使用它们。

当 pyproject.toml 已存在且包含 dependencies 时，会交互式询问如何合并（保留现有、合并去重、替换、取消）。

### 版本推断
基于 Git 提交历史，规则如下：

若提交信息包含 BREAKING CHANGE 或类型为 feat! → major 升级

否则若包含 feat 提交 → minor 升级

否则 → patch 升级

工具会显示当前版本和建议版本，您可选择接受、拒绝或手动输入。

### 生成 CHANGELOG.md
获取自上一个 Git 标签以来的所有提交。

按约定式提交分类（feat → Added, fix → Fixed, 其余 → Changed 等）。

将新内容追加到 CHANGELOG.md 头部，若文件不存在则创建。

### 缓存依赖扫描
在项目根目录创建 .leaf-publisher-cache/deps_cache.json，记录所有 .py 文件的修改时间哈希。

仅当文件发生变化时才重新扫描，大幅提升重复执行速度。可通过 --no-cache 禁用。

### 私有仓库支持
使用 --repository-url 指定完整仓库 URL（例如 https://nexus.example.com/repository/pypi/）。

#### 认证方式：

默认使用 API token（用户名固定为 __token__）。

通过 --username 切换为用户名+密码认证（密码从环境变量 PYPI_PASSWORD 或交互输入获取）。

### 许可证生成
内置多种许可证模板，位于 leaf_publisher/data/licenses/。使用 --license <ID> 生成对应文件，模板中的 [year] 和 [fullname] 会被替换为当前年份和 Git 用户名（或手动输入）。若文件已存在，会询问是否覆盖。

支持以下许可证 ID：

MIT

Apache-2.0

GPL-3.0

LGPL-3.0

BSD-3-Clause

BSD-2-Clause

MPL-2.0

CC0-1.0

Unlicense

也可通过 --license-file 直接复制自定义许可证文件。

## 🌐 环境变量
变量	用途

PYPI_TOKEN 或 TWINE_PASSWORD	PyPI API token 或密码（token 方式时密码即 token）

PYPI_USERNAME 或 TWINE_USERNAME	PyPI 用户名（与密码认证配合）

## 📝 示例
### 典型手动发布
bash
````
cd my-package
lpub . --infer-version --changelog --test
````
交互过程：确认依赖合并 → 接受建议版本 → 生成 changelog → 运行测试 → 构建 → 输入 token → 上传。

### 仅生成 LICENSE 和更新 pyproject.toml

bash
````
lpub . --license MIT --no-build --skip-publish
````

### 使用私有仓库
bash
````
lpub . --repository-url https://private.pypi.org/simple/ --username myuser
````
### CI/CD 全自动发布（需提前设置环境变量）
bash
````
lpub . --yes --infer-version --changelog --test
````

## 🤝 贡献
拙作在此，疏漏不少。欢迎指出漏洞，提交 Issue 和 Pull Request（你可以私信FourWaterLeaf@163.com)！请确保代码符合 PEP 8，并通过测试。

## 📄 许可证
MIT © 2025 FourWater