启用分阶段构建
默认情况下,Rush 通过在每个项目文件夹中分别运行构建脚本(类似于 npm run build
)来构建每个项目,在依赖关系图允许的情况下并行处理项目。从 Rush 的角度来看,构建脚本内部发生的一切都是一个单独的操作。
分阶段构建 是一种通过将单个操作定义为可以在项目上执行的阶段 来提高并行性的方法。例如,如果项目 B 依赖于项目 A,我们可以先构建项目 A,然后在并行运行项目 A 的单元测试时开始构建项目 B。
注意:分阶段构建建立在构建缓存功能之上,并需要构建缓存功能。如果您还没有为您的单体仓库启用构建缓存,请参阅 启用构建缓存.
启用实验
在 common/config/rush/experiments.json
中,启用 "phasedCommands"
实验。
{
"phasedCommands": true
}
定义阶段
在 common/config/rush/command-line.json
中,添加一个名为 "phases"
的部分,如下所示
{
"phases": [
{
/**
* The name of the phase. Note that this value must start with the \"_phase:\" prefix.
*/
"name": "_phase:build",
/**
* The dependencies of this phase.
*/
"dependencies": {
"upstream": ["_phase:build"]
},
/**
* Normally Rush requires that each project's package.json has a \"scripts\" entry matching the phase name. To disable this check, set \"ignoreMissingScript\" to true.
*/
"ignoreMissingScript": true,
/**
* By default, Rush returns a nonzero exit code if errors or warnings occur during a command. If this option is set to \"true\", Rush will return a zero exit code if warnings occur during the execution of this phase.
*/
"allowWarningsOnSuccess": false
},
{
"name": "_phase:test",
"dependencies": {
"self": ["_phase:build"]
},
"ignoreMissingScript": true,
"allowWarningsOnSuccess": false
}
]
}
在这个例子中,我们定义了两个阶段——_phase:build
和 _phase:test
。_phase:build
操作依赖于其上游项目的 _phase:build
操作(使用传统的 Rush 依赖关系图)。_phase:test
操作不依赖于任何上游项目,但需要先完成其自身项目的 _phase:build
操作。请注意,阶段名称必须以 _phase:
开头。
各个项目可以选择不实现阶段(如果启用了 ignoreMissingScript
),但它们不能定义自己的阶段,也不能更改阶段的依赖关系。这确保了阶段在您的单体仓库中始终保持一致的行为,无论您构建的项目子集是什么。
重新定义构建和测试命令
在 common/config/rush/command-line.json
中,在 "commands"
部分中,将 "build"
命令重新定义为 phased
命令,而不是 bulk
命令,并指定您希望它运行的阶段。在下面的示例中,我们还定义了一个 "test"
命令。
{
"commands": [
{
"commandKind": "phased",
"name": "build",
"phases": ["_phase:build"],
"enableParallelism": true,
"incremental": true
},
// No need to define "rebuild", by default, it is the same as build
// but with incremental=false.
{
"commandKind": "phased",
"name": "test",
"summary": "Build and test all projects.",
"phases": ["_phase:build", "_phase:test"],
"enableParallelism": true,
"incremental": true
},
{
"commandKind": "phased",
"name": "retest",
"summary": "Build and test all projects.",
"phases": ["_phase:build", "_phase:test"],
"enableParallelism": true,
"incremental": false
}
]
}
此命令定义展示了分阶段构建的另一个有用功能:我们可以创建“阶段”构建块,然后用它们构建命令。rush build
不再运行所有项目的构建和测试,而是可以定义 rush build
代表“构建所有内容,但不进行测试”,而 rush test
代表“构建所有内容并运行测试”。
向阶段分配参数
如果您在 command-line.json
中为构建命令定义了任何自定义参数,您现在需要将它们与阶段关联起来,以便 Rush 知道哪些阶段可以接受您的参数。
以下是一些示例
{
"parameters": [
{
"longName": "--production",
"parameterKind": "flag",
"description": "Perform a production build, including minification and localization steps",
"associatedCommands": ["build", "rebuild", "test", "retest"],
"associatedPhases": ["_phase:build"]
},
{
"longName": "--update-snapshots",
"parameterKind": "flag",
"description": "Update unit test snapshots for all projects",
"associatedCommands": ["test", "retest"],
"associatedPhases": ["_phase:test"]
}
]
}
在这里,我们定义了一个标志(--production
),它可以在我们的构建命令的 4 个变体中指定,但它只会被传递给构建阶段。此外,我们还定义了另一个标志(--update-snapshots
),它只能在 test
和 retest
命令中指定,并且只会被传递给 test
阶段。
因此,如果我们要执行以下命令
rush test --production --update-snapshots
Rush 会将 --production
参数传递给每个项目的 _phase:build
脚本,然后将 --update-snapshots
参数传递给每个项目的 _phase:test
脚本。
将阶段脚本添加到您的项目中
在单体仓库中每个项目的 package.json
文件中,添加新的 _phase:
脚本
{
"scripts": {
"_phase:build": "heft build --clean",
"_phase:test": "heft test --no-build",
"build": "heft build --clean",
"test": "heft test --clean"
}
}
上面的示例尝试将开发人员对 build
和 test
命令的期望对齐
- 进入项目文件夹并运行
rushx build
会清理并构建项目,不会进行测试。 - 进入项目文件夹并运行
rushx test
会清理、构建和测试项目。 - 运行
rush build --only <project>
会清理并构建项目,不会进行测试。 - 运行
rush test --only <project>
会清理、构建和测试项目。
在可能的情况下,对于您定义的任何自定义阶段,请牢记此模式——重要的不是阶段是否与 rushx 命令完全相同,而是 rush <something>
和 rushx <something>
是否会产生类似的结果(如果适用)。
某些项目可能在某个阶段没有有意义的工作要做,在这种情况下,您可以将其定义为空操作(""
),或者如果在阶段定义中指定了 ignoreMissingScript
,则完全将其省略。
定义每个阶段的输出文件夹名称
在每个项目的 rush-project.json
配置文件(或更优的是每个 rig 配置文件)中,重新定义您的 operationSettings
,以便每个文件夹只在一个阶段中指定。例如
{
"operationSettings": [
// Old configuration (before phases)
{
"operationName": "build",
"outputFolderNames": ["lib", "lib-commonjs", "dist", "temp"]
},
// New configuration (after phases)
{
"operationName": "_phase:build",
"outputFolderNames": ["lib", "lib-commonjs", "dist"]
},
{
"operationName": "_phase:test",
"outputFolderNames": ["temp/coverage", "temp/jest-reports"]
}
]
}
注意 _phase:build
和 _phase:test
指定的输出文件夹之间没有重叠——这是分阶段构建的一项重要的新要求。一般来说,如果 Rush 可以通过其他操作修改操作的输出,则 Rush 无法可靠地缓存该操作的输出,因此您应该将操作结构化,以便如果 _phase:build
生成了一个 "lib"
文件夹,则其他操作不会将输出放入该文件夹。
分阶段构建功能仍在开发中。欢迎提供反馈!
一些相关的 GitHub 问题供您关注