14 个版本 (重大变更)
0.12.0 | 2023 年 6 月 26 日 |
---|---|
0.10.0 | 2022 年 11 月 7 日 |
0.9.0 | 2022 年 3 月 15 日 |
0.7.0 | 2021 年 11 月 23 日 |
0.1.1 | 2020 年 12 月 2 日 |
#3 in #guppy
42,910 每月下载量
在 6 个 crate 中使用 (5 个直接使用)
575KB
10K SLoC
determinator
确定 Rust 工作区中两个提交之间哪些包发生了变化。
典型的持续集成系统会在每个拉取请求或提议的更改上运行每个构建和每个测试。在大型单体仓库中,大多数提议的更改对大多数包没有影响。一个 目标判定器 根据提议的更改决定哪些包可能已发生更改。
判定器旨在用于 Diem Core 工作区,这是一个此类单体仓库。
示例
use determinator::{Determinator, rules::DeterminatorRules};
use guppy::{CargoMetadata, graph::DependencyDirection};
use std::path::Path;
// guppy accepts `cargo metadata` JSON output. Use a pre-existing fixture for these examples.
let old_metadata = CargoMetadata::parse_json(include_str!("../../../fixtures/guppy/metadata_guppy_78cb7e8.json")).unwrap();
let old = old_metadata.build_graph().unwrap();
let new_metadata = CargoMetadata::parse_json(include_str!("../../../fixtures/guppy/metadata_guppy_869476c.json")).unwrap();
let new = new_metadata.build_graph().unwrap();
let mut determinator = Determinator::new(&old, &new);
// The determinator supports custom rules read from a TOML file.
let rules = DeterminatorRules::parse(include_str!("../../../fixtures/guppy/path-rules.toml")).unwrap();
determinator.set_rules(&rules).unwrap();
// The determinator expects a list of changed files to be passed in.
determinator.add_changed_paths(vec!["guppy/src/lib.rs", "tools/determinator/README.md"]);
let determinator_set = determinator.compute();
// determinator_set.affected_set contains the workspace packages directly or indirectly affected
// by the change.
for package in determinator_set.affected_set.packages(DependencyDirection::Forward) {
println!("affected: {}", package.name());
}
平台支持
- Unix 平台:判定器工作且受支持。
- Windows:实验性支持。路径归一化方面可能仍然存在错误:请 报告它们!
工作原理
Rust 包的行为可能因以下之一的变化而不同
- 包的源代码或
Cargo.toml
。 - 一个依赖项。
- 构建或测试环境。
判定器从多个来源收集数据,并通过 guppy 处理这些数据,以确定哪些包需要重新测试。
文件更改
判定器将两个版本之间的文件更改列表作为输入。对于提供的每个文件
- 判定器会查找离文件最近的包并将其标记为已更改。
- 如果文件位于包之外,判定器假定需要重新构建一切。
可以从 Git 等源代码控制系统获取文件更改列表。此 crate 提供了一个简化枚举文件列表并处理一些复杂边缘情况的辅助工具。有关更多信息,请参阅 Utf8Paths0
的文档。
这些简单规则可能需要根据特定场景进行定制(例如,忽略某些文件或如果包外的文件更改则标记包为已更改)。对于这些情况,判定器允许您指定 自定义规则。有关更多信息,请参阅下文的 自定义行为 部分。
依赖项更改
如果以下之一发生变化,则假定依赖项已更改
- 对于工作区依赖项,其源代码。
- 对于第三方依赖,其版本或功能集。
- 它所依赖的环境中的某些内容。
确定器会对工作空间中的每个包运行Cargo构建模拟。对于每个包,确定器会确定其依赖项(包括功能集)是否有任何变化。这些模拟是在以下条件下进行的:
- 启用dev依赖项(默认;可以自定义)
- 主机和目标平台都设置为当前平台(默认;可以自定义)
- 每个包的三套功能
- 未启用任何功能
- 默认功能
- 启用所有功能
如果这些模拟构建中的任何一项表明工作空间包的任何依赖项发生了变化,则将其标记为已更改。
环境变化
构建或测试运行的“环境”是指任何不是源代码部分的,可能会影响它的事物。这包括但不限于
- 使用的Rust编译器的版本
- 包所依赖的系统库
- 包所依赖的环境变量
- 测试所依赖的外部服务
默认情况下,确定器假设环境在运行之间保持不变。
为了表示环境的变化,你可能需要找到将这些变化表示为检入到仓库中的文件的方法,并为它们添加自定义规则。例如
- 使用
rust-toolchain
文件来表示Rust编译器的版本。有一个默认规则,如果在rust-toolchain
发生变化时会导致完整运行。 - 将所有环境变量记录在CI配置文件中,例如GitHub Actions工作流程文件中,并在任何这些文件发生变化时添加自定义规则以进行完整运行。
- 尽可能使测试具有封闭性,不调用网络。如果你只有少数几个进行网络调用的测试,请无条件运行它们。
自定义行为
确定器遵循的标准规则在某些情况下可能需要调整
- 某些文件应该被忽略。
- 如果某些文件或包发生变化,可能需要进行完整测试运行。
- Cargo不认识的虚拟依赖可能需要被插入。
对于这些情况,确定器允许指定自定义的规则。确定器还提供了一套默认规则,用于处理诸如.gitignore
和rust-toolchain
等常见文件。
有关自定义规则的更多信息,请参阅rules
模块的文档。
限制
虽然确定器可以为CI和本地工作流程带来显著的好处,但其模型与Cargo的模型相当不同。请在为项目使用确定器之前了解这些限制。
为了获得最佳效果,除了基于确定器的运行外,还应考虑偶尔进行完整运行。你可能希望配置你的CI系统使用确定器进行拉取请求,并在主分支上每几个小时安排一次完整运行,以防确定器错过某些内容。
构建脚本和包含/排除指令
确定器无法运行构建脚本。声明对文件或环境变量的依赖项的标准Cargo方法是,在构建脚本中输出rerun-if-changed
或rerun-if-env-changed
指令。这些指令必须通过自定义规则进行复制。
确定器不跟踪 include
和 exclude
字段在 Cargo.toml
中的。 这是因为确定器对变更的看法并不总是与这些字段一致。例如,软件包通常包括 README
文件,但确定器有一个默认规则来忽略它们。
如果软件包中包含工作区之外的文件,要么将其移动到软件包中(推荐),要么为其添加自定义规则。排除项可能会作为导致这些文件被忽略的自定义规则重复。
工作区外的路径依赖
确定器可能无法确定工作区之外的路径依赖的变更。 确定器依靠元数据来确定非工作区依赖是否已更改。这些元数据包括
- 版本号
- 源,例如
crates.io
或 Git 仓库中的修订版本
这种方法适用于对 crates.io
或其他软件包仓库的依赖,因为这些源代码的更改必然需要版本更改。
这种方法也适用于 Git 依赖。即使 Git 依赖未固定到 Cargo.toml
中的确切修订版本,这也适用,因为 Cargo.lock
记录了确切的修订版本。例如
# Specifying this in Cargo.toml...
[dependencies]
rand = { git = "https://github.com/rust-random/rand", branch = "master" }
# ...results in Cargo.lock with:
[[package]]
name = "rand"
version = "0.7.4"
source = "git+https://github.com/rust-random/rand?branch=master#50c34064c80762ddae11447adc6240f42a6bd266"
最后的哈希值是使用的确切 Git 修订版本,而变更会由确定器识别。
此方案可能不适用于路径依赖,因为磁盘上的文件可以更改而无需版本升级。 cargo build
可以识别这些更改,因为它比较磁盘上文件的最后修改时间,但确定器不能这样做。
预计这对大多数使用工作区的项目来说不会是问题。如果未来有需求,如果它们位于同一仓库中,就有可能添加对非工作区路径依赖变更的支持。
替代方案和权衡
看待确定器的一种方式是将其视为一种 缓存失效。通过这个视角来看,构建或测试系统的主要目的是缓存结果,并基于某些参数使这些缓存失效。当确定器将软件包标记为已更改时,它会使该软件包的任何缓存结果失效。
还有几种其他方式来设计缓存系统
- 内置于 Cargo 和 GNU Make 等系统中的缓存,这些系统基于文件修改时间。
- Mozilla 的
sccache
和其他“自下而上”的基于哈希的缓存构建系统。 - Bazel、Buck 和其他“自上而下”的基于哈希的缓存构建系统。
这些其他系统最终会做出不同的权衡
- Cargo 可以使用构建脚本来跟踪随时间变化的文件和环境更改。然而,它依赖于在相同机器上执行之前的构建。截至 Rust 1.48,无法使用 Cargo 缓存测试结果,只能用于构建。
sccache
需要在机器间具有精确的路径,并且无法缓存 某些类型的 Rust 艺术品。同样,就像 Cargo 的缓存一样,无法用于测试结果,只能用于构建。- bazel和buck对环境要求严格,以确保不影响构建结果。它们与Cargo也没有无缝集成。
- 该判定器适用于构建和测试,但不能跟踪文件和环境随时间的变化,必须依赖自定义规则。这种方案可能会产生误判。
虽然该判定器主要针对测试运行,但它也适用于构建。如果您想使用该判定器进行构建运行,请考虑将其与另一层缓存堆叠使用。
- 将判定器用作第一次筛选,以过滤掉未更改的包。
- 然后使用类似
sccache
的系统,为构建提供基于哈希的缓存。
灵感来源
此判定器受到Facebook主源仓库中使用的目标判定器的启发,并与其共享名称。
贡献
有关如何帮助的详细信息,请参阅CONTRIBUTING文件。
许可证
该项目可在Apache 2.0许可证或MIT许可证的条款下使用。
依赖项
~11MB
~202K SLoC