DBordeleau/hf-freeze
GitHub: DBordeleau/hf-freeze
一个为 Hugging Face Hub 模型和数据集生成确定性 lockfile 的 Python CLI 工具,将动态 revision 锁定为不可变 commit,填补传统包管理器无法覆盖 Hub 依赖的空白。
Stars: 0 | Forks: 0
# hf-freeze
`hf-freeze` 是一个本地 Python CLI,旨在填补包 lockfile 与 Hugging Face Hub 调用(例如 `AutoModel.from_pretrained("org/model")`)之间的空白。它会查找支持的字面量引用,将动态 revision 解析为不可变 commit,生成确定性的 `hf.lock`,并帮助你审查和应用精确的源码锁定(pin)。这使得你的项目所期望的 Hub snapshot 在 Git 中变得可见且可审查——而无需运行你的项目或下载模型权重来生成 lockfile。
## 60 秒快速入门
在一个包含受支持的 Hugging Face Hub 调用的 Python 项目根目录下,从以下三个命令开始:
```
hf-freeze scan
hf-freeze lock
hf-freeze pin
```
审查 pin 的 diff,然后在没有网络连接的情况下显式地写入并验证它:
```
hf-freeze pin --write
hf-freeze check --frozen
```
`scan` 会发现支持的字面量调用;`lock` 会解析它们的追踪 revision 并写入 `hf.lock`;`pin` 的试运行(dry-run)会展示源码 diff。只有在审查过该 diff 之后,才应使用 `pin --write` 将接受的不可变 SHA 显式地写入受支持的源码调用中。
## 安装
要求 Python 3.10+。
使用 [uv](https://docs.astral.sh/uv/) 直接从 GitHub 安装:
```
uv tool install git+https://github.com/DBordeleau/hf-freeze.git
```
或者从源码检出版本安装:
```
git clone https://github.com/DBordeleau/hf-freeze.git
cd hf-freeze
uv tool install .
```
## 从源码到 `hf.lock`,再到经过审查的更新
在你想追踪其 Hub 调用的 Python 项目根目录下运行以下命令:
```
hf-freeze scan
hf-freeze lock
hf-freeze pin
hf-freeze pin --write
hf-freeze check --frozen
hf-freeze diff org/model
hf-freeze update org/model
hf-freeze update org/model --write
hf-freeze pin
hf-freeze pin --write
hf-freeze check --frozen
```
`scan` 会显示受支持的发现结果以及未解析的动态值。`lock` 会解析当前请求的 revision,并将确定性的 JSON 写入 `hf.lock`。
默认情况下,`pin` 会预览最小的源码更改;只有在审查过该 diff 后才应使用 `pin --write`。随后,`diff` 是仅用于审查的:它会将锁定的 snapshot 与其存储的追踪 revision 的当前候选对象进行比较。`update` 会显示相同的审查内容,且默认为试运行;只有 `update --write` 才会将审查过的 snapshot 接收到 `hf.lock` 中。它特意不编辑源码,因此需运行单独的 `pin --write` 步骤,然后再运行离线的 `check --frozen` 守卫。
`hf.lock` 是对其他工具的补充,而不是替代。Python lockfile(例如 `uv.lock`、Poetry lockfile 和 pip 需求锁)锁定的是 Python 包,而不是 Hugging Face Hub 仓库。Hugging Face cache 存储的是本地 artifacts,但它既不是项目级别的依赖声明,也不是经过审查的验收记录。`hf.lock` 为该项目记录了已接受的 Hub 仓库 snapshot 及其追踪 revision。
## 命令与安全性
| 命令 | 用途 | 网络 |
| --- | --- | --- |
| `hf-freeze scan [PATH]` | 静态发现受支持的 Python 调用。 | 否 |
| `hf-freeze lock [PATH]` | 解析已知 revision 并以原子方式写入 `hf.lock`。 | 仅限 Hub metadata;不涉及权重 |
| `hf-freeze check [PATH] --frozen` | 检查 CI 的源码/lockfile 覆盖率。 | 否 |
| `hf-freeze diff REPO_ID [--revision REV]` | 将锁定的 SHA 与候选 revision 进行比较。 | 仅限 Hub metadata;为了进行语义比较,仅允许读取白名单内的小型 JSON 文件 |
| `hf-freeze update REPO_ID [--revision REV] [--write]` | 预览仓库更新;`--write` 仅将其以原子方式接收至 `hf.lock` 中。 | 仅限 Hub metadata;为了进行语义比较,仅允许读取白名单内的有限小型 JSON 文件;不涉及权重 |
| `hf-freeze pin [PATH] [--write]` | 预览,然后可选地以原子方式应用确切的源码 pin。 | 否 |
只要能够报告发现结果(包括未解析的),`scan` 就会执行成功。如果受支持的发现未解析或存在冲突,`lock` 将拒绝写入。`check --frozen` 以及跳过了不安全目标的 `pin` 运行会以非零状态退出,从而确保 CI 不会将不完整的覆盖率误判为成功。`diff` 报告更改仅作信息参考;repository、lockfile 或 Hub 错误才会被视为失败。`pin` 默认为试运行,除非显式使用 `--write`,否则不会编辑文件。`update` 默认也为试运行;当显式使用 `--write` 时,它仅会更改 `hf.lock`,而绝不更改源码。
### 支持的调用形式
原型能够识别以下形式中的字面量字符串和同作用域内的简单字符串常量:
- `*.from_pretrained("repo/id", ...)`(包括常见的 Diffusers 形式)
- `load_dataset("repo/id", ...)`
- `hf_hub_download(repo_id="repo/id", ...)`
- `snapshot_download(repo_id="repo/id", ...)`
- `SentenceTransformer("repo/id", ...)`
- `PeftModel.from_pretrained(base_model, "repo/id", ...)` 或 `model_id=`
- `pipeline(..., model="repo/id", ...)`
它扫描的是源码;它不会导入或执行你的项目。动态 ID、插值字符串、导入的配置以及不支持的 pipeline 形式会被报告出来,而不是被静默解析。
关于方法论以及来自五个公开仓库的精确 commit 结果,请参阅[代表性项目兼容性](docs/compatibility.md)。该表格记录了观察到的行为,并不暗示对框架的广泛支持。
## 完整生命周期演示
这个紧凑的终端演练使用了来自离线 fake-backed 生命周期测试的合成 repository metadata。它演示了受支持的“审查优先”流程;它不是下面真实示例的输出,并且它不会执行模型或下载权重。

## 不可变演示:tiny-random-bert
[`examples/tiny-random-bert`](examples/tiny-random-bert) 是一个真实的、有意设为 floating 的调用点。它所提交的 `hf.lock` 记录了历史 Hub commit `9b8c223d42b2188cb49d29af482996f9d0f3e5a6`;在应用 `pin` 的预览之前,这种不匹配是故意的。
```
cd examples/tiny-random-bert
hf-freeze scan .
hf-freeze diff hf-internal-testing/tiny-random-bert --revision 8fc97e155588266e09c9f37d4a9608e1a65a279e
hf-freeze pin .
```
确切的 revision 比较是可复现的:metadata 报告了候选对象新增的 `model.safetensors` 权重 artifact(520,212 字节)。它不会执行模型或下载该权重/artifact。因为源码是有意 floating 的,所以在应用审查过的 pin 之前,预期 `hf-freeze check . --frozen` 会失败;在应用该预览之后,请勿提交此示例。
## 局限性
- 发现机制是静态的、仅限 Python,且有意设计为不完全的;它不是一种行为或安全分析。
- 它不提供完整的传递依赖发现、notebook 支持、严格的文件清单、artifact 镜像或运行时强制执行。
- Metadata 操作避免了权重下载。当需要进行有限的语义比较时,`diff` 可能仅会读取白名单中的小型配置 JSON 文件。
- 锁定的 Hub commit 标识的是一个 snapshot,但本原型并不声称具有广泛的库兼容性、模型质量、安全性或未来的 Hub 保留度。
## 贡献与许可
请参阅 [CONTRIBUTING.md](CONTRIBUTING.md)。采用 [Apache-2.0](LICENSE) 许可。
标签:AI工程化, Hugging Face, Python, 依赖管理, 安全可观测性, 文档结构分析, 无后门, 版本控制, 逆向工具