Metadata-Version: 2.4
Name: svnpypi
Version: 0.1.5
Summary: SVN-backed Python package repository and uploader
Project-URL: Homepage, https://github.com/narutozb/svnpypi
Project-URL: Documentation, https://github.com/narutozb/svnpypi/tree/main/docs
Project-URL: Repository, https://github.com/narutozb/svnpypi.git
Project-URL: Issues, https://github.com/narutozb/svnpypi/issues
Author: yuanzhen qiao
License: MIT
License-File: LICENSE
Requires-Python: >=3.9
Requires-Dist: packaging>=23.2
Requires-Dist: tomli>=2.0.1; python_version < '3.11'
Provides-Extra: dev
Requires-Dist: build>=1.2; extra == 'dev'
Requires-Dist: mypy<2.0,>=1.11; extra == 'dev'
Requires-Dist: pytest>=8.0; extra == 'dev'
Requires-Dist: ruff>=0.6; extra == 'dev'
Provides-Extra: release
Requires-Dist: build>=1.2; extra == 'release'
Requires-Dist: twine>=5.1; extra == 'release'
Description-Content-Type: text/markdown

# svnpypi - Serverless SVN Python Package Manager

**svnpypi** 是一个面向内部局域网、无法或不想部署通用 HTTP 包服务器场景的小型 CLI 适配工具。它复用现有 SVN 权限体系，通过 `svn`、`svn+ssh` 或 `https` 完成 Python 内部包的上传、列出、下载和安装。

> **核心理念**: 不新增服务端，不接管认证，不重新实现依赖解析；SVN 负责权限和审计，`svnpypi` 只做最小必要的包分发适配。

---

## 目录
- [背景与限制](#背景与限制)
- [核心机制](#核心机制)
- [安装](#安装)
- [开发验证](#开发验证)
- [发布到 PyPI](#发布到-pypi)
- [进阶文档](#进阶文档)
- [认证前置与跨环境登录](#认证前置与跨环境登录)
- [推荐的工作目录结构](#推荐的工作目录结构)
- [命令指南](#命令指南)
  - [上传包 (upload)](#1-上传包-upload)
  - [列出包 (list)](#2-列出所有的包与版本-list)
  - [安装包 (install)](#3-安装包到本地-install)
  - [诊断环境 (doctor)](#4-诊断环境-doctor)
  - [同步项目内部依赖 (sync)](#5-同步项目内部依赖-sync)
- [兼容性说明（语言与操作系统）](#兼容性说明语言与操作系统)
- [原理与安全](#原理与安全)

---

## 背景与限制

在很多内网或对安全有高要求的环境中，我们通常:
1. 不允许在公网部署/上传代码；
2. 不愿意为了几十个内部的 `.whl` 去费时费力维护一个包含鉴权（Auth）、SSL、反向代理的独立中心化 PyPI/Devpi 服务器。
3. 但公司通常已经有了成熟的组织架构权限体系映射在现有的 SVN 仓库上。

所以我们需要一种 **“依靠 SVN 命令行客户端做认证代理，下载包后再喂给本地 `pip`”** 的方案，`svnpypi` 应运而生。

---

## 核心机制

`svnpypi` 的工作流程分为两个阶段：
1. **发布阶段 (Upload)**：在开发者机器上读取构建好的 `dist/*` 文件，识别包名并作为普通 SVN Asset 使用 sparse checkout 查验并建立 `svn commit` 提交到预先指定的 SVN 路径下供他人使用。
   - 上传新制品时会维护包目录下的 `SHA256SUMS` 清单。
2. **下载阶段 (Install)**：在其他开发者或部署机器上，使用同一套 CLI 读取该 SVN 目录中的文件，它将：
   - 使用终端原生的 `svn list` 列出所有版本供决策。
   - 在同一版本存在多个制品时，使用 `packaging.tags.sys_tags()` 按当前解释器的完整兼容标签选择 wheel；无兼容 wheel 时回落到 sdist。
   - 使用终端原生的 `svn cat` 将选定发行版流式写入 `/tmp/` 系统缓冲，避免大文件一次性进入内存。
   - 若包目录存在 `SHA256SUMS` 且包含目标文件条目，会在安装前校验 SHA256。
   - 脱离 SVN 后自动代理唤起原生的 `pip install /tmp/...whl`。
   - 清理所有缓冲。

---

## 安装

依赖于您的内网环境中同样获取了代码库，使用：

```bash
git clone <your-repo>/svnpypi
cd svnpypi

# 安装到您的 Python 环境中
pip install .
```

## 开发验证

仓库提供 GitHub Actions CI，覆盖 Linux 的 Python 3.9/3.12/3.14、Windows 的 Python 3.12，并运行真实本地 SVN 集成测试（工具可用时）。本地可执行：

```bash
python -m ruff check src tests scripts
python -m mypy src scripts
python -m pytest -q
python -m build
```

---

## 发布到 PyPI

仓库提供跨平台发布脚本。它默认只运行质量检查、构建 wheel/sdist 并执行
`twine check`，不会自动上传：

```bash
python -m pip install -e ".[dev,release]"
python scripts/release_pypi.py
```

先向 TestPyPI 试传：

```bash
python scripts/release_pypi.py --upload --repository testpypi
```

正式发布要求工作区干净、完成全部检查、当前提交带有已推送到 `origin` 的
annotated `v0.1.5`（或 `0.1.5`）标签，并显式确认版本：

```bash
python scripts/release_pypi.py --upload --repository pypi --confirm-version 0.1.5
```

令牌配置、TestPyPI 安装验证、正式发布清单和 CI Trusted Publishing 建议见
`docs/svnpypi/pypi-release.md`。

---

## 进阶文档

如果你需要面向团队/公司落地的长期治理方案（命名规范、环境分层、发布策略、CI、跨仓库部署等），请直接阅读按包拆分后的文档：

- `docs/README.md`
- `docs/svnpypi/README.md`
- `docs/svnpypi/command-reference.md`
- `docs/svnpypi/stable-usage-guide.md`
- `docs/svnpypi/deployment-readiness.md`
- `docs/svnpypi/pypi-release.md`
- `docs/svnpypi/platform-verification-matrix.md`
- `docs/svnpypi/migration-guide.md`
- `docs/pysvnlite/README.md`

---

## 认证前置与跨环境登录

`svnpypi` 本身不处理认证参数（例如 `--username/--password`），它只复用当前系统上的 `svn` 客户端认证状态。

**建议在每台机器首次使用前先做一次认证预热（必须非交互成功）：**

```bash
svn --non-interactive list <repo-url>
```

如果你是账号密码模式，先在受信终端执行一次交互式命令，让 SVN 按本机策略缓存凭据；随后再验证非交互访问：

```bash
svn list <repo-url>
svn --non-interactive list <repo-url>
```

不要把密码放在命令行参数中，避免泄漏到 shell 历史、进程列表或 CI 日志。

### 常见环境建议

1. **Linux/macOS + ssh key**
   - 先确保 `ssh-agent` 中已经加载密钥（`ssh-add -l` 可检查）。
   - 再执行一次 `svn --non-interactive list svn+ssh://...` 做预热。

2. **Windows + PPK (PuTTY/Pageant/plink)**
   - 推荐先在 Pageant 中加载 `.ppk`。
   - 建议通过 `SVN_SSH` 指定 plink（或在 Subversion `tunnels` 里配置）。
   - 示例（PowerShell）：

```powershell
$env:SVN_SSH = '"C:\Program Files\PuTTY\plink.exe" -batch -load your_saved_session'
svn --non-interactive list svn+ssh://svn.company.com/python-packages/release
```

3. **CI 环境**
   - 在流水线里先做一条 `svn --non-interactive list/info` 预检，再执行 `svnpypi` 命令。

---

## 推荐的工作目录结构

为了方便管理并利用 SVN 本身的特性处理“版本环境”，我们建议您将 SVN 仓库开辟特定子目录承接 Python 包：

```text
svn+ssh://svn.yourcompany.com/python-packages/
├── release/                     <-- 稳定版本仓库（严格权限：运维小组长才能合并）
│   ├── my-internal-tool/        <-- svnpypi 自动生成
│   │   ├── my_internal_tool-1.0.0-py3-none-any.whl
│   │   └── my_internal_tool-1.1.0-py3-none-any.whl
│   └── another-library/         <-- svnpypi 自动生成
│       └── ...
└── test/                        <-- 测试/研发版本仓库（较松权限：允许所有后端开发者无损上传测试迭代）
    ├── my-internal-tool/
    │   └── my_internal_tool-1.2.0.dev1-py3-none-any.whl
    └── ...
```

---

## 命令指南

### 1. 上传包 (upload)

当您在本地开发完毕并执行了 `python -m build` 生成了构建物（`.whl`、`.tar.gz`），您可以将其推送到远程：

```bash
# 先推送所有生成物到测试仓库频道
svnpypi upload dist/* --repo-url svn+ssh://svn.company.com/python-packages/test
```

上传会同步维护包目录下的 `SHA256SUMS` 文件，供安装前校验使用。
`dist/*` 等通配符由 `svnpypi` 自身展开，因此在 PowerShell 和 POSIX shell 中行为一致。只接受标准 wheel / sdist 文件，未匹配路径和无效制品会返回非零退出码。

如果多人同时上传同一包目录，`svnpypi` 会在常见 out-of-date / already exists 场景下重新查询远端并有限重试。同名远端制品只有在 SHA256 相同时才会跳过；内容不同会失败并要求提升版本。
上传准备和暂存期间会再次核对本地制品 SHA256；若构建产物正在被其他进程改写，上传会失败并要求使用稳定制品重试。

### 2. 列出所有的包与版本 (list)

想查看团队里都有哪些可共享组件可以安装在自己电脑上的？获取一次组件树！

```bash
svnpypi list --repo-url svn+ssh://svn.company.com/python-packages/release
```

**输出示例：**
```text
📦 SVN Package Registry
└── pysvnlite
    ├── 0.1.1 (pysvnlite-0.1.1-py3-none-any.whl)
    ├── 0.1.0 (pysvnlite-0.1.0-py3-none-any.whl)
    └── ... and 2 more
```

### 3. 安装包到本地 (install)

这是最重要的命令。它无缝替换您常用的 `pip install` 但支持 `svn+ssh://` 的文件读取：

```bash
# 临时探索时可安装 SVN 目录中的最高版本
svnpypi install pysvnlite --repo-url svn+ssh://svn.company.com/python-packages/release

# 稳定环境建议限定下载和安装特定版本
svnpypi install pysvnlite --version 0.1.0 --repo-url svn+ssh://svn.company.com/python-packages/release

# 限定版本范围，自动选择范围内最高可用版本
svnpypi install pysvnlite --specifier ">=0.1,<1.0" --repo-url svn+ssh://svn.company.com/python-packages/release

# 甚至向后传递任意的原生 pip 配置命令到最终触发时
svnpypi install pysvnlite --repo-url svn+ssh://svn.company.com/python-packages/release --pip-args --no-cache-dir --upgrade

# 安装成功后写出可复现记录
svnpypi install pysvnlite --version 0.1.0 --repo-url svn+ssh://svn.company.com/python-packages/release --record .svnpypi/install-record.json

# 部署环境要求制品必须存在 SHA256SUMS 条目
svnpypi install pysvnlite --version 0.1.0 --repo-url svn+ssh://svn.company.com/python-packages/release --require-hash
```

`--record` 会写出 JSON，包含包名、版本、文件名、仓库 URL、制品 URL、`SHA256SUMS` URL、实际 SHA256、期望 SHA256 以及是否完成 hash 校验。即使仓库里尚无对应 `SHA256SUMS` 条目，也会记录本次下载制品的实际 SHA256。
记录通过同目录临时文件和原子替换写入；写入失败不会截断已有记录。

### 4. 诊断环境 (doctor)

首次接入或 CI 排障时，可以先检查本机 `svn`、当前 Python 环境的 `pip`、以及仓库读取权限：

```bash
svnpypi doctor --repo-url svn+ssh://svn.company.com/python-packages/release
```

如需验证写权限，可显式启用临时目录创建/删除探针：

```bash
svnpypi doctor --repo-url svn+ssh://svn.company.com/python-packages/release --check-write
```

### JSON 输出

`list`、`upload`、`install`、`doctor`、`sync` 均支持 `--json`，便于 CI 或脚本解析：

```bash
svnpypi list --repo-url svn+ssh://svn.company.com/python-packages/release --json
svnpypi doctor --repo-url svn+ssh://svn.company.com/python-packages/release --json
```

### 5. 同步项目内部依赖 (sync)

如果项目的 `pyproject.toml` 配置了 `[tool.svnpypi]`，可以直接安装其中的内部依赖：

```toml
[tool.svnpypi]
repo_release = "svn+ssh://svn.company.com/python-packages/release"
internal_dependencies = [
    "company-core-lib==1.8.2",
    "company-auth-sdk>=3.4,<4.0",
]
```

执行同步：

```bash
svnpypi sync --channel release
```

预览将执行的安装命令：

```bash
svnpypi sync --channel release --dry-run
```

### 默认仓库地址

所有需要仓库地址的命令都可以省略 `--repo-url`，改用环境变量 `SVNPYPI_REPO_URL`：

```bash
export SVNPYPI_REPO_URL=svn+ssh://svn.company.com/python-packages/release
svnpypi list
svnpypi install pysvnlite --specifier ">=0.1,<1.0"
```

PowerShell 示例：

```powershell
$env:SVNPYPI_REPO_URL = "svn+ssh://svn.company.com/python-packages/release"
svnpypi doctor
```

---

## 兼容性说明（语言与操作系统）

- **Python**：包元数据保持 `>=3.9` 兼容；Python 3.9/3.10 会安装 `tomli`，3.11+ 使用标准库 `tomllib`。Python 3.9 已结束官方维护，生产部署建议使用仍受支持的 Python 3.12 至 3.14。
- **语言环境**：`list/install` 主要依赖 `svn --xml` 输出解析，基本不受终端语言（中文/日文/英文）影响。
- **提交回显**：`upload` 会解析 `svn commit` 的文本输出以显示 revision；当前已兼容多语言关键字并做了回退逻辑。
- **可预期边界**：若遇到极少见本地化文案导致 revision 无法提取，提交本身仍然成功，只是无法显示 revision 数字。
- **操作系统**：核心行为依赖系统 `svn` 与 `pip`；本地 `file://` SVN 已在 Windows 验证，CI 配置覆盖 Linux/Windows。`svn+ssh`、HTTPS 证书和 key agent 仍需在实际部署网络中验收。

---

## 原理与安全

`svnpypi` 不接收或保存密码、公钥和证书配置，所有认证由系统 `svn` 客户端完成。不要把密码嵌入仓库 URL；应使用 SVN 凭据缓存、SSH agent 或部署机凭据。CLI 输出和安装记录会对 URL userinfo 做脱敏，但这只是误配置防护，不是推荐的认证方式。

`SHA256SUMS` 用于发现传输损坏和意外内容变化，不等同于数字签名。拥有制品与 manifest 写权限的账号可以同时修改两者，因此发布可信度最终依赖 SVN ACL、提交审计、传输安全和发布账号保护。
