On this page
供应链管理
现代 JavaScript 项目会从许多来源拉取代码(JSR、npm、HTTPS URL、 本地工作区)。良好的供应链管理可以帮助你实现四个目标:
- 确定性:每个人(以及你的 CI)运行完全相同的代码。
- 安全性:及早发现意外的上游变更或被破坏的依赖。
- 速度:你可以在选择的时机有意地更新依赖。
- 韧性:即使离线或注册表出现故障,构建也能继续工作。
本页基于 锁文件 和 vendoring,它们来自 依赖管理指南。
核心实践 Jump to heading
- 有意地固定版本
- 对于应用程序,优先使用精确版本(例如
jsr:@luca/cases@1.2.3)。 - 对于库,使用 caret 范围(
^1.2.3)可让使用者获得 向后兼容的修复。 - 在生产应用中避免使用无限制(
*)或过于宽泛的范围。
- 对于应用程序,优先使用精确版本(例如
- 提交你的
deno.lock文件。 - 在 CI / 生产环境中启用冻结锁文件(
--frozen或"lock": { "frozen": true }),这样新的、未见过的依赖会导致构建失败, 而不是悄无声息地出现。 - 当你需要密封/离线构建时(
"vendor": true),或者当你 必须在本地修补第三方代码时,使用 vendoring。vendoring 并不会取消 锁文件的必要性——它是对锁文件的补充。 - 在较大的代码库中,优先使用 import map(
imports)条目,而不是原始 HTTPS 导入, 以集中管理版本变更。 - 定期解除冻结并有意识地更新(例如按周或按冲刺节奏),而不是在功能开发期间临时更新。
- 设置一个最低依赖年龄,以便新发布的 版本不会在生态系统有时间发现被破坏的发布之前就进入安装流程。
最低依赖年龄 Jump to heading
Deno 可以拒绝安装任何年龄低于配置值的包版本。 这是一种低成本、广覆盖的防御 npm 供应链攻击的方法:恶意版本通常会在几天内被发现并撤回,因此把安装延迟到类似的时间窗口可以捕获其中大部分。
你可以在三个地方配置相同的控制;选择最适合项目的方式:
-
deno.json,应用于整个项目:deno.json{ "minimumDependencyAge": "P3D" } -
CLI 标志,按需应用,例如用于一次性安装或 CI 步骤:
>_deno install --minimum-dependency-age=P3D -
.npmrc(Deno 2.8+),符合 npm 约定,在需要在 npm 和 Deno 工具之间共享同一个.npmrc时很有用。npm 设置只接受整数天数:.npmrcmin-release-age=3
deno.json 和 --minimum-dependency-age 接受
ISO-8601 持续时间,例如
P3D(3 天)或 PT72H(72 小时),也接受整数(按分钟解释)、
绝对截止日期(2025-09-16)或 RFC3339 时间戳,或者 0 以禁用。该字段还支持一种对象形式,用于豁免特定包;完整结构请参见
minimumDependencyAge 参考,
而有关 Deno 读取的其他 npm 注册表选项,请参见
.npmrc 配置。
典型 CI 模式 Jump to heading
在 Deno 2.8+ 中,单个命令 deno ci
封装了推荐的 CI 安装流程(冻结锁文件 + 生命周期脚本):
deno ci
对于较旧的 Deno 版本,或者需要手动组合这些步骤时:
# 按锁定状态精确安装(解析)依赖;如果有漂移或新依赖则失败
deno install --frozen --entrypoint main.ts
# (可选)仅使用缓存模块运行,以保证没有网络访问
deno run --cached-only main.ts
如果你依赖 npm 包(存在 package.json),请在 CI 中于运行测试之前包含 deno install
(或 deno ci),以便 node_modules 目录以确定性的方式被生成。
有意更新依赖 Jump to heading
当你决定更新时:
- 临时允许写入锁文件:添加
--frozen=false或设置"lock": { "frozen": false }。 - 修改版本(编辑
deno.json,使用deno add <specifier>@<newVersion>, 或使用deno remove删除)。 - 重新运行
deno install --entrypoint main.ts(可选加上--reload)以更新 解析结果和完整性哈希。 - 在你的拉取请求中审查
deno.lock(以及如果使用了vendor/,也要审查它)的差异。 - 重新启用冻结锁文件。
冻结锁文件疑难排查 Jump to heading
你可能会遇到如下错误:
error: The lockfile is frozen. Cannot add new entry for "jsr:@scope/pkg@1.3.0".
或者:
error: Module not found in frozen lockfile: https://example.com/dependency/mod.ts
常见原因和修复方法:
| 症状 | 原因 | 修复 |
|---|---|---|
| 需要提升版本但命令因冻结错误失败 | 锁文件处于冻结模式 | 重新运行时使用 --frozen=false(一次性)或临时设置 "lock": { "frozen": false },然后更新并重新冻结 |
| 编辑代码后出现新的传递依赖 | 代码现在导入了锁文件中没有的内容 | 解除冻结(--frozen=false),并运行 deno install --entrypoint <entry>.ts 记录它 |
| 删除了导入,但锁文件仍包含旧条目 | 锁文件是追加式的;条目会保留 | (可选)重新生成:先把 deno.lock 移到一边(mv deno.lock deno.lock.old),运行安装以重建、比较,然后提交 |
| 锁文件损坏 / 合并冲突 | 手动编辑或冲突导致 JSON 不一致 | 删除冲突部分并重新运行安装,或者完全重新生成 |
| 使用了 vendored 依赖但锁文件报错 | vendor 目录与锁文件不同步 | 重新运行 deno install --entrypoint <entry>(非冻结)以同步两者,然后提交 |
安全重建检查清单 Jump to heading
只有在必要时才完整重建整个 deno.lock(损坏、大规模清理)。当你需要这么做时:
- 备份它:
cp deno.lock deno.lock.bak。 - 删除它:
rm deno.lock。 - (如果使用 vendoring)删除或移动
vendor/目录。 - 运行
deno install --entrypoint main.ts以重新创建。 - 检查新旧之间的差异,以捕获意外新增内容。
Vendor 与锁文件 Jump to heading
它们是互补的:
- 锁文件:记录远程以及 npm/JSR 依赖的精确解析版本 + 完整性哈希。
- Vendor 目录:将实际源代码存储在本地,以实现密封、离线且可打补丁的构建。
为了获得最大的可复现性,请同时使用两者。即使锁文件被冻结,如果远程源消失,构建也不会完全密封;vendoring 填补了这一缺口。
快速决策指南 Jump to heading
| 需求 | 使用 |
|---|---|
| 检测上游篡改 | 锁文件(提交并冻结) |
| 离线 / 断网构建 | vendor: true + 锁文件 |
| 修补第三方代码 | Vendoring 或 scopes 覆盖(短期) |
| 具有完整性保证的快速 CI | deno install --frozen |
| 有意升级 | 临时解除冻结,运行安装,审查差异 |
最低供应链基线(推荐) Jump to heading
{
"imports": {/* 集中管理版本 */},
"vendor": true,
"lock": { "frozen": true }
}
提交 deno.json、deno.lock,以及(如果使用 vendor)整个 vendor/
目录。
一个计划执行的 CI 作业:解除冻结,运行 deno add --latest(或手动提升
关键包版本),执行测试,并打开一个包含更新后 deno.lock(以及 vendor/)
的拉取请求,可以在保持日常构建确定性的同时,让安全补丁持续流入。