8000 Merge pull request #3320 from iclanton/fix-no-deps-edge-case · psy-repos-typescript/rushstack@da9d490 · GitHub
[go: up one dir, main page]

Skip to content

Commit da9d490

Browse files
authored
Merge pull request microsoft#3320 from iclanton/fix-no-deps-edge-case
[rush] Fix an edge case where `rush update` fails in a repo with no dependencies.
2 parents 6ec531c + 43580c5 commit da9d490

File tree

3 files changed

+54
-10
lines changed

3 files changed

+54
-10
lines changed

apps/rush-lib/src/logic/base/BaseProjectShrinkwrapFile.ts

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
// See LICENSE in the project root for license information.
33

44
import * as path from 'path';
5-
import { FileSystem } from '@rushstack/node-core-library';
5+
import { FileSystem, JsonFile } from '@rushstack/node-core-library';
66

77
import { RushConfigurationProject } from '../../api/RushConfigurationProject';
88
import { RushConstants } from '../RushConstants';
@@ -26,6 +26,14 @@ export abstract class BaseProjectShrinkwrapFile {
2626
this._shrinkwrapFile = shrinkwrapFile;
2727
}
2828

29+
/**
30+
* Save an empty project shrinkwrap file. This is used in repos with no dependencies.
31+
*/
32+
public static async saveEmptyProjectShrinkwrapFileAsync(project: RushConfigurationProject): Promise<void> {
33+
const projectShrinkwrapFilePath: string = BaseProjectShrinkwrapFile.getFilePathForProject(project);
34+
await JsonFile.saveAsync({}, projectShrinkwrapFilePath, { ensureFolderExists: true });
35+
}
36+
2937
/**
3038
* Get the fully-qualified path to the <project>/.rush/temp/shrinkwrap-deps.json
3139
* for the specified project.

apps/rush-lib/src/logic/installManager/WorkspaceInstallManager.ts

Lines changed: 35 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import colors from 'colors/safe';
55
import * as os from 'os';
66
import * as path from 'path';
77
import * as semver from 'semver';
8-
import { FileSystem, FileConstants, AlreadyReportedError } from '@rushstack/node-core-library';
8+
import { FileSystem, FileConstants, AlreadyReportedError, Async } from '@rushstack/node-core-library';
99

1010
import { BaseInstallManager, IInstallManagerOptions } from '../base/BaseInstallManager';
1111
import { BaseShrinkwrapFile } from '../../logic/base/BaseShrinkwrapFile';
@@ -21,6 +21,7 @@ import { RepoStateFile } from '../RepoStateFile';
2121
import { LastLinkFlagFactory } from '../../api/LastLinkFlag';
2222
import { EnvironmentConfiguration } from '../../api/EnvironmentConfiguration';
2323
import { ShrinkwrapFileFactory } from '../ShrinkwrapFileFactory';
24+
import { BaseProjectShrinkwrapFile } from '../base/BaseProjectShrinkwrapFile';
2425

2526
/**
2627
* This class implements common logic between "rush install" and "rush update".
@@ -373,19 +374,44 @@ export class WorkspaceInstallManager extends BaseInstallManager {
373374
protected async postInstallAsync(): Promise<void> {
374375
// Grab the temp shrinkwrap, as this was the most recently completed install. It may also be
375376
// more up-to-date than the checked-in shrinkwrap since filtered installs are not written back.
376-
const tempShrinkwrapFile: BaseShrinkwrapFile = ShrinkwrapFileFactory.getShrinkwrapFile(
377+
// Note that if there are no projects, or if we're in PNPM workspace mode and there are no
378+
// projects with dependencies, a lockfile won't be generated.
379+
const tempShrinkwrapFile: BaseShrinkwrapFile | undefined = ShrinkwrapFileFactory.getShrinkwrapFile(
377380
this.rushConfiguration.packageManager,
378381
this.rushConfiguration.pnpmOptions,
379382
this.rushConfiguration.tempShrinkwrapFilename
380-
)!;
381-
382-
// Write or delete all project shrinkwraps related to the install
383-
await Promise.all(
384-
this.rushConfiguration.projects.map(async (project) => {
385-
await tempShrinkwrapFile.getProjectShrinkwrap(project)?.updateProjectShrinkwrapAsync();
386-
})
387383
);
388384

385+
if (tempShrinkwrapFile) {
386+
// Write or delete all project shrinkwraps related to the install
387+
await Async.forEachAsync(
388+
this.rushConfiguration.projects,
389+
async (project) => {
390+
await tempShrinkwrapFile.getProjectShrinkwrap(project)?.updateProjectShrinkwrapAsync();
391+
},
392+
{ concurrency: 10 }
393+
);
394+
} else if (
395+
this.rushConfiguration.packageManager === 'pnpm' &&
396+
this.rushConfiguration.pnpmOptions?.useWorkspaces
397+
) {
398+
// If we're in PNPM workspace mode and PNPM didn't create a shrinkwrap file,
399+
// there are no dependencies. Generate empty shrinkwrap files for all projects.
400+
await Async.forEachAsync(
401+
this.rushConfiguration.projects,
402+
async (project) => {
403+
await BaseProjectShrinkwrapFile.saveEmptyProjectShrinkwrapFileAsync(project);
404+
},
405+
{ concurrency: 10 }
406+
);
407+
} else {
408+
// This is an unexpected case
409+
throw new Error(
410+
'A shrinkwrap file does not exist after after successful installation. This probably indicates a ' +
411+
'bug in the package manager.'
412+
);
413+
}
414+
389415
// TODO: Remove when "rush link" and "rush unlink" are deprecated
390416
LastLinkFlagFactory.getCommonTempFlag(this.rushConfiguration).create();
391417
}
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": "Fix an edge case where `rush update` fails in a PNPM workspaces repo with no dependencies.",
6+
"type": "none"
7+
}
8+
],
9+
"packageName": "@microsoft/rush"
10+
}

0 commit comments

Comments
 (0)
0