Skip to content

Commit 96bda20

Browse files
committed
fix(ng-dev/release): update PATH after nvm install to affect subsequent commands
1 parent 5b99d6d commit 96bda20

1 file changed

Lines changed: 44 additions & 10 deletions

File tree

ng-dev/release/publish/external-commands.ts

Lines changed: 44 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,8 @@
66
* found in the LICENSE file at https://angular.io/license
77
*/
88

9-
import {existsSync} from 'fs';
10-
import {join} from 'path';
9+
import {existsSync, readFileSync} from 'fs';
10+
import {dirname, join} from 'path';
1111
import semver from 'semver';
1212
import {ChildProcess, SpawnResult, SpawnOptions} from '../../utils/child-process.js';
1313
import {Spinner} from '../../utils/spinner.js';
@@ -193,6 +193,10 @@ export abstract class ExternalCommands {
193193
/**
194194
* Invokes the `nvm install` command in order to install the correct Node.js version
195195
* as specified in a `.nvmrc` file, if present.
196+
*
197+
* We then run `nvm install` to install the correct version of node. We then run
198+
* `nvm which` to get the path to the node binary, and we update the PATH
199+
* environment variable to include this new node binary location.
196200
*/
197201
static async invokeNvmInstall(projectDir: string): Promise<void>;
198202
static async invokeNvmInstall(projectDir: string, quiet: boolean): Promise<void>;
@@ -202,20 +206,50 @@ export abstract class ExternalCommands {
202206
}
203207

204208
try {
209+
const nodeVersionFromNvmrc = readFileSync(join(projectDir, '.nvmrc'), 'utf8').trim();
210+
205211
// We must source nvm.sh so the shell recognizes the 'nvm' command since nvm is not a binary
206212
// but a shell function. The dot (.) built-in and && operator require shell: true here.
207-
await ChildProcess.spawn('. ~/.nvm/nvm.sh && nvm install', [], {
213+
// We redirect stdout of nvm install to stderr so that stdout only contains the result of nvm which.
214+
const {stdout} = await ChildProcess.spawn(
215+
'. ~/.nvm/nvm.sh && nvm install >&2 && nvm which',
216+
[],
217+
{
218+
cwd: projectDir,
219+
mode: 'on-error',
220+
shell: true,
221+
},
222+
);
223+
224+
const nodeBinPath = stdout.trim();
225+
if (nodeBinPath) {
226+
const nodeDir = dirname(nodeBinPath);
227+
const currentPath = process.env['PATH'] || '';
228+
const pathParts = currentPath.split(':');
229+
230+
// Only update if the requested node directory is not already the first entry in PATH.
231+
if (pathParts[0] !== nodeDir) {
232+
const filteredParts = pathParts.filter((p) => p !== nodeDir);
233+
process.env['PATH'] = [nodeDir, ...filteredParts].join(':');
234+
Log.debug(`Updated process.env.PATH to include ${nodeDir}`);
235+
}
236+
}
237+
238+
const {stdout: nodeVersionOutput} = await ChildProcess.spawn('node', ['--version'], {
239+
mode: 'silent',
208240
cwd: projectDir,
209-
mode: 'on-error',
210-
shell: true,
211241
});
242+
const detectedNodeVersion = nodeVersionOutput.trim();
243+
244+
if (!semver.satisfies(detectedNodeVersion, nodeVersionFromNvmrc)) {
245+
Log.error(` ✘ Node.js version mismatch after update.\n`);
246+
Log.error(` Expected version: ${nodeVersionFromNvmrc}`);
247+
Log.error(` Actual version: ${detectedNodeVersion}`);
248+
throw new FatalReleaseActionError();
249+
}
212250

213251
if (!quiet) {
214-
const {stdout: nodeVersion} = await ChildProcess.spawn('node', ['--version'], {
215-
mode: 'silent',
216-
cwd: projectDir,
217-
});
218-
Log.info(green(` ✓ Set node version to ${nodeVersion}.`));
252+
Log.info(green(` ✓ Set node version to ${detectedNodeVersion}.`));
219253
}
220254
} catch (e) {
221255
Log.error(e);

0 commit comments

Comments
 (0)