Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion src/utils/deploy/upload-source-zip.ts
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,13 @@ export const uploadSourceZip = async ({
try {
zipPath = await createSourceZip({ sourceDir, filename, statusCb })
} catch (error) {
const errorMsg = error instanceof Error ? error.message : String(error)
const execError = error as { message?: string; stderr?: string | Buffer; code?: number | string }
const stderr = execError.stderr ? String(execError.stderr).trim() : ''
const detail = [stderr, execError.code != null ? `exit code ${String(execError.code)}` : '']
.filter(Boolean)
.join('; ')
const baseMsg = error instanceof Error ? error.message : String(error)
const errorMsg = detail ? `${baseMsg} (${detail})` : baseMsg
Comment thread
coderabbitai[bot] marked this conversation as resolved.
statusCb({
type: 'source-zip-upload',
msg: `Failed to create source zip: ${errorMsg}`,
Expand Down
48 changes: 48 additions & 0 deletions tests/unit/utils/deploy/upload-source-zip.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -282,6 +282,54 @@ describe('uploadSourceZip', () => {
expect(mockCommandHelpers.warn).toHaveBeenCalledWith('Failed to create source zip: zip command failed')
})

test('surfaces zip stderr and exit code in the error message', async () => {
const mockOs = await import('os')
vi.mocked(mockOs.platform).mockReturnValue('darwin')

const { uploadSourceZip } = await import('../../../../src/utils/deploy/upload-source-zip.js')

const mockChildProcess = await import('child_process')
const mockCommandHelpers = await import('../../../../src/utils/command-helpers.js')
const mockTempFile = await import('../../../../src/utils/temporary-file.js')

// util.promisify(execFile) rejects with an error carrying `stderr` and `code`.
const zipError = Object.assign(new Error('Command failed: zip -r /tmp/x.zip .'), {
stderr: 'zip error: Nothing to do! (/tmp/x.zip)',
code: 12,
})
vi.mocked(mockChildProcess.execFile).mockImplementation((_command, _args, _options, callback) => {
if (callback) {
callback(zipError, '', '')
}
return {} as ChildProcess
})

vi.mocked(mockCommandHelpers.warn).mockImplementation(() => {})
vi.mocked(mockTempFile.temporaryDirectory).mockReturnValue('/tmp/test-temp-dir')

const mockStatusCb = vi.fn()

await expect(
uploadSourceZip({
sourceDir: '/test/source',
uploadUrl: 'https://s3.example.com/upload-url',
filename: 'test-source.zip',
statusCb: mockStatusCb,
}),
).rejects.toThrow('Command failed: zip -r /tmp/x.zip .')

const expectedMsg =
'Failed to create source zip: Command failed: zip -r /tmp/x.zip . (zip error: Nothing to do! (/tmp/x.zip); exit code 12)'
expect(mockCommandHelpers.warn).toHaveBeenCalledWith(expectedMsg)
expect(mockStatusCb).toHaveBeenCalledWith(
expect.objectContaining({
type: 'source-zip-upload',
phase: 'error',
msg: expectedMsg,
}),
)
})

test('cleans up zip file even when upload fails', async () => {
// Ensure OS platform mock returns non-Windows
const mockOs = await import('os')
Expand Down
Loading