8000 [rush-sdk] Use rush-lib from parent process · pullup-0/rushstack@4ebdf5b · GitHub
[go: up one dir, main page]

Skip to content

Commit 4ebdf5b

Browse files
committed
[rush-sdk] Use rush-lib from parent process
1 parent ac4e41e commit 4ebdf5b

File tree

9 files changed

+216
-15
lines changed

9 files changed

+216
-15
lines changed
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
{
2+
"changes": [
3+
{
4+
"packageName": "@microsoft/rush",
5+
"comment": "Add code path to @rushstack/rush-sdk for using the @microsoft/rush-lib version from a parent process.",
6+
"type": "none"
7+
}
8+
],
9+
"packageName": "@microsoft/rush"
10+
}

common/reviews/api/rush-lib.api.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -177,6 +177,7 @@ export enum EnvironmentVariableNames {
177177
RUSH_GIT_BINARY_PATH = "RUSH_GIT_BINARY_PATH",
178178
RUSH_GLOBAL_FOLDER = "RUSH_GLOBAL_FOLDER",
179179
RUSH_INVOKED_FOLDER = "RUSH_INVOKED_FOLDER",
180+
RUSH_LIB_PATH = "RUSH_LIB_PATH",
180181
RUSH_PARALLELISM = "RUSH_PARALLELISM",
181182
RUSH_PNPM_STORE_PATH = "RUSH_PNPM_STORE_PATH",
182183
RUSH_PNPM_VERIFY_STORE_INTEGRITY = "RUSH_PNPM_VERIFY_STORE_INTEGRITY",

libraries/rush-lib/package.json

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -89,5 +89,11 @@
8989
"publishOnlyDependencies": {
9090
"@rushstack/rush-amazon-s3-build-cache-plugin": "workspace:*",
9191
"@rushstack/rush-azure-storage-build-cache-plugin": "workspace:*"
92-
}
92+
},
93+
"sideEffects": [
94+
"lib-esnext/start-pnpm.js",
95+
"lib-esnext/start.js",
96+
"lib-esnext/startx.js",
97+
"lib-esnext/utilities/SetRushLibPath.js"
98+
]
9399
}

libraries/rush-lib/src/api/EnvironmentConfiguration.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,12 @@ export enum EnvironmentVariableNames {
153153
*/
154154
RUSH_TAR_BINARY_PATH = 'RUSH_TAR_BINARY_PATH',
155155

156+
/**
157+
* Explicitly specifies the path for the version of `\@microsoft/rush-lib` being executed.
158+
* Will be set upon loading Rush.
159+
*/
160+
RUSH_LIB_PATH = 'RUSH_LIB_PATH',
161+
156162
/**
157163
* When Rush executes shell scripts, it sometimes changes the working directory to be a project folder or
158164
* the repository root folder. The original working directory (where the Rush command was invoked) is assigned
@@ -441,6 +447,7 @@ export class EnvironmentConfiguration {
441447
break;
442448

443449
case EnvironmentVariableNames.RUSH_INVOKED_FOLDER:
450+
case EnvironmentVariableNames.RUSH_LIB_PATH:
444451
// Assigned by Rush itself
445452
break;
446453

libraries/rush-lib/src/api/Rush.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ import {
1010
PackageJsonLookup
1111
} from '@rushstack/node-core-library';
1212

13+
import '../utilities/SetRushLibPath';
14+
1315
import { RushCommandLineParser } from '../cli/RushCommandLineParser';
1416
import { RushStartupBanner } from '../cli/RushStartupBanner';
1517
import { RushXCommandLine } from '../cli/RushXCommandLine';
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import { PackageJsonLookup } from '@rushstack/node-core-library';
2+
3+
import { EnvironmentVariableNames } from '../api/EnvironmentConfiguration';
4+
5+
if (!process.env[EnvironmentVariableNames.RUSH_LIB_PATH]) {
6+
const rootDir: string | undefined = PackageJsonLookup.instance.tryGetPackageFolderFor(__dirname);
7+
if (rootDir) {
8+
// Route to the 'main' field of package.json
9+
const rushLibIndex: string = require.resolve(rootDir, { paths: [] });
10+
process.env[EnvironmentVariableNames.RUSH_LIB_PATH] = rushLibIndex;
11+
}
12+
}

libraries/rush-sdk/src/index.ts

Lines changed: 29 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ import {
1515
} from '@rushstack/node-core-library';
1616
import type { SpawnSyncReturns } from 'child_process';
1717

18-
const RUSH_LIB_NAME: string = '@microsoft/rush-lib';
18+
const RUSH_LIB_NAME: '@microsoft/rush-lib' = '@microsoft/rush-lib';
1919

2020
const verboseEnabled: boolean = typeof process !== 'undefined' && process.env.RUSH_SDK_DEBUG === '1';
2121
const terminal: Terminal = new Terminal(
@@ -28,6 +28,7 @@ type RushLibModuleType = Record<string, unknown>;
2828
declare const global: NodeJS.Global &
2929
typeof globalThis & {
3030
___rush___rushLibModule?: RushLibModuleType;
31+
___rush___rushLibModuleFromEnvironment?: RushLibModuleType;
3132
___rush___rushLibModuleFromInstallAndRunRush?: RushLibModuleType;
3233
};
3334

@@ -46,7 +47,9 @@ function _require<TResult>(moduleName: string): TResult {
4647
// SCENARIO 1: Rush's PluginManager has initialized "rush-sdk" with Rush's own instance of rush-lib.
4748
// The Rush host process will assign "global.___rush___rushLibModule" before loading the plugin.
4849
let rushLibModule: RushLibModuleType | undefined =
49-
global.___rush___rushLibModule || global.___rush___rushLibModuleFromInstallAndRunRush;
50+
global.___rush___rushLibModule ||
51+
global.___rush___rushLibModuleFromEnvironment ||
52+
global.___rush___rushLibModuleFromInstallAndRunRush;
5053
let errorMessage: string = '';
5154

5255
// SCENARIO 2: The project importing "rush-sdk" has installed its own instance of "rush-lib"
@@ -89,7 +92,30 @@ if (rushLibModule === undefined) {
8992
}
9093
}
9194

92-
// SCENARIO 3: A tool or script depends on "rush-sdk", and is meant to be used inside a monorepo folder.
95+
// SCENARIO 3: A tool or script has been invoked as a child process by an instance of "rush-lib" and can use the
96+
// version that invoked it. In this case, use process.env.RUSH_LIB_PATH to find "rush-lib".
97+
if (rushLibModule === undefined) {
98+
const rushLibVariable: 'RUSH_LIB_PATH' = 'RUSH_LIB_PATH';
99+
const rushLibPath: string | undefined = process.env[rushLibVariable];
100+
if (rushLibPath) {
101+
terminal.writeVerboseLine(
102+
`Try to load ${RUSH_LIB_NAME} from process.env.${rushLibVariable} from caller package`
103+
);
104+
try {
105+
rushLibModule = _require(rushLibPath);
106+
} catch (error) {
107+
terminal.writeVerboseLine(`Filed to load ${RUSH_LIB_NAME} via process.env.${rushLibVariable}`);
108+
}
109+
110+
if (rushLibModule !== undefined) {
111+
// to track which scenario is active and how it got initialized.
112+
global.___rush___rushLibModuleFromEnvironment = rushLibModule;
113+
terminal.writeVerboseLine(`Loaded ${RUSH_LIB_NAME} from process.env.${rushLibVariable}`);
114+
}
115+
}
116+
}
117+
118+
// SCENARIO 4: A standalone tool or script depends on "rush-sdk", and is meant to be used inside a monorepo folder.
93119
// In this case, we can use install-run-rush.js to obtain the appropriate rush-lib version for the monorepo.
94120
if (rushLibModule === undefined) {
95121
try {
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
// Jest Snapshot v1, https://goo.gl/fbAQLP
2+
3+
exports[`@rushstack/rush-sdk Should load via env when Rush has loaded (for child processes): stderr 1`] = `""`;
4+
5+
exports[`@rushstack/rush-sdk Should load via env when Rush has loaded (for child processes): stdout 1`] = `
6+
"Try to load @microsoft/rush-lib from process.env.RUSH_LIB_PATH from caller package
7+
Loaded @microsoft/rush-lib from process.env.RUSH_LIB_PATH
8+
[
9+
'_rushSdk_loadInternalModule',
10+
'ApprovedPackagesConfiguration',
11+
'ApprovedPackagesItem',
12+
'ApprovedPackagesPolicy',
13+
'BuildCacheConfiguration',
14+
'BumpType',
15+
'ChangeManager',
16+
'CommonVersionsConfiguration',
17+
'CredentialCache',
18+
'DependencyType',
19+
'EnvironmentConfiguration',
20+
'EnvironmentVariableNames',
21+
'Event',
22+
'EventHooks',
23+
'ExperimentsConfiguration',
24+
'FileSystemBuildCacheProvider',
25+
'IndividualVersionPolicy',
26+
'LockStepVersionPolicy',
27+
'LookupByPath',
28+
'NpmOptionsConfiguration',
29+
'Operation',
30+
'OperationStatus',
31+
'PackageJsonDependency',
32+
'PackageJsonEditor',
33+
'PackageManager',
34+
'PackageManagerOptionsConfigurationBase',
35+
'PhasedCommandHooks',
36+
'PnpmOptionsConfiguration',
37+
'ProjectChangeAnalyzer',
38+
'RepoStateFile',
39+
'Rush',
40+
'RushConfiguration',
41+
'RushConfigurationProject',
42+
'RushConstants',
43+
'RushLifecycleHooks',
44+
'RushSession',
45+
'RushUserConfiguration',
46+
'VersionPolicy',
47+
'VersionPolicyConfiguration',
48+
'VersionPolicyDefinitionName',
49+
'YarnOptionsConfiguration',
50+
'_LastInstallFlag',
51+
'_OperationMetadataManager',
52+
'_OperationStateFile',
53+
'_RushGlobalFolder',
54+
'_RushInternals'
55+
]"
56+
`;
57+
58+
exports[`@rushstack/rush-sdk Should load via global (for plugins): stderr 1`] = `""`;
59+
60+
exports[`@rushstack/rush-sdk Should load via global (for plugins): stdout 1`] = `"[ '_rushSdk_loadInternalModule', 'foo' ]"`;
61+
62+
exports[`@rushstack/rush-sdk Should load via install-run (for standalone tools): stderr 1`] = `""`;
63+
64+
exports[`@rushstack/rush-sdk Should load via install-run (for standalone tools): stdout 1`] = `
65+
"Trying to load @microsoft/rush-lib installed by install-run-rush
66+
Loaded @microsoft/rush-lib installed by install-run-rush
67+
[ '_rushSdk_loadInternalModule', 'foo' ]"
68+
`;
69+
70+
exports[`@rushstack/rush-sdk Should load via process.env.RUSH_LIB_PATH (for child processes): stderr 1`] = `""`;
71+
72+
exports[`@rushstack/rush-sdk Should load via process.env.RUSH_LIB_PATH (for child processes): stdout 1`] = `
73+
"Try to load @microsoft/rush-lib from process.env.RUSH_LIB_PATH from caller package
74+
Loaded @microsoft/rush-lib from process.env.RUSH_LIB_PATH
75+
[ '_rushSdk_loadInternalModule', 'foo' ]"
76+
`;

libraries/rush-sdk/src/test/script.test.ts

Lines changed: 72 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,70 @@ const mockRushLibPath: string = `${__dirname}/fixture/mock-rush-lib.js`;
88

99
const coreLibPath: string = require.resolve('@rushstack/node-core-library');
1010

11-
describe('used in script', () => {
12-
it('should work when used in script', () => {
11+
describe('@rushstack/rush-sdk', () => {
12+
it('Should load via global (for plugins)', () => {
13+
const result = Executable.spawnSync(
14+
'node',
15+
[
16+
'-e',
17+
`
18+
global.___rush___rushLibModule = { foo: 1 };
19+
console.log(Object.keys(require(${JSON.stringify(rushSdkPath)})));`
20+
],
21+
{
22+
currentWorkingDirectory: mockPackageFolder,
23+
environment: {
24+
...process.env,
25+
RUSH_SDK_DEBUG: '1'
26+
}
27+
}
28+
);
29+
expect(result.stderr.trim()).toMatchSnapshot('stderr');
30+
expect(result.stdout.trim()).toMatchSnapshot('stdout');
31+
expect(result.status).toBe(0);
32+
});
33+
34+
it('Should load via env when Rush has loaded (for child processes)', () => {
35+
const result = Executable.spawnSync(
36+
'node',
37+
[
38+
'-e',
39+
`
40+
require('@microsoft/rush-lib');
41+
console.log(Object.keys(require(${JSON.stringify(rushSdkPath)})));`
42+
],
43+
{
44+
currentWorkingDirectory: mockPackageFolder,
45+
environment: {
46+
...process.env,
47+
RUSH_SDK_DEBUG: '1'
48+
}
49+
}
50+
);
51+
expect(result.stderr.trim()).toMatchSnapshot('stderr');
52+
expect(result.stdout.trim()).toMatchSnapshot('stdout');
53+
expect(result.status).toBe(0);
54+
});
55+
56+
it('Should load via process.env.RUSH_LIB_PATH (for child processes)', () => {
57+
const result = Executable.spawnSync(
58+
'node',
59+
['-e', `console.log(Object.keys(require(${JSON.stringify(rushSdkPath)})));`],
60+
{
61+
currentWorkingDirectory: mockPackageFolder,
62+
environment: {
63+
...process.env,
64+
RUSH_SDK_DEBUG: '1',
65+
RUSH_LIB_PATH: mockRushLibPath
66+
}
67+
}
68+
);
69+
expect(result.stderr.trim()).toMatchSnapshot('stderr');
70+
expect(result.stdout.trim()).toMatchSnapshot('stdout');
71+
expect(result.status).toBe(0);
72+
});
73+
74+
it('Should load via install-run (for standalone tools)', () => {
1375
const result = Executable.spawnSync(
1476
'node',
1577
[
@@ -24,20 +86,19 @@ const mockResolveModule = (options) => {
2486
return originalResolveModule(options);
2587
}
2688
Import.resolveModule = mockResolveModule;
27-
console.log(require(${JSON.stringify(rushSdkPath)}));
89+
console.log(Object.keys(require(${JSON.stringify(rushSdkPath)})));
2890
`
2991
],
3092
{
31-
currentWorkingDirectory: mockPackageFolder
93+
currentWorkingDirectory: mockPackageFolder,
94+
environment: {
95+
...process.env,
96+
RUSH_SDK_DEBUG: '1'
97+
}
3298
}
3399
);
34-
expect(result.stderr.trim()).toMatchInlineSnapshot(`""`);
35-
expect(result.stdout.trim()).toMatchInlineSnapshot(`
36-
"{
37-
_rushSdk_loadInternalModule: [Function: _rushSdk_loadInternalModule],
38-
foo: [Getter]
39-
}"
40-
`);
100+
expect(result.stderr.trim()).toMatchSnapshot('stderr');
101+
expect(result.stdout.trim()).toMatchSnapshot('stdout');
41102
expect(result.status).toBe(0);
42103
});
43104
});

0 commit comments

Comments
 (0)
0