Skip to content

Commit 995b335

Browse files
authored
fix: Handle uncaught exception in create-amplify (#2828)
* fix: Handle uncaught exception in create-amplify * update based on PR comments
1 parent ce18819 commit 995b335

File tree

10 files changed

+95
-50
lines changed

10 files changed

+95
-50
lines changed

.changeset/eighty-spoons-tickle.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
---
2+
'create-amplify': patch
3+
'@aws-amplify/cli-core': minor
4+
'@aws-amplify/backend-cli': patch
5+
---
6+
7+
Handle uncaught exception in create-amplify and refactor the error handler to cli-core

packages/cli-core/API.md

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,9 @@
55
```ts
66

77
import { AmplifyIOHost } from '@aws-amplify/plugin-types';
8+
import { Argv } from 'yargs';
89
import { PackageManagerController } from '@aws-amplify/plugin-types';
10+
import { UsageDataEmitter } from '@aws-amplify/platform-core';
911
import { WriteStream } from 'node:tty';
1012
import z from 'zod';
1113

@@ -33,12 +35,18 @@ export class AmplifyPrompter {
3335
}) => Promise<boolean>;
3436
}
3537

38+
// @public
39+
export const attachUnhandledExceptionListeners: (usageDataEmitter?: UsageDataEmitter) => void;
40+
3641
// @public (undocumented)
3742
export type ColorName = (typeof colorNames)[number];
3843

3944
// @public (undocumented)
4045
export const colorNames: readonly ["Green", "Yellow", "Blue", "Magenta", "Cyan", "Red"];
4146

47+
// @public
48+
export const extractSubCommands: (yargs: Argv) => string | undefined;
49+
4250
// @public
4351
export class Format {
4452
constructor(packageManagerRunnerName?: string);
@@ -75,6 +83,9 @@ export class Format {
7583
// @public (undocumented)
7684
export const format: Format;
7785

86+
// @public
87+
export const generateCommandFailureHandler: (parser?: Argv, usageDataEmitter?: UsageDataEmitter) => ((message: string, error: Error) => Promise<void>);
88+
7889
// @public (undocumented)
7990
export enum LogLevel {
8091
// (undocumented)
@@ -185,7 +196,7 @@ export const noticeSchema: z.ZodObject<{
185196
errorMessage: string;
186197
})[];
187198
link?: string | undefined;
188-
frequency?: "once" | "command" | "deployment" | "daily" | undefined;
199+
frequency?: "command" | "once" | "deployment" | "daily" | undefined;
189200
validFrom?: number | undefined;
190201
validTo?: number | undefined;
191202
}, {
@@ -213,7 +224,7 @@ export const noticeSchema: z.ZodObject<{
213224
errorMessage: string;
214225
})[];
215226
link?: string | undefined;
216-
frequency?: "once" | "command" | "deployment" | "daily" | undefined;
227+
frequency?: "command" | "once" | "deployment" | "daily" | undefined;
217228
validFrom?: number | undefined;
218229
validTo?: number | undefined;
219230
}>;
@@ -314,7 +325,7 @@ export const noticesManifestSchema: z.ZodObject<{
314325
errorMessage: string;
315326
})[];
316327
link?: string | undefined;
317-
frequency?: "once" | "command" | "deployment" | "daily" | undefined;
328+
frequency?: "command" | "once" | "deployment" | "daily" | undefined;
318329
validFrom?: number | undefined;
319330
validTo?: number | undefined;
320331
}, {
@@ -342,7 +353,7 @@ export const noticesManifestSchema: z.ZodObject<{
342353
errorMessage: string;
343354
})[];
344355
link?: string | undefined;
345-
frequency?: "once" | "command" | "deployment" | "daily" | undefined;
356+
frequency?: "command" | "once" | "deployment" | "daily" | undefined;
346357
validFrom?: number | undefined;
347358
validTo?: number | undefined;
348359
}>, "many">;
@@ -372,7 +383,7 @@ export const noticesManifestSchema: z.ZodObject<{
372383
errorMessage: string;
373384
})[];
374385
link?: string | undefined;
375-
frequency?: "once" | "command" | "deployment" | "daily" | undefined;
386+
frequency?: "command" | "once" | "deployment" | "daily" | undefined;
376387
validFrom?: number | undefined;
377388
validTo?: number | undefined;
378389
}[];
@@ -402,7 +413,7 @@ export const noticesManifestSchema: z.ZodObject<{
402413
errorMessage: string;
403414
})[];
404415
link?: string | undefined;
405-
frequency?: "once" | "command" | "deployment" | "daily" | undefined;
416+
frequency?: "command" | "once" | "deployment" | "daily" | undefined;
406417
validFrom?: number | undefined;
407418
validTo?: number | undefined;
408419
}[];

packages/cli-core/package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
"strip-ansi": "^7.1.0",
3030
"wrap-ansi": "^9.0.0",
3131
"semver": "^7.6.3",
32-
"zod": "3.25.17"
32+
"zod": "3.25.17",
33+
"yargs": "^17.7.2"
3334
}
3435
}

packages/cli/src/error_handler.test.ts renamed to packages/cli-core/src/error_handler.test.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,9 @@ import {
44
generateCommandFailureHandler,
55
} from './error_handler.js';
66
import { Argv } from 'yargs';
7-
import { LogLevel, format, printer } from '@aws-amplify/cli-core';
7+
import { format } from './format/format.js';
8+
import { LogLevel } from './printer/printer.js';
9+
import { printer } from './printer.js';
810
import assert from 'node:assert';
911
import { AmplifyUserError, UsageDataEmitter } from '@aws-amplify/platform-core';
1012

packages/cli/src/error_handler.ts renamed to packages/cli-core/src/error_handler.ts

Lines changed: 28 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1-
import { LogLevel, format, printer } from '@aws-amplify/cli-core';
1+
import { format } from './format/format.js';
2+
import { LogLevel } from './printer/printer.js';
3+
import { printer } from './printer.js';
24
import { Argv } from 'yargs';
35
import { AmplifyError, UsageDataEmitter } from '@aws-amplify/platform-core';
46
import { extractSubCommands } from './extract_sub_commands.js';
@@ -17,7 +19,7 @@ type HandleErrorProps = {
1719
* Attaches process listeners to handle unhandled exceptions and rejections
1820
*/
1921
export const attachUnhandledExceptionListeners = (
20-
usageDataEmitter: UsageDataEmitter,
22+
usageDataEmitter?: UsageDataEmitter,
2123
): void => {
2224
if (hasAttachUnhandledExceptionListenersBeenCalled) {
2325
return;
@@ -54,7 +56,7 @@ export const attachUnhandledExceptionListeners = (
5456
* This prevents our top-level error handler from being invoked after the yargs error handler has already been invoked
5557
*/
5658
export const generateCommandFailureHandler = (
57-
parser: Argv,
59+
parser?: Argv,
5860
usageDataEmitter?: UsageDataEmitter,
5961
): ((message: string, error: Error) => Promise<void>) => {
6062
/**
@@ -63,19 +65,29 @@ export const generateCommandFailureHandler = (
6365
* @param error error thrown by yargs handler
6466
*/
6567
const handleCommandFailure = async (message: string, error?: Error) => {
66-
const printHelp = () => {
67-
printer.printNewLine();
68-
parser.showHelp();
69-
printer.printNewLine();
70-
};
71-
await handleErrorSafe({
72-
command: extractSubCommands(parser),
73-
printMessagePreamble: printHelp,
74-
error,
75-
message,
76-
usageDataEmitter,
77-
});
78-
parser.exit(1, error || new Error(message));
68+
if (!parser) {
69+
await handleErrorSafe({
70+
error,
71+
message,
72+
});
73+
}
74+
75+
// for ampx commands
76+
if (parser) {
77+
const printHelp = () => {
78+
printer.printNewLine();
79+
parser.showHelp();
80+
printer.printNewLine();
81+
};
82+
await handleErrorSafe({
83+
command: extractSubCommands(parser),
84+
printMessagePreamble: printHelp,
85+
error,
86+
message,
87+
usageDataEmitter,
88+
});
89+
parser.exit(1, error || new Error(message));
90+
}
7991
};
8092
return handleCommandFailure;
8193
};

packages/cli-core/src/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,3 +6,5 @@ export * from './package-manager-controller/package_manager_controller_factory.j
66
export * from './loggers/amplify_io_events_bridge_singleton_factory.js';
77
export * from './notices/notices.js';
88
export * from './notices/notices_manifest_validator.js';
9+
export * from './error_handler.js';
10+
export * from './extract_sub_commands.js';

packages/cli/src/ampx.ts

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,6 @@ import {
77
import { AsyncLocalStorageContextManager } from '@opentelemetry/context-async-hooks';
88
import { BasicTracerProvider } from '@opentelemetry/sdk-trace-base';
99
import { createMainParser } from './main_parser_factory.js';
10-
import {
11-
attachUnhandledExceptionListeners,
12-
generateCommandFailureHandler,
13-
} from './error_handler.js';
14-
import { extractSubCommands } from './extract_sub_commands.js';
1510
import {
1611
AmplifyFault,
1712
PackageJsonReader,
@@ -25,7 +20,13 @@ import {
2520
import { fileURLToPath } from 'node:url';
2621
import { verifyCommandName } from './verify_command_name.js';
2722
import { hideBin } from 'yargs/helpers';
28-
import { PackageManagerControllerFactory, format } from '@aws-amplify/cli-core';
23+
import {
24+
PackageManagerControllerFactory,
25+
attachUnhandledExceptionListeners,
26+
extractSubCommands,
27+
format,
28+
generateCommandFailureHandler,
29+
} from '@aws-amplify/cli-core';
2930
import { NoticesRenderer } from './notices/notices_renderer.js';
3031
import { extractCommandInfo } from './extract_command_info.js';
3132
import { DeepPartial } from '@aws-amplify/plugin-types';

packages/cli/src/test-utils/command_runner.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
import { Argv } from 'yargs';
22
import { AsyncLocalStorage } from 'node:async_hooks';
33
import { UsageDataEmitter } from '@aws-amplify/platform-core';
4-
import { generateCommandFailureHandler } from '../error_handler.js';
5-
import { extractSubCommands } from '../extract_sub_commands.js';
4+
import {
5+
extractSubCommands,
6+
generateCommandFailureHandler,
7+
} from '@aws-amplify/cli-core';
68

79
class OutputInterceptor {
810
private output = '';

packages/create-amplify/src/create_amplify.ts

Lines changed: 25 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -8,37 +8,44 @@
88
*/
99

1010
import {
11-
LogLevel,
1211
PackageManagerControllerFactory,
12+
attachUnhandledExceptionListeners,
1313
format,
14-
printer,
14+
generateCommandFailureHandler,
1515
} from '@aws-amplify/cli-core';
1616
import { ProjectRootValidator } from './project_root_validator.js';
1717
import { AmplifyProjectCreator } from './amplify_project_creator.js';
1818
import { getProjectRoot } from './get_project_root.js';
1919
import { GitIgnoreInitializer } from './gitignore_initializer.js';
2020
import { InitialProjectFileGenerator } from './initial_project_file_generator.js';
2121

22-
const projectRoot = await getProjectRoot();
22+
attachUnhandledExceptionListeners();
23+
const errorHandler = generateCommandFailureHandler();
2324

24-
const packageManagerControllerFactory = new PackageManagerControllerFactory(
25-
projectRoot,
26-
);
25+
try {
26+
const projectRoot = await getProjectRoot();
2727

28-
const packageManagerController =
29-
packageManagerControllerFactory.getPackageManagerController();
28+
const packageManagerControllerFactory = new PackageManagerControllerFactory(
29+
projectRoot,
30+
);
3031

31-
const amplifyProjectCreator = new AmplifyProjectCreator(
32-
projectRoot,
33-
packageManagerController,
34-
new ProjectRootValidator(projectRoot),
35-
new GitIgnoreInitializer(projectRoot),
36-
new InitialProjectFileGenerator(projectRoot, packageManagerController),
37-
);
32+
const packageManagerController =
33+
packageManagerControllerFactory.getPackageManagerController();
34+
35+
const amplifyProjectCreator = new AmplifyProjectCreator(
36+
projectRoot,
37+
packageManagerController,
38+
new ProjectRootValidator(projectRoot),
39+
new GitIgnoreInitializer(projectRoot),
40+
new InitialProjectFileGenerator(projectRoot, packageManagerController),
41+
);
3842

39-
try {
4043
await amplifyProjectCreator.create();
4144
} catch (err) {
42-
printer.log(format.error(err), LogLevel.ERROR);
43-
process.exitCode = 1;
45+
if (err instanceof Error) {
46+
await errorHandler(format.error(err), err);
47+
} else {
48+
// eslint-disable-next-line @aws-amplify/amplify-backend-rules/prefer-amplify-errors
49+
await errorHandler(format.error(err), new Error(JSON.stringify(err)));
50+
}
4451
}

0 commit comments

Comments
 (0)