- 数据可视化
- 数据库
- 前端
- CLI
【免费下载链接】liam
Automatically generates beautiful and easy-to-read ER diagrams from your database.
在依赖动辄上千个的 JavaScript Monorepo 中,如何系统性地核查每一个第三方包的开源许可证,并保证它们与项目自身的 Apache-2.0 许可证兼容,是每个开源团队都要面对的合规问题。Liam(一个“从数据库自动生成 ER 图”的开源项目)在仓库中维护了一份由 LicenseFinder 自动生成的依赖许可证审计报告 docs/packages-license.md,并配套了 config/license_finder.yml 与 config/dependency_decisions.yml 两份决策配置。读完本文,你将理解这份报告的结构与生成方式、permit/approve两类人工决策的差异、以及项目如何为误检许可证、Public Domain、GPL 家族依赖等边界情况建立可追溯的例外记录。
审计基线:Liam 自身是 Apache-2.0 项目
一切许可证兼容性判断的出发点,是项目自身的许可证。仓库根目录的 LICENSE 文件完整收录了 Apache License Version 2.0 的条款,版权方为 ROUTE06, Inc.(2024)。README.md 中的 License 章节也明确声明:
Liam ERD is licensed under the Apache License Version 2.0. Licenses for third-party packages can be found in docs/packages-license.md.
这意味着两件事:
- 兼容性基线是 Apache-2.0。所有第三方依赖的许可证必须能与 Apache-2.0 共存。宽松型许可证(MIT、ISC、BSD 系列、Unlicense、CC0 等)天然兼容,而强 Copyleft(如 GPL)则需要特别评估——这一点在下面的“边界情况”一节有实例。
- 依赖许可证清单是一份正式的仓库文档,直接放在
docs/目录下并由 README 链接引用,供使用者与下游项目在引入 Liam 时做合规核查。
审计报告的构成:Summary 与逐包 Items
docs/packages-license.md 是一份约 1.9 万行的机器生成报告,由两大块组成。
Summary:许可证分布总览
报告开头的 Summary 小节按许可证名称聚合了全部被审计依赖的数量,当前快照为:
| 许可证 | 依赖数量 |
|---|---|
| MIT | 1414 |
| Apache 2.0 | 131 |
| ISC | 85 |
| New BSD | 40 |
| Simplified BSD | 25 |
| Mozilla Public License 2.0 | 8 |
| BlueOak-1.0.0 | 5 |
| CC0 1.0 Universal | 4 |
| Unknown | 3 |
| MIT OR Apache-2.0 | 2 |
| The Unlicense | 2 |
| MIT-0 | 1 |
| LGPL-3.0-or-later | 1 |
| Apache-2.0 AND MIT | 1 |
| Python-2.0 | 1 |
| CC-BY-4.0 | 1 |
| Artistic-2.0 | 1 |
| Eclipse 2.0 | 1 |
| Public Domain | 1 |
| (MIT AND Zlib) | 1 |
| (BSD-2-Clause OR MIT OR Apache-2.0) | 1 |
| (MIT AND BSD-3-Clause) | 1 |
| CC-BY-3.0 | 1 |
| BSD Zero Clause License | 1 |
| (MIT OR CC0-1.0) | 1 |
从分布可以直观读出项目的依赖生态画像:MIT 占绝对多数(1414 个),其次是 Apache 2.0 与 ISC,其余为零星的双许可证(如MIT OR Apache-2.0)、公共领域类(CC0、Unlicense、Public Domain)以及少数需要人工介入的条目(Unknown、Python-2.0、LGPL-3.0-or-later)。表格中所有许可证均为宽松型(permissive)许可或与 Apache-2.0 共存的许可,Unknown仅 3 个且全部有对应的人工批准记录——这正体现了决策机制兜底的作用。
Items:逐包明细的固定格式
Summary 之后是 Items 部分,按包名排序逐个列出。每个条目格式固定,例如报告中zod的条目:
<a name="zod"></a> ### zod v3.25.76 #### ##### Paths * /home/runner/work/liam/liam MIT permitted各部分含义:
### <包名> v<版本>:被审计的依赖及其精确版本。同一包可能出现多个版本(如yallist v3.1.1与yallist v5.0.0并存),因为它们来自 workspace 中不同子包的依赖链。##### Paths:该依赖被引入的仓库路径。报告中统一为/home/runner/work/liam/liam,这是 CI 环境(GitHub Actions runner)中的工作目录,即本次审计扫描的是整个仓库根目录。<许可证> permitted:自动判定结果——该许可证在“允许列表”中,无需人工干预。<许可证> manually approved:未进入自动允许列表,由人工批准(approve),并附带一段引用说明与批准人、时间,例如argparse v2.0.1的条目:### argparse v2.0.1 ##### Paths * /home/runner/work/liam/liam Python-2.0 manually approved >Python 2.0 license is compatible with Apache-2.0. But License Finder does >not support the name "Python-2.0". ><cite> OSPO @masutaka 2024-11-28</cite>
这份报告是静态产物:当pnpm-lock.yaml变化、依赖版本更新后,重新运行 LicenseFinder 即可重新生成,从而保证清单与锁文件一致。
配置侧的真相:license_finder.yml 与 dependency_decisions.yml
报告只是“输出”,真正定义审计规则的是config/下的两份文件。
config/license_finder.yml:工具行为配置
--- decisions_file: 'config/dependency_decisions.yml' enabled_package_managers: - pnpm内容极简但信息量完整:
decisions_file指定决策记录文件的相对路径(相对于仓库根目录);enabled_package_managers只启用pnpm——与仓库的实际情况一致:根目录 package.json 声明"packageManager": "pnpm@10.18.3",pnpm-workspace.yaml 将 workspace 包定义为frontend/apps/*、frontend/packages/*、frontend/packages/__mocks__/*、frontend/internal-packages/*。LicenseFinder 只解析 pnpm 的锁文件与依赖树,避免重复或漏检。
config/dependency_decisions.yml:人工决策台账
这份文件是许可证审计的“治理中枢”,当前包含 22 条记录,每条记录的结构为:
- - :permit # 动作:permit(许可证级)或 approve(包级) - MIT # 针对的许可证名或包名 - :who: OSPO @masutaka # 决策人 :why: Compatible with Apache-2.0 license. # 决策理由 :versions: [] # 限定的版本列表;[] 表示该包的所有版本 :when: 2024-11-22 08:47:26.167960000 Z # 决策时间两类动作有本质区别:
| 动作 | 作用对象 | 语义 | 典型场景 |
|---|---|---|---|
permit | 一种许可证 | 该许可证下任何包、任何版本都视为可接受 | MIT、Apache 2.0、ISC、BSD 各条款、Unlicense、CC0 1.0 Universal、Mozilla Public License 2.0、Eclipse 2.0、LGPL-3.0-or-later、Zlib、Artistic-2.0、CC-BY-3.0、CC-BY-4.0、MIT-0 等 |
approve | 某个具体包(可限定版本) | 该包被点名批准,记录理由与版本范围 | 工具误检或许可证名无法识别的个例 |
permit决策的理由高度一致:几乎每一条:why都是 "Compatible with Apache-2.0 license." 的变体,并附上许可证的官方说明出处——这正是“审计基线为 Apache-2.0”这一原则在配置层的落地。值得注意的是:versions: []表示不限版本,与approve中显式列出versions: [2.0.1]的写法形成对照:许可证级豁免是全局的,而包级批准往往只针对出问题的特定版本。
人工批准实例:工具误检的四种典型处理
config/dependency_decisions.yml 中的approve记录,对应报告 Items 部分的 4 处manually approved条目。逐个复盘,能看清 LicenseFinder 这类自动化工具的能力边界与正确的人工兜底方式。
1.argparse v2.0.1:许可证名不被工具识别
Python 的argparse2.x 采用 "Python Software Foundation License"(报告中记为 Python-2.0)。LicenseFinder 不认识这个许可证名称(:why字段引用了上游 LicenseFinder 的一个 pull request 佐证),于是审计会失败。处理方式:对该包approve,理由写明 "Python 2.0 license is compatible with Apache-2.0"。同一个包名下argparse v1.0.10是 MIT,仍走自动permitted通道——同一包不同版本可以走完全不同的判定路径,这就是版本级approve的价值。
2.jsonify v0.0.1:Public Domain 不是软件许可证
jsonify标记为 Public Domain,但 Public Domain 是权利放弃声明而非 SPDX 意义上的软件许可证,工具无法自动放行。决策记录写明 "Public Domain is compatible with Apache-2.0. But it is not a software license",并附了一条项目 issue 链接作为决策背景。人工批准解决。
3.spawndamnit v3.0.1:许可证字段写法导致误检
该包实际是 MIT,但license字段写成SEE LICENSE IN LICENSE这类间接指代,工具无法解析出实际许可证,报告中标记为Unknown manually approved。approve理由直接说明 "Its license is MIT, but it is mis-detected"。这类Unknown条目是审计时必须逐个清零的红项,任何一条无解释的 Unknown 都意味着合规盲区。
4.@browserbasehq/sdk与@vercel/gatsby-plugin-vercel-builder:Apache-2.0 却没被识别出来
这两个包的许可证就是Apache-2.0,但工具未能从包元数据中提取到(报告中均显示Unknown manually approved)。决策记录分别为 "The license is Apache-2.0. License Finder failed to identify the license." 与 "The license is Apache-2.0."。此外url-template v2.0.8也有一条approve:该包使用 BSD-3-Clause,但 2.0.8 版本的许可证标注错误(:why字段引用了上游的修复 PR),属于上游元数据错误导致的误检,同样以版本级批准兜底。
五个案例覆盖了自动审计的主要失败模式:许可证名缺失、非软件许可声明、间接指代、元数据标注错误。处理原则始终一致——人工判定实际许可证、记录理由与出处、限定影响范围(版本),让每一次例外都可追溯。
边界情况:Copyleft 与宽松许可的混排
Summary 中还有几个值得单独讨论的条目。
LGPL-3.0-or-later:对应@img/sharp-libvips-linux-x64(sharp 的 libvips 预编译二进制),这是报告中唯一一条 GPL 家族依赖。决策文件对LGPL-3.0-or-later做了permit,理由同样是 "Compatible with Apache-2.0 license"。需要注意其适用前提:LGPL 是弱 Copyleft,其约束主要针对对 LGPL 代码本身的修改与再分发;作为独立动态/预编译二进制被依赖时,与 Apache-2.0 项目共存的合规风险远低于 AGPL/GPL。团队以permit形式把这一判断固化进了配置,属于“人工评估后放行”而非“工具自动放行”。
双/多许可证条目:MIT OR Apache-2.0(2 个)、Apache-2.0 AND MIT(1 个)、(BSD-2-Clause OR MIT OR Apache-2.0)、(MIT AND BSD-3-Clause)、(MIT OR CC0-1.0)、(MIT AND Zlib)等。OR表示下游可任选其一(实践中选 MIT/Apache-2.0 即可),AND表示需同时满足两份条款(所列组合均相互兼容且宽松)。这类条目的存在提醒:许可证字符串本身可能是 SPDX 表达式,审计时不能只按“名字”匹配,必须理解表达式语义。
The Unlicense / CC0 1.0 Universal:两者都是公共领域奉献工具,对 Apache-2.0 完全兼容,报告中分别出现 2 个和 4 个依赖,配置里也都有对应permit。
版本并存:同一包名出现多行(如yallist同时有 v3.1.1 与 v5.0.0)是因为 pnpm workspace 下不同子包声明了不同版本区间。审计以“包名+版本”为最小单位,这正是 Monorepo 场景下依赖清单与单包项目最大的不同——它天然容纳了版本分叉。
如何阅读与使用这份审计报告
对不同类型的读者,docs/packages-license.md 的使用方式不同:
- 使用者/下游项目:若你的产品集成了 Liam(例如使用
@liam-hq/cli),这份报告告诉你 Liam 的依赖树中不存在 GPL/AGPL 等强 Copyleft 许可证,且所有例外都有书面决策依据。结合 LICENSE(Apache-2.0)即可做出引入决策。 - 贡献者:当你升级或新增依赖后,如果报告重新生成出现新的 Unknown 或不在
permit列表中的许可证,你需要按既有格式向 config/dependency_decisions.yml 追加一条:permit(新许可证)或:approve(个例包),:who、:why、:versions、:when四要素缺一不可。 - 合规审查者:
manually approved的引用块(含决策人、日期、理由)就是审计证据链。当前报告中恰好 3 条Unknown(@browserbasehq/sdk、@vercel/gatsby-plugin-vercel-builder、spawndamnit)加上 2 条有名字的例外(Python-2.0、Public Domain),每一条都能在决策文件中找到一一对应的记录,无悬空项。
小结
Liam 的依赖许可证审计方案可以概括为三层:
- 声明层:LICENSE 确立 Apache-2.0 基线,README.md 把报告作为正式文档链接出去;
- 配置层:config/license_finder.yml 限定 LicenseFinder 只审计 pnpm 依赖树;config/dependency_decisions.yml 以
permit(许可证级、不限版本)与approve(包级、可限定版本)两类带署名的决策记录,把“兼容性判断”变成可复核的数据; - 报告层:docs/packages-license.md 以 25 种许可证分类的 Summary + 逐包 Items 明细呈现结果,
permitted/manually approved两种状态让任何一条依赖的合规来源都能一眼追溯。
这套“自动审计 + 人工决策台账 + 全量报告”的组合,对任何以宽松许可证发布、依赖规模上千的 Monorepo 项目都具备直接可复用的参考价值:先明确自己的许可证基线,再让工具做全量扫描,最后用一份结构化的决策文件把所有例外钉死在证据链上。
- 数据可视化
- 数据库
- 前端
- CLI
【免费下载链接】liam
Automatically generates beautiful and easy-to-read ER diagrams from your database.
相关推荐
ReID-Survey核心突破:UntransReID如何实现无监督行人重识别SOTA性能?
ReID Survey核心突破:UntransReID如何实现无监督行人重识别SOTA性能? 在计算机视觉领域,行人重识别(Person Re Identifi
人工智能计算机视觉深度学习科研本地跑起一套量化交易系统:Qbot 的模块拆解与上手实录
本地跑起一套量化交易系统:Qbot 的模块拆解与上手实录 Qbot 是一个完全本地部署的 AI 量化交易平台,覆盖行情数据、策略开发、策略回测到实盘自动交易的全
金融科技人工智能后端前端深度学习KubeEdge:从部署 CloudCore 到接入第一个边缘节点,云边设备管理如何落地
KubeEdge:从部署 CloudCore 到接入第一个边缘节点,云边设备管理如何落地 工厂车间里的 PLC 断网两小时,数据丢了、状态没同步、云端看板一片灰
云原生边缘计算物联网容器编排边缘网关
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考