@@ -298,47 +298,81 @@ export class InstallHelpers {
298298 node : process . versions . node
299299 } ) ;
300300
301+ if (
302+ ( await packageManagerMarker . isValidAsync ( ) ) &&
303+ ! InstallHelpers . _doesPackageManagerInstallLockFileExist ( rushUserFolder , packageManagerAndVersion )
304+ ) {
305+ logIfConsoleOutputIsNotRestricted (
306+ `Found ${ packageManager } version ${ packageManagerVersion } in ${ packageManagerToolFolder } `
307+ ) ;
308+ InstallHelpers . _ensureLocalPackageManagerSymlink (
309+ rushConfiguration ,
310+ packageManager ,
311+ packageManagerToolFolder ,
312+ logIfConsoleOutputIsNotRestricted
313+ ) ;
314+ return ;
315+ }
316+
301317 logIfConsoleOutputIsNotRestricted ( `Trying to acquire lock for ${ packageManagerAndVersion } ` ) ;
302318
303319 const lock : LockFile = await LockFile . acquireAsync ( rushUserFolder , packageManagerAndVersion ) ;
304320
305321 logIfConsoleOutputIsNotRestricted ( `Acquired lock for ${ packageManagerAndVersion } ` ) ;
306322
307- if ( ! ( await packageManagerMarker . isValidAsync ( ) ) || lock . dirtyWhenAcquired ) {
308- logIfConsoleOutputIsNotRestricted (
309- Colorize . bold ( `Installing ${ packageManager } version ${ packageManagerVersion } \n` )
310- ) ;
323+ try {
324+ if ( ! ( await packageManagerMarker . isValidAsync ( ) ) || lock . dirtyWhenAcquired ) {
325+ logIfConsoleOutputIsNotRestricted (
326+ Colorize . bold ( `Installing ${ packageManager } version ${ packageManagerVersion } \n` )
327+ ) ;
328+
329+ // note that this will remove the last-install flag from the directory
330+ await Utilities . installPackageInDirectoryAsync ( {
331+ directory : packageManagerToolFolder ,
332+ packageName : packageManager ,
333+ version : rushConfiguration . packageManagerToolVersion ,
334+ tempPackageTitle : `${ packageManager } -local-install` ,
335+ maxInstallAttempts : maxInstallAttempts ,
336+ // This is using a local configuration to install a package in a shared global location.
337+ // Generally that's a bad practice, but in this case if we can successfully install
338+ // the package at all, we can reasonably assume it's good for all the repositories.
339+ // In particular, we'll assume that two different NPM registries cannot have two
340+ // different implementations of the same version of the same package.
341+ // This was needed for: https://github.com/microsoft/rushstack/issues/691
342+ commonRushConfigFolder : rushConfiguration . commonRushConfigFolder ,
343+ // Only filter npm-incompatible properties when the repo uses pnpm or yarn.
344+ // If the repo uses npm, the .npmrc is already configured for npm, so don't filter.
345+ filterNpmIncompatibleProperties : rushConfiguration . packageManager !== 'npm'
346+ } ) ;
347+
348+ logIfConsoleOutputIsNotRestricted (
349+ `Successfully installed ${ packageManager } version ${ packageManagerVersion } `
350+ ) ;
351+ } else {
352+ logIfConsoleOutputIsNotRestricted (
353+ `Found ${ packageManager } version ${ packageManagerVersion } in ${ packageManagerToolFolder } `
354+ ) ;
355+ }
311356
312- // note that this will remove the last-install flag from the directory
313- await Utilities . installPackageInDirectoryAsync ( {
314- directory : packageManagerToolFolder ,
315- packageName : packageManager ,
316- version : rushConfiguration . packageManagerToolVersion ,
317- tempPackageTitle : `${ packageManager } -local-install` ,
318- maxInstallAttempts : maxInstallAttempts ,
319- // This is using a local configuration to install a package in a shared global location.
320- // Generally that's a bad practice, but in this case if we can successfully install
321- // the package at all, we can reasonably assume it's good for all the repositories.
322- // In particular, we'll assume that two different NPM registries cannot have two
323- // different implementations of the same version of the same package.
324- // This was needed for: https://github.com/microsoft/rushstack/issues/691
325- commonRushConfigFolder : rushConfiguration . commonRushConfigFolder ,
326- // Only filter npm-incompatible properties when the repo uses pnpm or yarn.
327- // If the repo uses npm, the .npmrc is already configured for npm, so don't filter.
328- filterNpmIncompatibleProperties : rushConfiguration . packageManager !== 'npm'
329- } ) ;
357+ await packageManagerMarker . createAsync ( ) ;
330358
331- logIfConsoleOutputIsNotRestricted (
332- `Successfully installed ${ packageManager } version ${ packageManagerVersion } `
333- ) ;
334- } else {
335- logIfConsoleOutputIsNotRestricted (
336- `Found ${ packageManager } version ${ packageManagerVersion } in ${ packageManagerToolFolder } `
359+ InstallHelpers . _ensureLocalPackageManagerSymlink (
360+ rushConfiguration ,
361+ packageManager ,
362+ packageManagerToolFolder ,
363+ logIfConsoleOutputIsNotRestricted
337364 ) ;
365+ } finally {
366+ lock . release ( ) ;
338367 }
368+ }
339369
340- await packageManagerMarker . createAsync ( ) ;
341-
370+ private static _ensureLocalPackageManagerSymlink (
371+ rushConfiguration : RushConfiguration ,
372+ packageManager : PackageManagerName ,
373+ packageManagerToolFolder : string ,
374+ logIfConsoleOutputIsNotRestricted : ( message ?: string ) => void
375+ ) : void {
342376 // Example: "C:\MyRepo\common\temp"
343377 FileSystem . ensureFolder ( rushConfiguration . commonTempFolder ) ;
344378
@@ -365,8 +399,23 @@ export class InstallHelpers {
365399 linkTargetPath : packageManagerToolFolder ,
366400 newLinkPath : localPackageManagerToolFolder
367401 } ) ;
402+ }
403+
404+ private static _doesPackageManagerInstallLockFileExist (
405+ rushUserFolder : string ,
406+ packageManagerAndVersion : string
407+ ) : boolean {
408+ for ( const itemName of FileSystem . readFolderItemNames ( rushUserFolder ) ) {
409+ if ( itemName === `${ packageManagerAndVersion } .lock` ) {
410+ return true ;
411+ }
412+
413+ if ( itemName . startsWith ( `${ packageManagerAndVersion } #` ) && itemName . endsWith ( '.lock' ) ) {
414+ return true ;
415+ }
416+ }
368417
369- lock . release ( ) ;
418+ return false ;
370419 }
371420
372421 // Helper for getPackageManagerEnvironment
0 commit comments