[rush] package using rig with sharding defined is required to define _phase:name:shard script, even if it doesn't have the phase
Nobody has claimed this yet.
Assessment
- Difficulty
- 3/5
- Estimated time
- 1-2 days
- Newbie friendliness
- 68/100
- Issue type
- Bug
- Clarity
- Clearly specified
- Activity status
- Quiet
- Tech stack
- typescript
- Domain
- build-system, tooling
Research direction
Start in libraries/rush-lib/src/logic/operations/ShardedPhaseOperationPlugin.ts and compare its handling of the base phase script with the operation setup in PhasedOperationPlugin.ts. Review plugin registration in PhasedScriptAction.ts, then reproduce the rig-based sharding case; done means non-participating projects are skipped while projects defining the base phase still receive the missing shard-script error.
Written by the indexing model from the issue text.
Description
Summary
I'm working on converting our Rush monorepo to shard our various test related processes. One issue I have run into is that now that I have added sharding for the test phases into our shared rig configs, EVERY PACKAGE must have every shard phase's script defined or Rush throws The project 'X' does not define a '_phase:name:shard' command in the 'scripts' section of its package.json for each package that doesn't define it. Which means I need to add dummy shard phase scripts to all packages that don't even participate in the phase, which is less than ideal.
Repro steps
- In a Rush monorepo, put
sharding: { count: N }on a phase operation (e.g._phase:test) inside a rig'sconfig/rush-project.json. - Have two kinds of projects using that rig:
- Project A: defines both
_phase:testand_phase:test:shardin itspackage.jsonscripts. - Project B: defines neither — it simply doesn't participate in the test phase.
- Project A: defines both
- Run
rush test(or any phased command that includes_phase:testand selects both projects).
Expected result: Project A gets sharded. Project B's _phase:test op resolves to a no-op (same as it does today for a project that doesn't define a phase script), and sharding is skipped for it.
Actual result: Rush throws during operation graph construction:
The project '@kx/bulk-package-json-editor' does not define a
'_phase:test:shard' command in the 'scripts' section of its package.json
…even though @kx/bulk-package-json-editor also does not define _phase:test and would otherwise be treated as a no-op for that phase.
Details
This was generated by opus 4.7 with claude code, I don't know the details of the code but it seems accurate.
Root cause, tracing through rush-lib on main:
-
PhasedOperationPlugin(libraries/rush-lib/src/logic/operations/PhasedOperationPlugin.ts:44-48) creates an operation for every(phase, project)pair in the selection, regardless of whether the project defines a script for that phase. The "this project doesn't use this phase" decision is deferred to whichever runner plugin picks it up. -
Each operation's
settingsis pulled fromprojectConfigurations.get(project)?.operationSettingsByOperationName.get(name)(same file, line 63–65). Because settings originate from the rig'srush-project.json, they apply to every project using that rig — including projects that don't implement the phase at all. Sosettings.shardingis attached to those ops. -
Plugin registration order in
PhasedScriptAction.ts:417-421:new PhasedOperationPlugin().apply(hooks); new ShardedPhasedOperationPlugin().apply(hooks); new ShellOperationRunnerPlugin().apply(hooks);ShardedPhasedOperationPlugintaps intocreateOperationsbeforeShellOperationRunnerPlugindoes. -
The guard in
ShardedPhaseOperationPlugin.ts:59is:if (operationSettings?.sharding && !operation.runner) { ... }This appears to be trying to skip sharding for operations that have already been marked as no-ops — but because the sharding plugin runs before the shell plugin,
operation.runneris always stillundefinedat this point, even for projects that have no_phase:testscript and would be turned into aNullOperationRunnermoments later. -
The strict throw at
ShardedPhaseOperationPlugin.ts:148-152then fires, because the project doesn't define_phase:test:shard:const baseCommand: string | undefined = scripts?.[shardOperationName]; if (baseCommand === undefined) { throw new Error( `The project '${project.packageName}' does not define a '${phase.name}:shard' command in the 'scripts' section of its package.json` ); }Nothing checks whether the base phase script (
scripts[phase.name]) exists. If it had, we could cleanly distinguish "this project uses the phase but has no shard script" (keep throwing — that's a real config mistake) from "this project doesn't use the phase at all" (skip sharding, let ShellOperationRunnerPlugin NullOp it).
Suggested fix — either of:
-
(a) Skip sharding when the base phase script is not defined, in
ShardedPhaseOperationPlugin.ts:const { scripts } = project.packageJson; const phaseCommand = phase.shellCommand ?? scripts?.[phase.name]; if (phaseCommand === undefined) { continue; // no-op for this project; let ShellOperationRunnerPlugin handle it }Inserted before the current work at line 60. Preserves the strict error for projects that do define the base phase script but forgot the
:shardvariant. -
(b) Swap plugin registration order so
ShellOperationRunnerPluginruns first and the existing!operation.runnerguard actually does what it looks like it was intended to do. More invasive — likely has other ordering implications.
(a) is the minimal fix and preserves existing behaviour for correctly-configured projects.
Standard questions
Please answer these questions to help us investigate your issue more quickly:
| Question | Answer |
|---|---|
@microsoft/rush globally installed version? |
5.165.0 |
rushVersion from rush.json? |
5.165.0 |
pnpmVersion, npmVersion, or yarnVersion from rush.json? |
pnpm@10.24.0 |
(if pnpm) useWorkspaces from pnpm-config.json? |
true |
| Operating system? | Linux |
| Would you consider contributing a PR? | Yes |
Node.js version (node -v)? |
22.14.0 |
- Dominant language
- TypeScript
- Stars
- 6.5k
- Forks
- 708
- Avg merge
- 5d 19h
- Merged PRs (30d)
- 48
Contributor guide
No contributing guide indexed for this repository
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
More from microsoft/rushstack
-
Difficulty 2/5 1-3 hours Newbie friendliness 72/100
-
Difficulty 2/5 1-3 hours Newbie friendliness 65/100
-
Difficulty 2/5 1-3 hours Newbie friendliness 68/100
-
Difficulty 2/5 1-3 hours Newbie friendliness 70/100
-
Difficulty 4/5 3-5 days Newbie friendliness 48/100
All issues in microsoft/rushstack
Similar issues
-
clawsweeper:linked-pr-open clawsweeper:no-new-fix-pr clawsweeper:source-repro impact:message-loss issue-rating: 🦞 diamond lobster maturity:stable P2
Difficulty 2/5 1-3 hours Newbie friendliness 88/100
-
Difficulty 2/5 1-3 hours Newbie friendliness 84/100
Eynzof/Hermes-CN-Desktop#616 ·
-
ZCode 3.14.3 に対応する Open
Difficulty 2/5 1-3 hours Newbie friendliness 68/100
supermomonga/zcode-acp#24 ·
-
Difficulty 2/5 1-3 hours Newbie friendliness 76/100
growthbook/growthbook#7100 ·
-
triage
Difficulty 1/5 1-3 hours Newbie friendliness 88/100