diff --git a/packages/angular/cli/commands/update-impl.ts b/packages/angular/cli/commands/update-impl.ts index af9a21e43c43..59e9ef5dc118 100644 --- a/packages/angular/cli/commands/update-impl.ts +++ b/packages/angular/cli/commands/update-impl.ts @@ -623,6 +623,35 @@ export class UpdateCommand extends Command { continue; } + if (node.package && /^@(?:angular|nguniversal)\//.test(node.package.name)) { + const { name, version } = node.package; + const toBeInstalledMajorVersion = +manifest.version.split('.')[0]; + const currentMajorVersion = +version.split('.')[0]; + + if (toBeInstalledMajorVersion - currentMajorVersion > 1) { + // Only allow updating a single version at a time. + if (currentMajorVersion < 6) { + // Before version 6, the major versions were not always sequential. + // Example @angular/core skipped version 3, @angular/cli skipped versions 2-5. + this.logger.error( + `Updating multiple major versions of '${name}' at once is not supported. Please migrate each major version individually.\n` + + `For more information about the update process, see https://update.angular.io/.`, + ); + } else { + const nextMajorVersionFromCurrent = currentMajorVersion + 1; + + this.logger.error( + `Updating multiple major versions of '${name}' at once is not supported. Please migrate each major version individually.\n` + + `Run 'ng update ${name}@${nextMajorVersionFromCurrent}' in your workspace directory ` + + `to update to latest '${nextMajorVersionFromCurrent}.x' version of '${name}'.\n\n` + + `For more information about the update process, see https://update.angular.io/?v=${currentMajorVersion}.0-${nextMajorVersionFromCurrent}.0`, + ); + } + + return 1; + } + } + packagesToUpdate.push(requestIdentifier.toString()); } diff --git a/tests/legacy-cli/e2e/tests/update/update-multiple-versions.ts b/tests/legacy-cli/e2e/tests/update/update-multiple-versions.ts new file mode 100644 index 000000000000..9d59214e57a0 --- /dev/null +++ b/tests/legacy-cli/e2e/tests/update/update-multiple-versions.ts @@ -0,0 +1,35 @@ +import { createProjectFromAsset } from '../../utils/assets'; +import { installWorkspacePackages, setRegistry } from '../../utils/packages'; +import { ng } from '../../utils/process'; +import { isPrereleaseCli } from '../../utils/project'; +import { expectToFail } from '../../utils/utils'; + +export default async function () { + try { + await createProjectFromAsset('8.0-project', true, true); + await setRegistry(false); + await installWorkspacePackages(); + + await setRegistry(true); + const extraArgs = ['--force']; + if (isPrereleaseCli()) { + extraArgs.push('--next'); + } + + const { message } = await expectToFail(() => + ng('update', '@angular/cli', '--force', ...extraArgs), + ); + if ( + !message.includes( + `Updating multiple major versions of '@angular/cli' at once is not supported`, + ) + ) { + console.error(message); + throw new Error( + `Expected error message to include "Updating multiple major versions of '@angular/cli' at once is not supported" but didn't.`, + ); + } + } finally { + await setRegistry(true); + } +}