Skip to content

Commit beff023

Browse files
committed
Merge branch 'master' into floralph/nep-bug-fix
Resolved conflict in displayImage.ts by keeping both completion check and patch validation logic
2 parents aabf20f + 5b3048f commit beff023

File tree

9 files changed

+760
-760
lines changed

9 files changed

+760
-760
lines changed

packages/amazonq/src/app/inline/EditRendering/displayImage.ts

Lines changed: 42 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,9 @@
33
* SPDX-License-Identifier: Apache-2.0
44
*/
55

6-
import { getLogger, setContext } from 'aws-core-vscode/shared'
6+
import { getContext, getLogger, setContext } from 'aws-core-vscode/shared'
77
import * as vscode from 'vscode'
8-
import { diffLines } from 'diff'
8+
import { applyPatch, diffLines } from 'diff'
99
import { LanguageClient } from 'vscode-languageclient'
1010
import { CodeWhispererSession } from '../sessionManager'
1111
import { LogInlineCompletionSessionResultsParams } from '@aws/language-server-runtimes/protocol'
@@ -310,6 +310,44 @@ export async function displaySvgDecoration(
310310
return
311311
}
312312

313+
const isPatchValid = applyPatch(editor.document.getText(), item.insertText as string)
314+
if (!isPatchValid) {
315+
const params: LogInlineCompletionSessionResultsParams = {
316+
sessionId: session.sessionId,
317+
completionSessionResult: {
318+
[item.itemId]: {
319+
seen: false,
320+
accepted: false,
321+
discarded: true,
322+
},
323+
},
324+
totalSessionDisplayTime: Date.now() - session.requestStartTime,
325+
firstCompletionDisplayLatency: session.firstCompletionDisplayLatency,
326+
isInlineEdit: true,
327+
}
328+
// TODO: this session is closed on flare side hence discarded is not emitted in flare
329+
languageClient.sendNotification('aws/logInlineCompletionSessionResults', params)
330+
return
331+
}
332+
const documentChangeListener = vscode.workspace.onDidChangeTextDocument((e) => {
333+
if (e.contentChanges.length <= 0) {
334+
return
335+
}
336+
if (e.document !== editor.document) {
337+
return
338+
}
339+
if (vsCodeState.isCodeWhispererEditing) {
340+
return
341+
}
342+
if (getContext('aws.amazonq.editSuggestionActive') === false) {
343+
return
344+
}
345+
346+
const isPatchValid = applyPatch(e.document.getText(), item.insertText as string)
347+
if (!isPatchValid) {
348+
void vscode.commands.executeCommand('aws.amazonq.inline.rejectEdit')
349+
}
350+
})
313351
await decorationManager.displayEditSuggestion(
314352
editor,
315353
svgImage,
@@ -334,6 +372,7 @@ export async function displaySvgDecoration(
334372
editor.selection = new vscode.Selection(endPosition, endPosition)
335373

336374
await decorationManager.clearDecorations(editor)
375+
documentChangeListener.dispose()
337376
const params: LogInlineCompletionSessionResultsParams = {
338377
sessionId: session.sessionId,
339378
completionSessionResult: {
@@ -367,6 +406,7 @@ export async function displaySvgDecoration(
367406
// Handle reject
368407
getLogger().info('Edit suggestion rejected')
369408
await decorationManager.clearDecorations(editor)
409+
documentChangeListener.dispose()
370410
const params: LogInlineCompletionSessionResultsParams = {
371411
sessionId: session.sessionId,
372412
completionSessionResult: {

packages/core/src/amazonqGumby/chat/controller/controller.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ import {
5858
import { getAuthType } from '../../../auth/utils'
5959
import fs from '../../../shared/fs/fs'
6060
import { setContext } from '../../../shared/vscode/setContext'
61-
import { readHistoryFile } from '../../../codewhisperer/service/transformByQ/transformationHubViewProvider'
61+
import { readHistoryFile } from '../../../codewhisperer/service/transformByQ/transformationHistoryHandler'
6262

6363
// These events can be interactions within the chat,
6464
// or elsewhere in the IDE

packages/core/src/codewhisperer/commands/startTransformByQ.ts

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

66
import * as vscode from 'vscode'
77
import * as fs from 'fs' // eslint-disable-line no-restricted-imports
8-
import os from 'os'
98
import path from 'path'
109
import { getLogger } from '../../shared/logger/logger'
1110
import * as CodeWhispererConstants from '../models/constants'
@@ -79,6 +78,12 @@ import { convertDateToTimestamp } from '../../shared/datetime'
7978
import { findStringInDirectory } from '../../shared/utilities/workspaceUtils'
8079
import { makeTemporaryToolkitFolder } from '../../shared/filesystemUtilities'
8180
import { AuthUtil } from '../util/authUtil'
81+
import {
82+
cleanupTempJobFiles,
83+
createMetadataFile,
84+
JobMetadata,
85+
writeToHistoryFile,
86+
} from '../service/transformByQ/transformationHistoryHandler'
8287

8388
export function getFeedbackCommentData() {
8489
const jobId = transformByQState.getJobId()
@@ -477,28 +482,21 @@ export async function startTransformationJob(
477482
})
478483

479484
// create local history folder(s) and store metadata
480-
const jobHistoryPath = path.join(os.homedir(), '.aws', 'transform', transformByQState.getProjectName(), jobId)
481-
if (!fs.existsSync(jobHistoryPath)) {
482-
fs.mkdirSync(jobHistoryPath, { recursive: true })
485+
const metadata: JobMetadata = {
486+
jobId: jobId,
487+
projectName: transformByQState.getProjectName(),
488+
transformationType: transformByQState.getTransformationType() ?? TransformationType.LANGUAGE_UPGRADE,
489+
sourceJDKVersion: transformByQState.getSourceJDKVersion() ?? JDKVersion.JDK8,
490+
targetJDKVersion: transformByQState.getTargetJDKVersion() ?? JDKVersion.JDK17,
491+
customDependencyVersionFilePath: transformByQState.getCustomDependencyVersionFilePath(),
492+
customBuildCommand: transformByQState.getCustomBuildCommand(),
493+
targetJavaHome: transformByQState.getTargetJavaHome() ?? '',
494+
projectPath: transformByQState.getProjectPath(),
495+
startTime: transformByQState.getStartTime(),
483496
}
484-
transformByQState.setJobHistoryPath(jobHistoryPath)
485-
// save a copy of the upload zip
486-
fs.copyFileSync(transformByQState.getPayloadFilePath(), path.join(jobHistoryPath, 'zipped-code.zip'))
487497

488-
const fields = [
489-
jobId,
490-
transformByQState.getTransformationType(),
491-
transformByQState.getSourceJDKVersion(),
492-
transformByQState.getTargetJDKVersion(),
493-
transformByQState.getCustomDependencyVersionFilePath(),
494-
transformByQState.getCustomBuildCommand(),
495-
transformByQState.getTargetJavaHome(),
496-
transformByQState.getProjectPath(),
497-
transformByQState.getStartTime(),
498-
]
499-
500-
const jobDetails = fields.join('\t')
501-
fs.writeFileSync(path.join(jobHistoryPath, 'metadata.txt'), jobDetails)
498+
const jobHistoryPath = await createMetadataFile(transformByQState.getPayloadFilePath(), metadata)
499+
transformByQState.setJobHistoryPath(jobHistoryPath)
502500
} catch (error) {
503501
getLogger().error(`CodeTransformation: ${CodeWhispererConstants.failedToStartJobNotification}`, error)
504502
const errorMessage = (error as Error).message.toLowerCase()
@@ -749,24 +747,11 @@ export async function postTransformationJob() {
749747
})
750748
}
751749

752-
// delete original upload ZIP at very end of transformation
753-
fs.rmSync(transformByQState.getPayloadFilePath(), { force: true })
754-
755-
if (
756-
transformByQState.isSucceeded() ||
757-
transformByQState.isPartiallySucceeded() ||
758-
transformByQState.isCancelled()
759-
) {
760-
// delete the copy of the upload ZIP
761-
fs.rmSync(path.join(transformByQState.getJobHistoryPath(), 'zipped-code.zip'), { force: true })
762-
// delete transformation job metadata file (no longer needed)
763-
fs.rmSync(path.join(transformByQState.getJobHistoryPath(), 'metadata.txt'), { force: true })
764-
}
765-
// delete temporary build logs file
766-
const logFilePath = path.join(os.tmpdir(), 'build-logs.txt')
767-
if (fs.existsSync(logFilePath)) {
768-
fs.rmSync(logFilePath, { force: true })
769-
}
750+
await cleanupTempJobFiles(
751+
transformByQState.getJobHistoryPath(),
752+
transformByQState.getPolledJobStatus(),
753+
transformByQState.getPayloadFilePath()
754+
)
770755

771756
// attempt download for user
772757
// TODO: refactor as explained here https://github.com/aws/aws-toolkit-vscode/pull/6519/files#r1946873107
@@ -777,35 +762,14 @@ export async function postTransformationJob() {
777762
// store job details and diff path locally (history)
778763
// TODO: ideally when job is cancelled, should be stored as CANCELLED instead of FAILED (remove this if statement after bug is fixed)
779764
if (!transformByQState.isCancelled()) {
780-
const historyLogFilePath = path.join(os.homedir(), '.aws', 'transform', 'transformation_history.tsv')
781-
// create transform folder if necessary
782-
if (!fs.existsSync(historyLogFilePath)) {
783-
fs.mkdirSync(path.dirname(historyLogFilePath), { recursive: true })
784-
// create headers of new transformation history file
785-
fs.writeFileSync(historyLogFilePath, 'date\tproject_name\tstatus\tduration\tdiff_patch\tsummary\tjob_id\n')
786-
}
787765
const latest = sessionJobHistory[transformByQState.getJobId()]
788-
const fields = [
766+
await writeToHistoryFile(
789767
latest.startTime,
790768
latest.projectName,
791769
latest.status,
792770
latest.duration,
793-
transformByQState.isSucceeded() || transformByQState.isPartiallySucceeded()
794-
? path.join(transformByQState.getJobHistoryPath(), 'diff.patch')
795-
: '',
796-
transformByQState.isSucceeded() || transformByQState.isPartiallySucceeded()
797-
? path.join(transformByQState.getJobHistoryPath(), 'summary', 'summary.md')
798-
: '',
799771
transformByQState.getJobId(),
800-
]
801-
802-
const jobDetails = fields.join('\t') + '\n'
803-
fs.writeFileSync(historyLogFilePath, jobDetails, { flag: 'a' }) // 'a' flag used to append to file
804-
await vscode.commands.executeCommand(
805-
'aws.amazonq.transformationHub.updateContent',
806-
'job history',
807-
undefined,
808-
true
772+
transformByQState.getJobHistoryPath()
809773
)
810774
}
811775
}

packages/core/src/codewhisperer/models/constants.ts

Lines changed: 1 addition & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -547,7 +547,7 @@ export const noChangesMadeMessage = "I didn't make any changes for this transfor
547547

548548
export const noOngoingJobMessage = 'No ongoing job.'
549549

550-
export const nothingToShowMessage = 'Nothing to show'
550+
export const noJobHistoryMessage = 'No job history'
551551

552552
export const jobStartedNotification =
553553
'Amazon Q is transforming your code. It can take 10 to 30 minutes to upgrade your code, depending on the size of your project. To monitor progress, go to the Transformation Hub.'
@@ -941,13 +941,3 @@ export const displayFindingsSuffix = '_displayFindings'
941941

942942
export const displayFindingsDetectorName = 'DisplayFindings'
943943
export const findingsSuffix = '_codeReviewFindings'
944-
945-
export interface HistoryObject {
946-
startTime: string
947-
projectName: string
948-
status: string
949-
duration: string
950-
diffPath: string
951-
summaryPath: string
952-
jobId: string
953-
}

packages/core/src/codewhisperer/service/transformByQ/transformFileHandler.ts

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ import { AbsolutePathDetectedError } from '../../../amazonqGumby/errors'
1717
import { getLogger } from '../../../shared/logger/logger'
1818
import AdmZip from 'adm-zip'
1919
import { IManifestFile } from './humanInTheLoopManager'
20+
import { ExportResultArchiveStructure } from '../../../shared/utilities/download'
21+
import { isFileNotFoundError } from '../../../shared/errors'
2022

2123
export async function getDependenciesFolderInfo(): Promise<FolderInfo> {
2224
const dependencyFolderName = `${CodeWhispererConstants.dependencyFolderName}${globals.clock.Date.now()}`
@@ -348,3 +350,40 @@ export async function parseVersionsListFromPomFile(xmlString: string): Promise<I
348350

349351
return { latestVersion, majorVersions, minorVersions, status }
350352
}
353+
354+
/**
355+
* Saves a copy of the diff patch, summary, and build logs (if any) locally
356+
*
357+
* @param pathToArchiveDir path to the archive directory where the artifacts are unzipped
358+
* @param pathToDestinationDir destination directory (will create directories if path doesn't exist already)
359+
*/
360+
export async function copyArtifacts(pathToArchiveDir: string, pathToDestinationDir: string) {
361+
// create destination path if doesn't exist already
362+
// mkdir() will not raise an error if path exists
363+
await fs.mkdir(pathToDestinationDir)
364+
365+
const diffPath = path.join(pathToArchiveDir, ExportResultArchiveStructure.PathToDiffPatch)
366+
const summaryPath = path.join(pathToArchiveDir, ExportResultArchiveStructure.PathToSummary)
367+
368+
try {
369+
await fs.copy(diffPath, path.join(pathToDestinationDir, 'diff.patch'))
370+
// make summary directory if needed
371+
await fs.mkdir(path.join(pathToDestinationDir, 'summary'))
372+
await fs.copy(summaryPath, path.join(pathToDestinationDir, 'summary', 'summary.md'))
373+
} catch (error) {
374+
getLogger().error('Code Transformation: Error saving local copy of artifacts: %s', (error as Error).message)
375+
}
376+
377+
const buildLogsPath = path.join(path.dirname(summaryPath), 'buildCommandOutput.log')
378+
try {
379+
await fs.copy(buildLogsPath, path.join(pathToDestinationDir, 'summary', 'buildCommandOutput.log'))
380+
} catch (error) {
381+
// build logs won't exist for SQL conversions (not an error)
382+
if (!isFileNotFoundError(error)) {
383+
getLogger().error(
384+
'Code Transformation: Error saving local copy of build logs: %s',
385+
(error as Error).message
386+
)
387+
}
388+
}
389+
}

0 commit comments

Comments
 (0)