Skip to content

Commit

Permalink
fix(@angular/cli): logic to determine if the installed CLI is out of …
Browse files Browse the repository at this point in the history
…date

With this change we now check if the current CLI version is the latest published version. If it is not, we install a temporary version to run the ng update with.
  • Loading branch information
alan-agius4 authored and vikerman committed Oct 22, 2019
1 parent 7299d7a commit dfa1ab8
Show file tree
Hide file tree
Showing 13 changed files with 335 additions and 135 deletions.
2 changes: 1 addition & 1 deletion etc/api/angular_devkit/core/node/_golden-api.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ export interface ProcessOutput {
write(buffer: string | Buffer): boolean;
}

export declare function resolve(x: string, options: ResolveOptions): string;
export declare function resolve(packageName: string, options: ResolveOptions): string;

export interface ResolveOptions {
basedir: string;
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,7 @@
"@types/minimist": "^1.2.0",
"@types/node": "10.9.4",
"@types/request": "^2.47.1",
"@types/rimraf": "^2.0.2",
"@types/semver": "^6.0.0",
"@types/webpack": "^4.32.1",
"@types/webpack-dev-server": "^3.1.7",
Expand Down
8 changes: 7 additions & 1 deletion packages/angular/cli/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -38,11 +38,11 @@ ts_library(
"@npm//debug",
"@npm//@types/node",
"@npm//@types/inquirer",
"@npm//@types/rimraf",
"@npm//@types/semver",
"@npm//@types/universal-analytics",
"@npm//@types/uuid",
"@npm//ansi-colors",
"@npm//rxjs",
],
)

Expand All @@ -53,6 +53,7 @@ ts_library(
":add_schema",
":analytics_schema",
":build_schema",
":cli_schema",
":config_schema",
":deploy_schema",
":deprecated_schema",
Expand All @@ -72,6 +73,11 @@ ts_library(
],
)

ts_json_schema(
name = "cli_schema",
src = "lib/config/schema.json",
)

ts_json_schema(
name = "analytics_schema",
src = "commands/analytics.json",
Expand Down
4 changes: 2 additions & 2 deletions packages/angular/cli/commands/add-impl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import { intersects, prerelease, rcompare, satisfies, valid, validRange } from '
import { isPackageNameSafeForAnalytics } from '../models/analytics';
import { Arguments } from '../models/interface';
import { RunSchematicOptions, SchematicCommand } from '../models/schematic-command';
import npmInstall from '../tasks/npm-install';
import { installPackage } from '../tasks/install-package';
import { colors } from '../utilities/color';
import { getPackageManager } from '../utilities/package-manager';
import {
Expand Down Expand Up @@ -135,7 +135,7 @@ export class AddCommand extends SchematicCommand<AddCommandSchema> {
}
}

await npmInstall(packageIdentifier.raw, this.logger, this.packageManager, this.workspace.root);
installPackage(packageIdentifier.raw, this.logger, this.packageManager);

return this.executeSchematic(collectionName, options['--']);
}
Expand Down
49 changes: 45 additions & 4 deletions packages/angular/cli/commands/update-impl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,10 @@
import { execSync } from 'child_process';
import * as fs from 'fs';
import * as path from 'path';
import * as semver from 'semver';
import { Arguments, Option } from '../models/interface';
import { SchematicCommand } from '../models/schematic-command';
import { runTempPackageBin } from '../tasks/install-package';
import { getPackageManager } from '../utilities/package-manager';
import {
PackageIdentifier,
Expand All @@ -30,13 +32,29 @@ const oldConfigFileNames = ['.angular-cli.json', 'angular-cli.json'];

export class UpdateCommand extends SchematicCommand<UpdateCommandSchema> {
public readonly allowMissingWorkspace = true;
private readonly packageManager = getPackageManager(this.workspace.root);

async parseArguments(_schematicOptions: string[], _schema: Option[]): Promise<Arguments> {
return {};
}

// tslint:disable-next-line:no-big-function
async run(options: UpdateCommandSchema & Arguments) {
// Check if the current installed CLI version is older than the latest version.
if (await this.checkCLILatestVersion(options.verbose)) {
this.logger.warn(
'The installed Angular CLI version is older than the latest published version.\n' +
'Installing a temporary version to perform the update.',
);

return runTempPackageBin(
'@angular/cli@latest',
this.logger,
this.packageManager,
process.argv.slice(2),
);
}

const packages: PackageIdentifier[] = [];
for (const request of options['--'] || []) {
try {
Expand Down Expand Up @@ -99,8 +117,7 @@ export class UpdateCommand extends SchematicCommand<UpdateCommandSchema> {
}
}

const packageManager = getPackageManager(this.workspace.root);
this.logger.info(`Using package manager: '${packageManager}'`);
this.logger.info(`Using package manager: '${this.packageManager}'`);

// Special handling for Angular CLI 1.x migrations
if (
Expand Down Expand Up @@ -134,7 +151,7 @@ export class UpdateCommand extends SchematicCommand<UpdateCommandSchema> {
force: options.force || false,
next: options.next || false,
verbose: options.verbose || false,
packageManager,
packageManager: this.packageManager,
packages: options.all ? Object.keys(rootDependencies) : [],
},
});
Expand Down Expand Up @@ -351,7 +368,7 @@ export class UpdateCommand extends SchematicCommand<UpdateCommandSchema> {
additionalOptions: {
verbose: options.verbose || false,
force: options.force || false,
packageManager,
packageManager: this.packageManager,
packages: packagesToUpdate,
},
});
Expand Down Expand Up @@ -381,4 +398,28 @@ export class UpdateCommand extends SchematicCommand<UpdateCommandSchema> {

return true;
}

/**
* Checks if the current installed CLI version is older than the latest version.
* @returns `true` when the installed version is older.
*/
private async checkCLILatestVersion(verbose = false): Promise<boolean> {
const { version: installedCLIVersion } = require('../package.json');

const LatestCLIManifest = await fetchPackageMetadata(
'@angular/cli',
this.logger,
{
verbose,
usingYarn: this.packageManager === 'yarn',
},
);

const latest = LatestCLIManifest.tags['latest'];
if (!latest) {
return false;
}

return semver.lt(installedCLIVersion, latest.version);
}
}
8 changes: 7 additions & 1 deletion packages/angular/cli/lib/cli/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,15 @@ import { colors, supportsColor } from '../../utilities/color';
import { getWorkspaceRaw } from '../../utilities/config';
import { getWorkspaceDetails } from '../../utilities/project';

const debugEnv = process.env['NG_DEBUG'];
const isDebug =
debugEnv !== undefined &&
debugEnv !== '0' &&
debugEnv.toLowerCase() !== 'false';

// tslint:disable: no-console
export default async function(options: { testing?: boolean; cliArgs: string[] }) {
const logger = createConsoleLogger(false, process.stdout, process.stderr, {
const logger = createConsoleLogger(isDebug, process.stdout, process.stderr, {
info: s => (supportsColor ? s : colors.unstyle(s)),
debug: s => (supportsColor ? s : colors.unstyle(s)),
warn: s => (supportsColor ? colors.bold.yellow(s) : colors.unstyle(s)),
Expand Down
98 changes: 55 additions & 43 deletions packages/angular/cli/lib/init.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,17 +19,15 @@ import { isWarningEnabled } from '../utilities/config';

const packageJson = require('../package.json');

function _fromPackageJson(cwd?: string) {
cwd = cwd || process.cwd();

function _fromPackageJson(cwd = process.cwd()): SemVer | null {
do {
const packageJsonPath = path.join(cwd, 'node_modules/@angular/cli/package.json');
if (fs.existsSync(packageJsonPath)) {
const content = fs.readFileSync(packageJsonPath, 'utf-8');
if (content) {
const json = JSON.parse(content);
if (json['version']) {
return new SemVer(json['version']);
const { version } = JSON.parse(content);
if (version) {
return new SemVer(version);
}
}
}
Expand Down Expand Up @@ -78,50 +76,64 @@ if (process.env['NG_CLI_PROFILING']) {
}

let cli;
try {
const projectLocalCli = require.resolve('@angular/cli', { paths: [process.cwd()] });

// This was run from a global, check local version.
const globalVersion = new SemVer(packageJson['version']);
let localVersion;
let shouldWarn = false;
const disableVersionCheckEnv = process.env['NG_DISABLE_VERSION_CHECK'];
/**
* Disable CLI version mismatch checks and forces usage of the invoked CLI
* instead of invoking the local installed version.
*/
const disableVersionCheck =
disableVersionCheckEnv !== undefined &&
disableVersionCheckEnv !== '0' &&
disableVersionCheckEnv.toLowerCase() !== 'false';

if (disableVersionCheck) {
cli = require('./cli');
} else {
try {
localVersion = _fromPackageJson();
shouldWarn = localVersion != null && globalVersion.compare(localVersion) > 0;
} catch (e) {
// eslint-disable-next-line no-console
console.error(e);
shouldWarn = true;
}
const projectLocalCli = require.resolve('@angular/cli', { paths: [process.cwd()] });

if (shouldWarn && isWarningEnabled('versionMismatch')) {
const warning = colors.yellow(tags.stripIndents`
Your global Angular CLI version (${globalVersion}) is greater than your local
version (${localVersion}). The local Angular CLI version is used.
// This was run from a global, check local version.
const globalVersion = new SemVer(packageJson['version']);
let localVersion;
let shouldWarn = false;

To disable this warning use "ng config -g cli.warnings.versionMismatch false".
`);
// Don't show warning colorised on `ng completion`
if (process.argv[2] !== 'completion') {
try {
localVersion = _fromPackageJson();
shouldWarn = localVersion != null && globalVersion.compare(localVersion) > 0;
} catch (e) {
// eslint-disable-next-line no-console
console.error(warning);
} else {
// eslint-disable-next-line no-console
console.error(warning);
process.exit(1);
console.error(e);
shouldWarn = true;
}
}

// No error implies a projectLocalCli, which will load whatever
// version of ng-cli you have installed in a local package.json
cli = require(projectLocalCli);
} catch {
// If there is an error, resolve could not find the ng-cli
// library from a package.json. Instead, include it from a relative
// path to this script file (which is likely a globally installed
// npm package). Most common cause for hitting this is `ng new`
cli = require('./cli');
if (shouldWarn && isWarningEnabled('versionMismatch')) {
const warning = colors.yellow(tags.stripIndents`
Your global Angular CLI version (${globalVersion}) is greater than your local
version (${localVersion}). The local Angular CLI version is used.
To disable this warning use "ng config -g cli.warnings.versionMismatch false".
`);
// Don't show warning colorised on `ng completion`
if (process.argv[2] !== 'completion') {
// eslint-disable-next-line no-console
console.error(warning);
} else {
// eslint-disable-next-line no-console
console.error(warning);
process.exit(1);
}
}

// No error implies a projectLocalCli, which will load whatever
// version of ng-cli you have installed in a local package.json
cli = require(projectLocalCli);
} catch {
// If there is an error, resolve could not find the ng-cli
// library from a package.json. Instead, include it from a relative
// path to this script file (which is likely a globally installed
// npm package). Most common cause for hitting this is `ng new`
cli = require('./cli');
}
}

if ('default' in cli) {
Expand Down
1 change: 1 addition & 0 deletions packages/angular/cli/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
"open": "6.4.0",
"pacote": "9.5.5",
"read-package-tree": "5.3.1",
"rimraf": "3.0.0",
"semver": "6.3.0",
"symbol-observable": "1.2.0",
"universal-analytics": "^0.4.20",
Expand Down
Loading

0 comments on commit dfa1ab8

Please sign in to comment.