Skip to content
Apothem
参考

依赖锁定清单

依赖声明策略,涵盖 Python 运行时版本范围、开发工具链、发布工具锁文件,以及每个构建表面所批准的版本下限。

Apothem 如何声明、固定与锁定依赖的既定姿态。受众:需要理解可复现性模型的贡献者与下游消费者。

姿态

Apothem 是一个 Python CLI,拥有一组很小的运行时依赖,外加一套更庞大的开发、审计、发布与文档工具链。运行时依赖在 pyproject.toml 中使用保守的下限;网站提交 site/package-lock.json;GitHub Actions 通过不可变 SHA 固定并附带版本注释,由 Dependabot 进行升级。

类别声明方式锁定策略
运行时 Python 依赖pyproject.toml 中的 [project.dependencies]面向用户的下限范围;包中不附带 Python 运行时锁文件。CI 安装这些依赖的哈希锁定闭包(见 CI 依赖锁)
开发 / 安全工具[project.optional-dependencies] extras下限范围,但 ruff 和 mypy 除外:dev extra 将二者精确固定为 CI 运行的版本(ruff 见 CI 工具锁);CI 从哈希锁定闭包安装 dev extra(见 CI 依赖锁)
发布工具发布工作流使用 scripts/release/requirements-build-linux.txt;本地构建使用 [project.optional-dependencies] 中的 release extra为发布工作流哈希锁定;由发布工具闭包生成。release extra 是下限范围
CI pipscripts/ci/requirements-ci-pip.txt哈希锁定:一个 pip==X.Y.Z 固定,附带其 wheel 与 sdist 的哈希,由每个升级 pip 的工作流以 --require-hashes -r 安装。Dependabot 提出升级并重写哈希;scripts/dev/check_pip_pin_drift.py 将该固定保持在 pip 26.2 或更高版本
CI 依赖锁scripts/ci/requirements-ci-dev.txt、-runtime.txt、-test.txt 与 -prereqs.txt哈希锁定的通用闭包,用 uv pip compile 从 pyproject.toml(-prereqs.txt 则从引擎的宿主前置条件)编译,基准为锁定日期减去 7 天冷却期,二者都记录在各文件中;以 --require-hashes -r 安装(包本身用 --no-deps -e . 安装)。在 scripts/dev/refresh_ci_locks.py 重新生成之前,CI 一直测试锁定的版本;pip-audit.yml 每周审计允许的最新版本,并审计这些锁。当某个锁不再满足其输入时,tests/scripts/test_ci_locks_fresh.py 失败
CI 工具锁scripts/ci/requirements-ci-ruff.txt、-bandit.txt、-pip-audit.txt、-pip-licenses.txt 与 -pre-commit.txt工作流运行的每个工具各有一个哈希锁定闭包,由同一脚本依据 scripts/dev/ci_lock_table.py 中的精确规格编译,使用相同的锁定日期与冷却期,并以 --require-hashes -r 安装。没有工作流写明工具版本。pre-commit 需要 Python 3.11 或更高版本,因此其锁也是如此;pip-audit 的锁同样针对 3.11 编译,因为支持 3.10 的最新 filelock 存在未修复的安全公告。除 CI pip 外,没有锁固定 pip
文档站点site/package.json提交 site/package-lock.json 并由 npm ci 消费
VS Code 打包器tools/vsce/package.json提交 tools/vsce/package-lock.json;release 与 publish 作业运行 npm ci --ignore-scripts
Channel-smoke 工具 CLItools/channel-smoke/package.json提交 tools/channel-smoke/package-lock.json 并由 npm ci 消费
GitHub Actions带版本注释的 SHA 固定 uses: 引用Dependabot 每周提出更新;例外项携带 action-pinning-exempt: 标记

发布批准

表面已批准状态理由
运行时依赖当前下限位于 pyproject.toml [project.dependencies]发布前批准当前稳定下限;无已知运行时 CVE 阻断项
开发工具当前下限位于 pyproject.toml dev extra;ruff 与 mypy 在其中精确固定发布前批准最新稳定下限;ruff 与 mypy 各自固定为同一个版本,供钩子、CI 与贡献者共用,mypy 钩子将其列出的库固定为 dev 锁中的版本,版本不一致时 tests/scripts/test_ci_tool_locks.py 失败
安全工具当前下限位于 pyproject.toml security extra发布前批准当前扫描器下限
发布工具哈希锁定闭包位于 scripts/release/requirements-build-linux.txt发布工作流以 --require-hashes 安装;本地脚本要求 build 已存在,而非静默安装漂移的依赖
CI pip哈希锁定的固定位于 scripts/ci/requirements-ci-pip.txtpip 26.2 或更高版本修复了 CVE-2026-13346;工作流以 --require-hashes 安装它,因此下载内容会与其公布的哈希比对
CI 依赖锁哈希锁定闭包位于 scripts/ci/requirements-ci-{dev,runtime,test,prereqs}.txt工作流以 --require-hashes 安装它们;每个文件记录其锁定日期、冷却期与截止时间,新鲜度测试使它们与 pyproject.toml 保持同步
CI 工具锁哈希锁定闭包位于 scripts/ci/requirements-ci-{ruff,bandit,pip-audit,pip-licenses,pre-commit}.txt工作流以 --require-hashes 安装它们,且不写明工具版本;规格位于 scripts/dev/ci_lock_table.py,锁定日期、冷却期与截止时间与依赖锁一致
文档工具当前范围位于 site/package.json;精确解析位于 site/package-lock.json锁文件刷新后 npm outdated 不返回任何陈旧的文档依赖
GitHub Actions当前发布保留既有 SHA 固定;SLSA 可复用工作流仍是已记录的标签固定例外,在 release.yml 中以 action-pinning-exempt: 注释标记公共 CI 重跑结果为绿;为避免发布表面的频繁变动,广泛的 action 大版本升级有意留给依赖自动化的拉取请求
npm 打包publish-npm.yml 工作流在 v*.*.* 标签推送时运行,并以 workflow_dispatch 作为后备唯一的公共 npm 包是 @ahmed-g-gad/apothem;发布会等待操作者对 release 环境的批准,该批准在 scripts/release/check-release-ready.sh 通过之后给出

锁定

贡献者自己的 Python 锁文件按需生成,不会提交:Apothem 的运行时闭包很小,且 CLI 支持多个 Python 小版本。CI 是例外。它安装 scripts/ci/ 中的哈希锁定文件:四个依赖锁(requirements-ci-dev.txt、-runtime.txt、-test.txt 与 -prereqs.txt),以及工作流运行的每个工具各一个锁(-ruff.txt、-bandit.txt、-pip-audit.txt、-pip-licenses.txt 与 -pre-commit.txt)。python scripts/dev/refresh_ci_locks.py 以锁定日期减去 7 天冷却期(与 Dependabot 的冷却期一致)为基准编译全部九个文件,因此 CI 测试的是在锁定日期时已发布至少一周的版本,刷新这些文件会让这些版本向前推进。每个文件记录锁定日期、冷却期以及由此得出的截止时间,--check 会重现该截止时间。比冷却期更新的紧急安全修复需要以 --cooldown-days 0 运行一次刷新,或在 scripts/dev/ci_lock_table.py 中为其包设置冷却期例外,该例外只对这一个包解除冷却期。每周的 pip-audit.yml 运行会审计 pyproject.toml 允许的最新版本以及这九个锁,并向 OSV 服务查询安全公告。贡献者仍使用 pip install -e ".[dev]" 安装。

# Astral uv:
uv pip install -e ".[dev,security,release]"
uv pip freeze > requirements.dev.lock

# pip-tools:
pip-compile --extra=dev --extra=security --extra=release -o requirements.dev.lock pyproject.toml

# Plain pip:
pip install -e ".[dev,security,release]"
pip freeze > requirements.dev.lock

网站则不同:site/package-lock.json 被提交,且 CI 使用 npm ci,因此渲染出的文档站点拥有一个可复现的 Node 闭包。

理由

  • 为什么 Python 用范围固定? CLI 支持广泛的 Python 矩阵,因此 pyproject.toml 保持保守的下限。CI 安装这些范围的哈希锁定闭包,由维护者用 scripts/dev/refresh_ci_locks.py 刷新(预期节奏为每周一次),而每周的 pip-audit.yml 运行会解析允许的最新版本,以揭示与当前工具之间的问题。
  • 为什么提交 Node 锁文件? 网站是一个已部署的静态产物;锁文件为那个公共表面提供确定性的 npm ci 构建。
  • 为什么用 SHA 固定 Actions? 标签是可变的;SHA 是不可变的。Dependabot 提出 SHA 升级并重写每条版本注释,而发布闸门记录任何有意的搁置。
  • 更新机器人不触及的内容。 Dependabot 版本更新(.github/dependabot.yml)涵盖 GitHub Actions 固定、网站锁文件、vsce 打包器锁文件(tools/vsce)、channel smoke 所安装的工具 CLI 锁文件(tools/channel-smoke)、CI 安装的哈希锁定 pip(scripts/ci/requirements-ci-pip.txt)以及 pre-commit 钩子。scripts/ci 中的其余九个文件是用 scripts/dev/refresh_ci_locks.py 整体重新生成的闭包,其中没有一个固定 pip,且 Dependabot 对该文件夹的版本更新仅限于 pip。pyproject.toml 中的运行时下限、哈希锁定的发布依赖、src/apothem/_vendor/vendor.txt 中的 vendored 闭包,以及与 pyproject.toml 同步固定的 pre-commit 钩子都由人工调整,因为单个固定的升级会破坏与之保持一致的那些文件。ruff 与 mypy 钩子就是其中两个:tests/scripts/test_ci_tool_locks.py 会一直失败,直到 pyproject.toml 中 ruff 的精确固定、钩子 rev、ruff 锁与 dev 锁使用同一版本,并且 mypy 的精确固定、mypy 钩子 rev 以及该钩子列出的库都与 dev 锁一致;因此,针对这两个工具中任一个的 Dependabot 安全拉取请求需要人工调整。网站的 @types/node 大版本跟随其 Node 下限(engines 与 .nvmrc,Node 22),因此 Dependabot 只提出其小版本与补丁更新;请将该大版本与下限一同提升。版本更新不会改动 npm 的 overrides 块。Dependabot 安全更新仍然涵盖每一个清单,因此一个安全拉取请求可能会提出上述某个固定的升级,或改动某个 override;请人工调整它或将其关闭。
  • 合并 Dependabot 拉取请求。 仓库的 squash 默认设置采用 PR 标题。部分 Dependabot 标题(主要是点名某个目录的分组标题)会超出 72 字符的 squash 主题上限;PR Title 检查会列出它们,重新命名标题即可修复。更改工具锁文件的拉取请求不会运行任何 ci.yml 作业,因此由 npm audit 工作流对其进行审计与安装,针对 vsce 锁文件打包 VS Code 扩展并运行 vsce 发布测试,而针对 channel-smoke 锁文件则运行 channel smoke 工作流。Markdown Lint 工作流会在 pre-commit 钩子拉取请求上运行 markdownlint 钩子;其他钩子不会在 CI 中得到演练,因此请在合并之前于其分支上运行 pre-commit run --all-files。

On this page