Skip to content

Commit 9ad44f4

Browse files
Merge pull request #3063 from nkomonen-amazon/samPythonError
feat(sam): collect sam cli errors
2 parents 7b9ed90 + f70ed70 commit 9ad44f4

File tree

2 files changed

+81
-1
lines changed

2 files changed

+81
-1
lines changed

src/shared/sam/cli/samCliInvokerUtils.ts

Lines changed: 42 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,47 @@ export function logAndThrowIfUnexpectedExitCode(processResult: ChildProcessResul
5555
message = processResult.error.message
5656
}
5757
}
58+
const usefulErrors = collectAcceptedErrorMessages(processResult.stderr).join('\n')
59+
throw makeUnexpectedExitCodeError(message ?? usefulErrors)
60+
}
61+
62+
/**
63+
* Collect known errors messages from a sam error message
64+
* that may have multiple errors in one message.
65+
* @param errors A string that can have multiple error messages
66+
*/
67+
export function collectAcceptedErrorMessages(errorMessage: string): string[] {
68+
const errors = errorMessage.split('\n')
69+
const shouldCollectFuncs = [startsWithEscapeSequence, startsWithError]
70+
return errors.filter(error => {
71+
return shouldCollectFuncs.some(shouldCollect => {
72+
return shouldCollect(error)
73+
})
74+
})
75+
}
76+
77+
/**
78+
* All accepted escape sequences.
79+
*/
80+
const yellowForeground = '[33m'
81+
const acceptedSequences = [yellowForeground]
82+
83+
/**
84+
* Returns true if text starts with an escape
85+
* sequence with one of the accepted sequences.
86+
*/
87+
function startsWithEscapeSequence(text: string, sequences = acceptedSequences): boolean {
88+
const escapeInDecimal = 27
89+
if (text.charCodeAt(0) !== escapeInDecimal) {
90+
return false
91+
}
92+
93+
const remainingText = text.substring(1)
94+
return sequences.some(code => {
95+
return remainingText.startsWith(code)
96+
})
97+
}
5898

59-
throw makeUnexpectedExitCodeError(message ?? 'no message available')
99+
function startsWithError(text: string): boolean {
100+
return text.startsWith('Error:')
60101
}

src/test/shared/sam/cli/samCliInvokerUtils.test.ts

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55

66
import * as assert from 'assert'
77
import {
8+
collectAcceptedErrorMessages,
89
logAndThrowIfUnexpectedExitCode,
910
makeUnexpectedExitCodeError,
1011
} from '../../../../shared/sam/cli/samCliInvokerUtils'
@@ -42,3 +43,41 @@ describe('logAndThrowIfUnexpectedExitCode', async function () {
4243
await assertLogContainsBadExitInformation(getTestLogger(), childProcessResult, 456)
4344
})
4445
})
46+
47+
/**
48+
* Returns a string with the 'Escape' character
49+
* prepended to the given text.
50+
*
51+
* This exists because using '\e' does not
52+
* work.
53+
*/
54+
function prependEscapeCode(text: string): string {
55+
return String.fromCharCode(27) + text
56+
}
57+
58+
describe('collectAcceptedErrorMessages()', async () => {
59+
let result: string[]
60+
61+
before(async () => {
62+
const input = [
63+
prependEscapeCode('[33m This is an accepted escape sequence'),
64+
prependEscapeCode('[100m This is not an accepted escape sequence'),
65+
'This will be ignored',
66+
'Error: This is accepted due to the prefix',
67+
].join('\n')
68+
result = collectAcceptedErrorMessages(input)
69+
})
70+
71+
it('has the expected amount of messages', async () => {
72+
assert.strictEqual(result.length, 2)
73+
})
74+
it('collects the "Error:" prefix', async () => {
75+
assert(result.includes('Error: This is accepted due to the prefix'))
76+
})
77+
it('collects accepted escape sequence prefixes', async () => {
78+
assert(result.includes(prependEscapeCode('[33m This is an accepted escape sequence')))
79+
})
80+
it('ignores non-accepted escape sequence prefixes', async () => {
81+
assert(!result.includes(prependEscapeCode('[100m This is not an accepted escape sequence')))
82+
})
83+
})

0 commit comments

Comments
 (0)