diff --git a/.changeset/failure-reason-stderr.md b/.changeset/failure-reason-stderr.md new file mode 100644 index 0000000000..8f75e64733 --- /dev/null +++ b/.changeset/failure-reason-stderr.md @@ -0,0 +1,5 @@ +--- +'eas-cli': major +--- + +Write the reason a command failed to stderr, next to the `command failed.` line, instead of to stdout. A program that reads the reason from stdout must read stderr instead. diff --git a/packages/eas-cli/src/commandUtils/EasCommand.ts b/packages/eas-cli/src/commandUtils/EasCommand.ts index d167880247..9359d377b7 100644 --- a/packages/eas-cli/src/commandUtils/EasCommand.ts +++ b/packages/eas-cli/src/commandUtils/EasCommand.ts @@ -262,7 +262,7 @@ export default abstract class EasCommand extends Command { const commandId = this.id ?? 'unknown'; let baseMessage = `${commandId} command failed.`; if (err instanceof EasCommandError) { - Log.error(err.message); + Log.errorToStderr(err.message); } else if (err instanceof CombinedError && err?.graphQLErrors) { const cleanGQLErrorsMessage = err?.graphQLErrors .map((graphQLError: GraphQLError) => { @@ -299,10 +299,10 @@ export default abstract class EasCommand extends Command { const cleanMessage = err.networkError ? `${cleanGQLErrorsMessage}\n${err.networkError.message}` : cleanGQLErrorsMessage; - Log.error(cleanMessage); + Log.errorToStderr(cleanMessage); baseMessage = BASE_GRAPHQL_ERROR_MESSAGE; } else { - Log.error(err.message); + Log.errorToStderr(err.message); } Log.debug(err); Sentry.withScope(scope => { diff --git a/packages/eas-cli/src/commandUtils/__tests__/EasCommand-test.ts b/packages/eas-cli/src/commandUtils/__tests__/EasCommand-test.ts index a3c74cd36d..bae6b4fdb4 100644 --- a/packages/eas-cli/src/commandUtils/__tests__/EasCommand-test.ts +++ b/packages/eas-cli/src/commandUtils/__tests__/EasCommand-test.ts @@ -199,7 +199,7 @@ describe('EasCommand', () => { it('logs the message', async () => { const TestEasCommand = createTestEasCommand(); const Log = jest.requireMock('../../log').default; - const logErrorSpy = jest.spyOn(Log, 'error'); + const logErrorSpy = jest.spyOn(Log, 'errorToStderr'); const logDebugSpy = jest.spyOn(Log, 'debug'); const runAsyncMock = jest.spyOn(TestEasCommand.prototype, 'runAsync'); const error = new Error('Unexpected, internal error message'); @@ -217,7 +217,7 @@ describe('EasCommand', () => { it('logs the cleaned message if needed', async () => { const TestEasCommand = createTestEasCommand(); const Log = jest.requireMock('../../log').default; - const logErrorSpy = jest.spyOn(Log, 'error'); + const logErrorSpy = jest.spyOn(Log, 'errorToStderr'); const logDebugSpy = jest.spyOn(Log, 'debug'); const runAsyncMock = jest.spyOn(TestEasCommand.prototype, 'runAsync'); const { CombinedError } = require('@urql/core'); @@ -237,7 +237,7 @@ describe('EasCommand', () => { it('logs the cleaned message with request ID if present', async () => { const TestEasCommand = createTestEasCommand(); const Log = jest.requireMock('../../log').default; - const logErrorSpy = jest.spyOn(Log, 'error'); + const logErrorSpy = jest.spyOn(Log, 'errorToStderr'); const logDebugSpy = jest.spyOn(Log, 'debug'); const runAsyncMock = jest.spyOn(TestEasCommand.prototype, 'runAsync'); const { CombinedError } = require('@urql/core'); @@ -272,7 +272,7 @@ describe('EasCommand', () => { it('logs the cleaned messages with request IDs if multiple GraphQL errors present', async () => { const TestEasCommand = createTestEasCommand(); const Log = jest.requireMock('../../log').default; - const logErrorSpy = jest.spyOn(Log, 'error'); + const logErrorSpy = jest.spyOn(Log, 'errorToStderr'); const logDebugSpy = jest.spyOn(Log, 'debug'); const runAsyncMock = jest.spyOn(TestEasCommand.prototype, 'runAsync'); const { CombinedError } = require('@urql/core'); diff --git a/packages/eas-cli/src/log.ts b/packages/eas-cli/src/log.ts index 64c57ac54a..deb5e67581 100644 --- a/packages/eas-cli/src/log.ts +++ b/packages/eas-cli/src/log.ts @@ -39,6 +39,12 @@ export default class Log { Log.consoleLog(...Log.withTextColor(args, chalk.red)); } + public static errorToStderr(...args: any[]): void { + Log.updateIsLastLineNewLine(args); + // eslint-disable-next-line no-console + console.error(...Log.withTextColor(args, chalk.red)); + } + public static warn(...args: any[]): void { Log.consoleLog(...Log.withTextColor(args, chalk.yellow)); }