Skip to content

Commit 23124f5

Browse files
authored
Merge pull request microsoft#2802 from elliot-nelson/rebuild
[rush] Rebuild ignores existing build cache entries
2 parents b79574c + 3874dd2 commit 23124f5

File tree

6 files changed

+154
-130
lines changed

6 files changed

+154
-130
lines changed

apps/rush-lib/src/cli/scriptActions/BulkScriptAction.ts

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,6 @@ export class BulkScriptAction extends BaseScriptAction {
7878
private _verboseParameter!: CommandLineFlagParameter;
7979
private _parallelismParameter: CommandLineStringParameter | undefined;
8080
private _ignoreHooksParameter!: CommandLineFlagParameter;
81-
private _disableBuildCacheFlag: CommandLineFlagParameter | undefined;
8281

8382
public constructor(options: IBulkScriptActionOptions) {
8483
super(options);
@@ -126,7 +125,7 @@ export class BulkScriptAction extends BaseScriptAction {
126125

127126
const terminal: Terminal = new Terminal(new ConsoleTerminalProvider());
128127
let buildCacheConfiguration: BuildCacheConfiguration | undefined;
129-
if (!this._disableBuildCacheFlag?.value && !this._disableBuildCache) {
128+
if (!this._disableBuildCache) {
130129
buildCacheConfiguration = await BuildCacheConfiguration.tryLoadAsync(terminal, this.rushConfiguration);
131130
}
132131

@@ -300,11 +299,6 @@ export class BulkScriptAction extends BaseScriptAction {
300299
description: `Skips execution of the "eventHooks" scripts defined in rush.json. Make sure you know what you are skipping.`
301300
});
302301

303-
this._disableBuildCacheFlag = this.defineFlagParameter({
304-
parameterLongName: '--disable-build-cache',
305-
description: '(EXPERIMENTAL) Disables the build cache for this command invocation.'
306-
});
307-
308302
this.defineScriptParameters();
309303
}
310304

apps/rush-lib/src/cli/test/__snapshots__/CommandLineHelp.test.ts.snap

Lines changed: 3 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,7 @@ exports[`CommandLineHelp prints the help for each action: build 1`] = `
119119
[-o PROJECT] [-i PROJECT] [-I PROJECT]
120120
[--to-version-policy VERSION_POLICY_NAME]
121121
[--from-version-policy VERSION_POLICY_NAME] [-v] [-c]
122-
[--ignore-hooks] [--disable-build-cache] [-s] [-m]
122+
[--ignore-hooks] [-s] [-m]
123123
124124
125125
This command is similar to \\"rush rebuild\\", except that \\"rush build\\" performs
@@ -236,9 +236,6 @@ Optional arguments:
236236
--ignore-hooks Skips execution of the \\"eventHooks\\" scripts defined
237237
in rush.json. Make sure you know what you are
238238
skipping.
239-
--disable-build-cache
240-
(EXPERIMENTAL) Disables the build cache for this
241-
command invocation.
242239
-s, --ship Perform a production build, including minification
243240
and localization steps
244241
-m, --minimal Perform a fast build, which disables certain tasks
@@ -363,7 +360,7 @@ exports[`CommandLineHelp prints the help for each action: import-strings 1`] = `
363360
[-f PROJECT] [-o PROJECT] [-i PROJECT] [-I PROJECT]
364361
[--to-version-policy VERSION_POLICY_NAME]
365362
[--from-version-policy VERSION_POLICY_NAME] [-v]
366-
[--ignore-hooks] [--disable-build-cache]
363+
[--ignore-hooks]
367364
[--locale {en-us,fr-fr,es-es,zh-cn}]
368365
369366
@@ -463,9 +460,6 @@ Optional arguments:
463460
--ignore-hooks Skips execution of the \\"eventHooks\\" scripts defined
464461
in rush.json. Make sure you know what you are
465462
skipping.
466-
--disable-build-cache
467-
(EXPERIMENTAL) Disables the build cache for this
468-
command invocation.
469463
--locale {en-us,fr-fr,es-es,zh-cn}
470464
Selects a single instead of the default locale
471465
(en-us) for non-ship builds or all locales for ship
@@ -817,7 +811,7 @@ exports[`CommandLineHelp prints the help for each action: rebuild 1`] = `
817811
[-o PROJECT] [-i PROJECT] [-I PROJECT]
818812
[--to-version-policy VERSION_POLICY_NAME]
819813
[--from-version-policy VERSION_POLICY_NAME] [-v]
820-
[--ignore-hooks] [--disable-build-cache] [-s] [-m]
814+
[--ignore-hooks] [-s] [-m]
821815
822816
823817
This command assumes that the package.json file for each project contains a
@@ -922,9 +916,6 @@ Optional arguments:
922916
--ignore-hooks Skips execution of the \\"eventHooks\\" scripts defined
923917
in rush.json. Make sure you know what you are
924918
skipping.
925-
--disable-build-cache
926-
(EXPERIMENTAL) Disables the build cache for this
927-
command invocation.
928919
-s, --ship Perform a production build, including minification
929920
and localization steps
930921
-m, --minimal Perform a fast build, which disables certain tasks

apps/rush-lib/src/logic/buildCache/test/__snapshots__/AzureStorageBuildCacheProvider.test.ts.snap

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,4 @@ Array [
1414
]
1515
`;
1616

17-
exports[`AzureStorageBuildCacheProvider Uses a correct list of Azure authority hosts 1`] = `"The specified Azure Environment (\\"INCORRECT_AZURE_ENVIRONMENT\\") is invalid. If it is specified, it must be one of: AzureChina, AzureGermany, AzureGovernment, AzurePublicCloud"`;
18-
1917
exports[`AzureStorageBuildCacheProvider uses a correct list of Azure authority hosts 1`] = `"The specified Azure Environment (\\"INCORRECT_AZURE_ENVIRONMENT\\") is invalid. If it is specified, it must be one of: AzureChina, AzureGermany, AzureGovernment, AzurePublicCloud"`;

apps/rush-lib/src/logic/taskRunner/ProjectBuilder.ts

Lines changed: 128 additions & 109 deletions
Original file line numberDiff line numberDiff line change
@@ -10,14 +10,16 @@ import {
1010
JsonObject,
1111
NewlineKind,
1212
InternalError,
13-
Terminal
13+
Terminal,
14+
ColorValue
1415
} from '@rushstack/node-core-library';
1516
import {
1617
TerminalChunkKind,
1718
TextRewriterTransform,
1819
StderrLineTransform,
1920
SplitterTransform,
20-
DiscardStdoutTransform
21+
DiscardStdoutTransform,
22+
PrintUtilities
2123
} from '@rushstack/terminal';
2224
import { CollatedTerminal } from '@rushstack/stream-collator';
2325

@@ -225,138 +227,155 @@ export class ProjectBuilder extends BaseBuilder {
225227
files,
226228
arguments: this._commandToRun
227229
};
228-
} else {
229-
terminal.writeLine(
230-
'Unable to calculate incremental build state. Instead running full rebuild. Ensure Git is present.'
231-
);
230+
} else if (this.isIncrementalBuildAllowed) {
231+
// To test this code path:
232+
// Remove the `.git` folder then run "rush build --verbose"
233+
terminal.writeLine({
234+
text: PrintUtilities.wrapWords(
235+
'This workspace does not appear to be tracked by Git. ' +
236+
'Rush will proceed without incremental build, caching, and change detection.'
237+
),
238+
foregroundColor: ColorValue.Cyan
239+
});
232240
}
233241
} catch (error) {
234-
terminal.writeLine(
235-
'Error calculating incremental build state. Instead running full rebuild. ' + error.toString()
236-
);
242+
// To test this code path:
243+
// Delete a project's ".rush/temp/shrinkwrap-deps.json" then run "rush build --verbose"
244+
terminal.writeLine('Unable to calculate incremental build state: ' + error.toString());
245+
terminal.writeLine({
246+
text: 'Rush will proceed without incremental build, caching, and change detection.',
247+
foregroundColor: ColorValue.Cyan
248+
});
237249
}
238250

239-
const isPackageUnchanged: boolean = !!(
240-
lastProjectBuildDeps &&
241-
projectBuildDeps &&
242-
projectBuildDeps.arguments === lastProjectBuildDeps.arguments &&
243-
_areShallowEqual(projectBuildDeps.files, lastProjectBuildDeps.files)
244-
);
251+
// If the current command is allowed to do incremental builds, attempt to retrieve
252+
// the project from the build cache or skip building, if appropriate.
253+
if (this.isIncrementalBuildAllowed) {
254+
const projectBuildCache: ProjectBuildCache | undefined = await this._getProjectBuildCacheAsync(
255+
terminal,
256+
trackedFiles,
257+
context.repoCommandLineConfiguration
258+
);
259+
const restoreFromCacheSuccess: boolean | undefined =
260+
await projectBuildCache?.tryRestoreFromCacheAsync(terminal);
245261

246-
const projectBuildCache: ProjectBuildCache | undefined = await this._getProjectBuildCacheAsync(
247-
terminal,
248-
trackedFiles,
249-
context.repoCommandLineConfiguration
250-
);
251-
const restoreFromCacheSuccess: boolean | undefined = await projectBuildCache?.tryRestoreFromCacheAsync(
252-
terminal
253-
);
262+
if (restoreFromCacheSuccess) {
263+
return TaskStatus.FromCache;
264+
}
254265

255-
if (restoreFromCacheSuccess) {
256-
return TaskStatus.FromCache;
257-
} else if (isPackageUnchanged && this.isIncrementalBuildAllowed) {
258-
return TaskStatus.Skipped;
259-
} else {
260-
// If the deps file exists, remove it before starting a build.
261-
FileSystem.deleteFile(currentDepsPath);
262-
263-
// TODO: Remove legacyDepsPath with the next major release of Rush
264-
const legacyDepsPath: string = path.join(this._rushProject.projectFolder, 'package-deps.json');
265-
// Delete the legacy package-deps.json
266-
FileSystem.deleteFile(legacyDepsPath);
267-
268-
if (!this._commandToRun) {
269-
// Write deps on success.
270-
if (projectBuildDeps) {
271-
JsonFile.save(projectBuildDeps, currentDepsPath, {
272-
ensureFolderExists: true
273-
});
274-
}
266+
const isPackageUnchanged: boolean = !!(
267+
lastProjectBuildDeps &&
268+
projectBuildDeps &&
269+
projectBuildDeps.arguments === lastProjectBuildDeps.arguments &&
270+
_areShallowEqual(projectBuildDeps.files, lastProjectBuildDeps.files)
271+
);
275272

276-
return TaskStatus.Success;
273+
if (isPackageUnchanged) {
274+
return TaskStatus.Skipped;
277275
}
276+
}
278277

279-
// Run the task
280-
terminal.writeLine('Invoking: ' + this._commandToRun);
278+
// If the deps file exists, remove it before starting a build.
279+
FileSystem.deleteFile(currentDepsPath);
281280

282-
const task: child_process.ChildProcess = Utilities.executeLifecycleCommandAsync(this._commandToRun, {
283-
rushConfiguration: this._rushConfiguration,
284-
workingDirectory: projectFolder,
285-
initCwd: this._rushConfiguration.commonTempFolder,
286-
handleOutput: true,
287-
environmentPathOptions: {
288-
includeProjectBin: true
289-
}
290-
});
281+
// TODO: Remove legacyDepsPath with the next major release of Rush
282+
const legacyDepsPath: string = path.join(this._rushProject.projectFolder, 'package-deps.json');
283+
// Delete the legacy package-deps.json
284+
FileSystem.deleteFile(legacyDepsPath);
291285

292-
// Hook into events, in order to get live streaming of build log
293-
if (task.stdout !== null) {
294-
task.stdout.on('data', (data: Buffer) => {
295-
const text: string = data.toString();
296-
collatedTerminal.writeChunk({ text, kind: TerminalChunkKind.Stdout });
286+
if (!this._commandToRun) {
287+
// Write deps on success.
288+
if (projectBuildDeps) {
289+
JsonFile.save(projectBuildDeps, currentDepsPath, {
290+
ensureFolderExists: true
297291
});
298292
}
299-
if (task.stderr !== null) {
300-
task.stderr.on('data', (data: Buffer) => {
301-
const text: string = data.toString();
302-
collatedTerminal.writeChunk({ text, kind: TerminalChunkKind.Stderr });
303-
hasWarningOrError = true;
304-
});
293+
294+
return TaskStatus.Success;
295+
}
296+
297+
// Run the task
298+
terminal.writeLine('Invoking: ' + this._commandToRun);
299+
300+
const task: child_process.ChildProcess = Utilities.executeLifecycleCommandAsync(this._commandToRun, {
301+
rushConfiguration: this._rushConfiguration,
302+
workingDirectory: projectFolder,
303+
initCwd: this._rushConfiguration.commonTempFolder,
304+
handleOutput: true,
305+
environmentPathOptions: {
306+
includeProjectBin: true
305307
}
308+
});
309+
310+
// Hook into events, in order to get live streaming of build log
311+
if (task.stdout !== null) {
312+
task.stdout.on('data', (data: Buffer) => {
313+
const text: string = data.toString();
314+
collatedTerminal.writeChunk({ text, kind: TerminalChunkKind.Stdout });
315+
});
316+
}
317+
if (task.stderr !== null) {
318+
task.stderr.on('data', (data: Buffer) => {
319+
const text: string = data.toString();
320+
collatedTerminal.writeChunk({ text, kind: TerminalChunkKind.Stderr });
321+
hasWarningOrError = true;
322+
});
323+
}
306324

307-
let status: TaskStatus = await new Promise(
308-
(resolve: (status: TaskStatus) => void, reject: (error: TaskError) => void) => {
309-
task.on('close', (code: number) => {
310-
try {
311-
if (code !== 0) {
312-
reject(new TaskError('error', `Returned error code: ${code}`));
313-
} else if (hasWarningOrError) {
314-
resolve(TaskStatus.SuccessWithWarning);
315-
} else {
316-
resolve(TaskStatus.Success);
317-
}
318-
} catch (error) {
319-
reject(error);
325+
let status: TaskStatus = await new Promise(
326+
(resolve: (status: TaskStatus) => void, reject: (error: TaskError) => void) => {
327+
task.on('close', (code: number) => {
328+
try {
329+
if (code !== 0) {
330+
reject(new TaskError('error', `Returned error code: ${code}`));
331+
} else if (hasWarningOrError) {
332+
resolve(TaskStatus.SuccessWithWarning);
333+
} else {
334+
resolve(TaskStatus.Success);
320335
}
321-
});
336+
} catch (error) {
337+
reject(error);
338+
}
339+
});
340+
}
341+
);
342+
343+
if (status === TaskStatus.Success && projectBuildDeps) {
344+
// Write deps on success.
345+
const writeProjectStatePromise: Promise<boolean> = JsonFile.saveAsync(
346+
projectBuildDeps,
347+
currentDepsPath,
348+
{
349+
ensureFolderExists: true
322350
}
323351
);
324352

325-
if (status === TaskStatus.Success && projectBuildDeps) {
326-
// Write deps on success.
327-
const writeProjectStatePromise: Promise<boolean> = JsonFile.saveAsync(
328-
projectBuildDeps,
329-
currentDepsPath,
330-
{
331-
ensureFolderExists: true
332-
}
333-
);
334-
335-
const setCacheEntryPromise: Promise<boolean | undefined> = this.tryWriteCacheEntryAsync(
336-
terminal,
337-
trackedFiles,
338-
context.repoCommandLineConfiguration
339-
);
353+
// If the command is successful and we can calculate project hash, we will write a
354+
// new cache entry even if incremental builds are not allowed.
355+
const setCacheEntryPromise: Promise<boolean | undefined> = this.tryWriteCacheEntryAsync(
356+
terminal,
357+
trackedFiles,
358+
context.repoCommandLineConfiguration
359+
);
340360

341-
const [, cacheWriteSuccess] = await Promise.all([writeProjectStatePromise, setCacheEntryPromise]);
361+
const [, cacheWriteSuccess] = await Promise.all([writeProjectStatePromise, setCacheEntryPromise]);
342362

343-
if (terminalProvider.hasErrors) {
344-
status = TaskStatus.Failure;
345-
} else if (cacheWriteSuccess === false) {
346-
status = TaskStatus.SuccessWithWarning;
347-
}
363+
if (terminalProvider.hasErrors) {
364+
status = TaskStatus.Failure;
365+
} else if (cacheWriteSuccess === false) {
366+
status = TaskStatus.SuccessWithWarning;
348367
}
368+
}
349369

350-
normalizeNewlineTransform.close();
370+
normalizeNewlineTransform.close();
351371

352-
// If the pipeline is wired up correctly, then closing normalizeNewlineTransform should
353-
// have closed projectLogWritable.
354-
if (projectLogWritable.isOpen) {
355-
throw new InternalError('The output file handle was not closed');
356-
}
357-
358-
return status;
372+
// If the pipeline is wired up correctly, then closing normalizeNewlineTransform should
373+
// have closed projectLogWritable.
374+
if (projectLogWritable.isOpen) {
375+
throw new InternalError('The output file handle was not closed');
359376
}
377+
378+
return status;
360379
} finally {
361380
projectLogWritable.close();
362381
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
{
2+
"changes": [
3+
{
4+
"packageName": "@microsoft/rush",
5+
"comment": "When the experimental build cache is enabled, \"rush rebuild\" now forces cached projects to be rebuilt (GitHub #2802)",
6+
"type": "patch"
7+
}
8+
],
9+
"packageName": "@microsoft/rush",
10+
"email": "elliot.nelson@users.noreply.github.com"
11+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
{
2+
"changes": [
3+
{
4+
"packageName": "@microsoft/rush",
5+
"comment": "(Breaking change) Remove the experimental \"--disable-build-cache\" command line parameter.",
6+
"type": "minor"
7+
}
8+
],
9+
"packageName": "@microsoft/rush",
10+
"email": "elliot-nelson@users.noreply.github.com"
11+
}

0 commit comments

Comments
 (0)