Rush Stack商店博客活动
跳至主要内容

启用分阶段构建

默认情况下,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),它只能在 testretest 命令中指定,并且只会被传递给 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"
}
}

上面的示例尝试将开发人员对 buildtest 命令的期望对齐

  • 进入项目文件夹并运行 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 问题供您关注