From 9188467ef7eba875eb787a5c41cb7a5299fddcfd Mon Sep 17 00:00:00 2001 From: Alan Agius Date: Mon, 17 Feb 2020 12:53:56 +0100 Subject: [PATCH] fix(@angular/cli): print `ng update` errors stack to log file MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When an error occurs during ng update we currently discard the stack trace which in some cases made it hard to identify the cause of the error. Now, we print the stack trace to a log file similarly to unhandled exceptions. Example of CMD output; ```cmd ** Executing migrations of package '@angular/core' ** > Static flag migration. Removes the `static` flag from dynamic queries. As of Angular 9, the "static" flag defaults to false and is no longer required for your view and content queries. Read more about this here: https://v9.angular.io/guide/migration-dynamic-flag × Migration failed: x See "C:\Users\alag\AppData\Local\Temp\ng-NgmC1G\angular-errors.log" for further details. ``` Example of log file contents: ```txt [error] Error: x at UpdateCommand.executeSchematic (C:\git\angular-cli\test\node_modules\@angular\cli\commands\update-impl.js:98:19) at UpdateCommand.executePackageMigrations (C:\git\angular-cli\test\node_modules\@angular\cli\commands\update-impl.js:167:39) at UpdateCommand.executeMigrations (C:\git\angular-cli\test\node_modules\@angular\cli\commands\update-impl.js:161:21) at UpdateCommand.run (C:\git\angular-cli\test\node_modules\@angular\cli\commands\update-impl.js:394:38) at async UpdateCommand.validateAndRun (C:\git\angular-cli\test\node_modules\@angular\cli\models\command.js:134:28) at async Object.runCommand (C:\git\angular-cli\test\node_modules\@angular\cli\models\command-runner.js:201:24) at async default_1 (C:\git\angular-cli\test\node_modules\@angular\cli\lib\cli\index.js:62:31) ``` --- packages/angular/cli/commands/update-impl.ts | 11 +++++--- packages/angular/cli/lib/cli/index.ts | 9 ++---- packages/angular/cli/utilities/log-file.ts | 29 ++++++++++++++++++++ 3 files changed, 38 insertions(+), 11 deletions(-) create mode 100644 packages/angular/cli/utilities/log-file.ts diff --git a/packages/angular/cli/commands/update-impl.ts b/packages/angular/cli/commands/update-impl.ts index 22c85d7ec047..8ac3aea073af 100644 --- a/packages/angular/cli/commands/update-impl.ts +++ b/packages/angular/cli/commands/update-impl.ts @@ -18,6 +18,7 @@ import { Command } from '../models/command'; import { Arguments } from '../models/interface'; import { runTempPackageBin } from '../tasks/install-package'; import { colors } from '../utilities/color'; +import { writeErrorToLogFile } from '../utilities/log-file'; import { getPackageManager } from '../utilities/package-manager'; import { PackageIdentifier, @@ -141,9 +142,13 @@ export class UpdateCommand extends Command { return { success: !error, files }; } catch (e) { if (e instanceof UnsuccessfulWorkflowExecution) { - this.logger.error('The update failed. See above.'); + this.logger.error(`${colors.symbols.cross} Migration failed. See above for further details.\n`); } else { - this.logger.fatal(e.message); + const logPath = writeErrorToLogFile(e); + this.logger.fatal( + `${colors.symbols.cross} Migration failed: ${e.message}\n` + + ` See "${logPath}" for further details.\n`, + ); } return { success: false, files }; @@ -223,8 +228,6 @@ export class UpdateCommand extends Command { const result = await this.executeSchematic(migration.collection.name, migration.name); if (!result.success) { - this.logger.error(`${colors.symbols.cross} Migration failed. See above for further details.\n`); - return false; } diff --git a/packages/angular/cli/lib/cli/index.ts b/packages/angular/cli/lib/cli/index.ts index e4ae7a88ceae..5aeed04c3215 100644 --- a/packages/angular/cli/lib/cli/index.ts +++ b/packages/angular/cli/lib/cli/index.ts @@ -6,11 +6,11 @@ * found in the LICENSE file at https://angular.io/license */ import { createConsoleLogger } from '@angular-devkit/core/node'; -import { normalize } from 'path'; import { format } from 'util'; import { runCommand } from '../../models/command-runner'; import { colors, supportsColor } from '../../utilities/color'; import { getWorkspaceRaw } from '../../utilities/config'; +import { writeErrorToLogFile } from '../../utilities/log-file'; import { getWorkspaceDetails } from '../../utilities/project'; const debugEnv = process.env['NG_DEBUG']; @@ -82,12 +82,7 @@ export default async function(options: { testing?: boolean; cliArgs: string[] }) } catch (err) { if (err instanceof Error) { try { - const fs = await import('fs'); - const os = await import('os'); - const tempDirectory = fs.mkdtempSync(fs.realpathSync(os.tmpdir()) + '/' + 'ng-'); - const logPath = normalize(tempDirectory + '/angular-errors.log'); - fs.appendFileSync(logPath, '[error] ' + (err.stack || err)); - + const logPath = writeErrorToLogFile(err); logger.fatal( `An unhandled exception occurred: ${err.message}\n` + `See "${logPath}" for further details.`, diff --git a/packages/angular/cli/utilities/log-file.ts b/packages/angular/cli/utilities/log-file.ts new file mode 100644 index 000000000000..4e5d33bb4571 --- /dev/null +++ b/packages/angular/cli/utilities/log-file.ts @@ -0,0 +1,29 @@ +/** + * @license + * Copyright Google Inc. All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + +import { appendFileSync, mkdtempSync, realpathSync } from 'fs'; +import { tmpdir } from 'os'; +import { normalize } from 'path'; + +let logPath: string | undefined; + +/** + * Writes an Error to a temporary log file. + * If this method is called multiple times from the same process the same log file will be used. + * @returns The path of the generated log file. + */ +export function writeErrorToLogFile(error: Error): string { + if (!logPath) { + const tempDirectory = mkdtempSync(realpathSync(tmpdir()) + '/ng-'); + logPath = normalize(tempDirectory + '/angular-errors.log'); + } + + appendFileSync(logPath, '[error] ' + (error.stack || error) + '\n\n'); + + return logPath; +}