Skip to content

Commit d9e60a8

Browse files
authored
fix(gumby): add jobId to feedback, delete temp files, use shell:true aws#4237
Problem: 1. Need to add jobId to in-IDE feedback when a transformation job was run so that we can investigate any issues 2. Need to delete temp files (the ZIP upload and the result archive) once no longer needed 3. Need a test requested by AppSec to ensure zipCode behaves correctly 4. Minor typos 5. Dropdown menu with 3 buttons (Show Changes, Show Plan, Show Summary) was inconsistent: Show Plan was visible but enabled/disabled as necessary while the other 2 buttons toggled their visibility as necessary 6. Used Font Awesome for an icon, but better to use built-in icon 7. When running shell commands setting `shell: false` resulted in the `mvn` command failing for 1 user due to inability to find the Maven executable Solution: 1. Add jobId 2. Delete temp files 3. Write test 4. Fix typos 5. Align behavior of all 3 buttons via `"enablement"` 6. Use built-in icon 7. Set `shell: true`
1 parent e64bd7a commit d9e60a8

File tree

10 files changed

+111
-45
lines changed

10 files changed

+111
-45
lines changed
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
{
2+
"type": "Bug Fix",
3+
"description": "Amazon Q Transform: delete temp files we create when no longer needed"
4+
}
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
{
2+
"type": "Bug Fix",
3+
"description": "Amazon Q Transform: make copy-dependencies command less error prone"
4+
}

package.json

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -787,7 +787,7 @@
787787
},
788788
{
789789
"view": "aws.amazonq.transformationProposedChangesTree",
790-
"contents": "Project transformation is complete.\n Downloading the updated code.",
790+
"contents": "Project transformation is complete.\n Downloading the proposed changes...",
791791
"when": "gumby.reviewState == PreparingReview"
792792
}
793793
],
@@ -1343,11 +1343,11 @@
13431343
},
13441344
{
13451345
"command": "aws.amazonq.transformationHub.summary.reveal",
1346-
"when": "view == aws.amazonq.transformationHub && gumby.reviewState == InReview"
1346+
"when": "view == aws.amazonq.transformationHub"
13471347
},
13481348
{
13491349
"command": "aws.amazonq.transformationHub.reviewChanges.reveal",
1350-
"when": "view == aws.amazonq.transformationHub && gumby.reviewState == InReview"
1350+
"when": "view == aws.amazonq.transformationHub"
13511351
},
13521352
{
13531353
"command": "aws.amazonq.showTransformationPlanInHub",
@@ -2414,15 +2414,18 @@
24142414
},
24152415
{
24162416
"command": "aws.amazonq.transformationHub.summary.reveal",
2417-
"title": "%AWS.command.q.transform.showChangeSummary%"
2417+
"title": "%AWS.command.q.transform.showChangeSummary%",
2418+
"enablement": "gumby.reviewState == InReview"
24182419
},
24192420
{
24202421
"command": "aws.amazonq.transformationHub.reviewChanges.reveal",
2421-
"title": "%AWS.command.q.transform.showChanges%"
2422+
"title": "%AWS.command.q.transform.showChanges%",
2423+
"enablement": "gumby.reviewState == InReview"
24222424
},
24232425
{
24242426
"command": "aws.amazonq.showTransformationPlanInHub",
2425-
"title": "%AWS.command.q.transform.showTransformationPlan%"
2427+
"title": "%AWS.command.q.transform.showTransformationPlan%",
2428+
"enablement": "gumby.isPlanAvailable"
24262429
},
24272430
{
24282431
"command": "aws.auth.switchConnections",

src/codewhisperer/commands/startTransformByQ.ts

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -164,6 +164,7 @@ export async function startTransformByQ() {
164164
let intervalId = undefined
165165
let errorMessage = ''
166166
let resultStatusMessage = ''
167+
let payloadFileName = ''
167168

168169
try {
169170
intervalId = setInterval(() => {
@@ -176,11 +177,11 @@ export async function startTransformByQ() {
176177
let uploadId = ''
177178
throwIfCancelled()
178179
try {
179-
const payloadFileName = await zipCode(state.project.description!)
180+
payloadFileName = await zipCode(state.project.description!)
180181
await vscode.commands.executeCommand('aws.amazonq.refresh') // so that button updates
181182
uploadId = await uploadPayload(payloadFileName)
182183
} catch (error) {
183-
errorMessage = 'Failed to upload archive'
184+
errorMessage = 'Failed to upload code'
184185
telemetry.codeTransform_logGeneralError.emit({
185186
codeTransformSessionId: codeTransformTelemetryState.getSessionId(),
186187
codeTransformApiErrorMessage: errorMessage,
@@ -331,6 +332,10 @@ export async function startTransformByQ() {
331332
} else if (transformByQState.isPartiallySucceeded()) {
332333
void vscode.window.showInformationMessage(CodeWhispererConstants.transformByQPartiallyCompletedMessage)
333334
}
335+
336+
if (payloadFileName !== '') {
337+
fs.rmSync(payloadFileName, { recursive: true, force: true }) // delete ZIP if it exists
338+
}
334339
}
335340
clearInterval(intervalId)
336341
transformByQState.setToNotStarted() // so that the "Transform by Q" button resets

src/codewhisperer/models/model.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -274,6 +274,8 @@ export class TransformByQState {
274274
private planFilePath: string = ''
275275
private summaryFilePath: string = ''
276276

277+
private resultArchiveFilePath: string = ''
278+
277279
private polledJobStatus: string = ''
278280

279281
private jobFailureReason: string = ''
@@ -338,6 +340,10 @@ export class TransformByQState {
338340
return this.summaryFilePath
339341
}
340342

343+
public getResultArchiveFilePath() {
344+
return this.resultArchiveFilePath
345+
}
346+
341347
public getJobFailureReason() {
342348
return this.jobFailureReason
343349
}
@@ -402,6 +408,10 @@ export class TransformByQState {
402408
this.summaryFilePath = filePath
403409
}
404410

411+
public setResultArchiveFilePath(filePath: string) {
412+
this.resultArchiveFilePath = filePath
413+
}
414+
405415
public setJobFailureReason(reason: string) {
406416
this.jobFailureReason = reason
407417
}

src/codewhisperer/service/transformByQHandler.ts

Lines changed: 25 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,7 @@ export async function validateProjectSelection(project: vscode.QuickPickItem) {
113113
const classFilePath = compiledJavaFiles[0].fsPath
114114
const baseCommand = 'javap'
115115
const args = ['-v', classFilePath]
116-
const spawnResult = spawnSync(baseCommand, args, { shell: false, encoding: 'utf-8' })
116+
const spawnResult = spawnSync(baseCommand, args, { shell: true, encoding: 'utf-8' })
117117

118118
if (spawnResult.error || spawnResult.status !== 0) {
119119
void vscode.window.showErrorMessage(CodeWhispererConstants.noSupportedJavaProjectsFoundMessage)
@@ -190,13 +190,13 @@ export async function uploadArtifactToS3(fileName: string, resp: CreateUploadUrl
190190
})
191191
getLogger().info(`CodeTransform: Status from S3 Upload = ${response.status}`)
192192
} catch (e: any) {
193-
const errorMessage = e?.message || 'Error in S3 UploadZip API call'
193+
const errorMessage = (e as Error).message ?? 'Error in S3 UploadZip API call'
194194
getLogger().error('CodeTransform: UploadZip error = ', errorMessage)
195195
telemetry.codeTransform_logApiError.emit({
196196
codeTransformApiNames: 'UploadZip',
197197
codeTransformSessionId: codeTransformTelemetryState.getSessionId(),
198198
codeTransformApiErrorMessage: errorMessage,
199-
codeTransformRequestId: e?.requestId,
199+
codeTransformRequestId: e.requestId ?? '',
200200
result: MetadataResult.Fail,
201201
reason: 'UploadToS3Failed',
202202
})
@@ -229,8 +229,8 @@ export async function stopJob(jobId: string) {
229229
codeTransformApiNames: 'StopTransformation',
230230
codeTransformSessionId: codeTransformTelemetryState.getSessionId(),
231231
codeTransformJobId: jobId,
232-
codeTransformApiErrorMessage: e?.message || errorMessage,
233-
codeTransformRequestId: e?.requestId,
232+
codeTransformApiErrorMessage: (e as Error).message ?? errorMessage,
233+
codeTransformRequestId: e.requestId ?? '',
234234
result: MetadataResult.Fail,
235235
reason: 'StopTransformationFailed',
236236
})
@@ -242,9 +242,10 @@ export async function stopJob(jobId: string) {
242242
export async function uploadPayload(payloadFileName: string) {
243243
const sha256 = getSha256(payloadFileName)
244244
throwIfCancelled()
245+
let response = undefined
245246
try {
246247
const apiStartTime = Date.now()
247-
const response = await codeWhisperer.codeWhispererClient.createUploadUrl({
248+
response = await codeWhisperer.codeWhispererClient.createUploadUrl({
248249
contentChecksum: sha256,
249250
contentChecksumType: CodeWhispererConstants.contentChecksumType,
250251
uploadIntent: CodeWhispererConstants.uploadIntent,
@@ -257,22 +258,27 @@ export async function uploadPayload(payloadFileName: string) {
257258
codeTransformRequestId: response.$response.requestId,
258259
result: MetadataResult.Pass,
259260
})
260-
await uploadArtifactToS3(payloadFileName, response)
261-
return response.uploadId
262261
} catch (e: any) {
263-
const errorMessage = e?.message || 'Error in CreateUploadUrl API call'
262+
const errorMessage = (e as Error).message ?? 'Error in CreateUploadUrl API call'
264263
getLogger().error('CodeTransform: CreateUploadUrl error: = ', errorMessage)
265264
telemetry.codeTransform_logApiError.emit({
266265
codeTransformApiNames: 'CreateUploadUrl',
267266
codeTransformSessionId: codeTransformTelemetryState.getSessionId(),
268267
codeTransformApiErrorMessage: errorMessage,
269-
codeTransformRequestId: e?.requestId,
268+
codeTransformRequestId: e.requestId ?? '',
270269
result: MetadataResult.Fail,
271270
reason: 'CreateUploadUrlFailed',
272271
})
273272
// Pass along error to callee function
274273
throw new ToolkitError(errorMessage, { cause: e as Error })
275274
}
275+
try {
276+
await uploadArtifactToS3(payloadFileName, response)
277+
} catch (e: any) {
278+
const errorMessage = (e as Error).message ?? 'Error in uploadArtifactToS3 call'
279+
throw new ToolkitError(errorMessage, { cause: e as Error })
280+
}
281+
return response.uploadId
276282
}
277283

278284
/**
@@ -317,7 +323,7 @@ function getProjectDependencies(modulePath: string): string[] {
317323
'-Dmdep.copyPom=true',
318324
'-Dmdep.addParentPoms=true',
319325
]
320-
const spawnResult = spawnSync(baseCommand, args, { cwd: modulePath, shell: false, encoding: 'utf-8' })
326+
const spawnResult = spawnSync(baseCommand, args, { cwd: modulePath, shell: true, encoding: 'utf-8' })
321327

322328
if (spawnResult.error || spawnResult.status !== 0) {
323329
void vscode.window.showErrorMessage(CodeWhispererConstants.dependencyErrorMessage)
@@ -439,13 +445,13 @@ export async function startJob(uploadId: string) {
439445
})
440446
return response.transformationJobId
441447
} catch (e: any) {
442-
const errorMessage = e?.message || 'Error in StartTransformation API call'
448+
const errorMessage = (e as Error).message ?? 'Error in StartTransformation API call'
443449
getLogger().error('CodeTransform: StartTransformation error = ', errorMessage)
444450
telemetry.codeTransform_logApiError.emit({
445451
codeTransformApiNames: 'StartTransformation',
446452
codeTransformSessionId: codeTransformTelemetryState.getSessionId(),
447453
codeTransformApiErrorMessage: errorMessage,
448-
codeTransformRequestId: e?.requestId,
454+
codeTransformRequestId: e.requestId ?? '',
449455
result: MetadataResult.Fail,
450456
reason: 'StartTransformationFailed',
451457
})
@@ -490,14 +496,14 @@ export async function getTransformationPlan(jobId: string) {
490496

491497
return plan
492498
} catch (e: any) {
493-
const errorMessage = e?.message || 'Error in GetTransformationPlan API call'
499+
const errorMessage = (e as Error).message ?? 'Error in GetTransformationPlan API call'
494500
getLogger().error('CodeTransform: GetTransformationPlan error = ', errorMessage)
495501
telemetry.codeTransform_logApiError.emit({
496502
codeTransformApiNames: 'GetTransformationPlan',
497503
codeTransformSessionId: codeTransformTelemetryState.getSessionId(),
498504
codeTransformJobId: jobId,
499505
codeTransformApiErrorMessage: errorMessage,
500-
codeTransformRequestId: e?.requestId,
506+
codeTransformRequestId: e.requestId ?? '',
501507
result: MetadataResult.Fail,
502508
reason: 'GetTransformationPlanFailed',
503509
})
@@ -523,14 +529,14 @@ export async function getTransformationSteps(jobId: string) {
523529
})
524530
return response.transformationPlan.transformationSteps
525531
} catch (e: any) {
526-
const errorMessage = e?.message || 'Error in GetTransformationPlan API call'
532+
const errorMessage = (e as Error).message ?? 'Error in GetTransformationPlan API call'
527533
getLogger().error('CodeTransform: GetTransformationPlan error = ', errorMessage)
528534
telemetry.codeTransform_logApiError.emit({
529535
codeTransformApiNames: 'GetTransformationPlan',
530536
codeTransformSessionId: codeTransformTelemetryState.getSessionId(),
531537
codeTransformJobId: jobId,
532538
codeTransformApiErrorMessage: errorMessage,
533-
codeTransformRequestId: e?.requestId,
539+
codeTransformRequestId: e.requestId ?? '',
534540
result: MetadataResult.Fail,
535541
reason: 'GetTransformationPlanFailed',
536542
})
@@ -584,14 +590,14 @@ export async function pollTransformationJob(jobId: string, validStates: string[]
584590
throw new Error('Transform by Q timed out')
585591
}
586592
} catch (e: any) {
587-
const errorMessage = e?.message || 'Error in GetTransformation API call'
593+
const errorMessage = (e as Error).message ?? 'Error in GetTransformation API call'
588594
getLogger().error('CodeTransform: GetTransformation error = ', errorMessage)
589595
telemetry.codeTransform_logApiError.emit({
590596
codeTransformApiNames: 'GetTransformation',
591597
codeTransformSessionId: codeTransformTelemetryState.getSessionId(),
592598
codeTransformJobId: jobId,
593599
codeTransformApiErrorMessage: errorMessage,
594-
codeTransformRequestId: e?.requestId,
600+
codeTransformRequestId: e.requestId ?? '',
595601
result: MetadataResult.Fail,
596602
reason: 'GetTransformationFailed',
597603
})

src/codewhisperer/service/transformationHubViewProvider.ts

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,7 @@ export class TransformationHubViewProvider implements vscode.WebviewViewProvider
123123
progressHtml = `<p><b>Transformation Status</b></p>`
124124
progressHtml += `<p> ${this.getProgressIconMarkup(
125125
planProgress['uploadCode']
126-
)} Upload code to secure build environment</p>`
126+
)} Zip code and upload to secure build environment</p>`
127127
if (planProgress['uploadCode'] === StepProgress.Succeeded) {
128128
progressHtml += `<p> ${this.getProgressIconMarkup(
129129
planProgress['buildCode']
@@ -189,7 +189,20 @@ export class TransformationHubViewProvider implements vscode.WebviewViewProvider
189189
<html lang="en">
190190
<head>
191191
<title>Transformation Hub</title>
192-
<script src="https://kit.fontawesome.com/f865aad943.js" crossorigin="anonymous"></script>
192+
<style>
193+
@keyframes spin {
194+
0% {
195+
transform: rotate(0deg);
196+
}
197+
100% {
198+
transform: rotate(360deg);
199+
}
200+
}
201+
.spinner {
202+
display: inline-block;
203+
animation: spin 1s infinite;
204+
}
205+
</style>
193206
</head>
194207
<body>
195208
<div style="display: flex">
@@ -233,13 +246,14 @@ export class TransformationHubViewProvider implements vscode.WebviewViewProvider
233246
</html>`
234247
}
235248

249+
// TODO: replace these pasted icons with codicons
236250
private getProgressIconMarkup(stepStatus: StepProgress) {
237251
if (stepStatus === StepProgress.Succeeded) {
238252
return `<span style="color: green"> ✓ </span>`
239253
} else if (stepStatus === StepProgress.Pending) {
240-
return `<span> <i class="fas fa-spinner fa-spin"></i> </span>` // TODO: switch from FA to native VSCode icons
254+
return `<span style="color: grey" class="spinner"> ↻ </span>`
241255
} else {
242-
return `<span style="color: red"> X </span>`
256+
return `<span style="color: grey"> </span>`
243257
}
244258
}
245259
}

0 commit comments

Comments
 (0)