Skip to content

Commit 81f88e4

Browse files
authored
fix(amazonq): decide project language by file count aws#4862
Problem Project scans fail server side validation if the active file is an unsupported codewhisperer language, even if there are other supported files in the project. Solution Get the most-frequent language (based on file extension) when preparing the zip file and use that as the language.
1 parent d3a7ca1 commit 81f88e4

File tree

4 files changed

+32
-1
lines changed

4 files changed

+32
-1
lines changed

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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,9 @@ export async function startSecurityScan(
131131
codeScanTelemetryEntry.codewhispererCodeScanBuildPayloadBytes = zipMetadata.buildPayloadSizeInBytes
132132
codeScanTelemetryEntry.codewhispererCodeScanSrcZipFileBytes = zipMetadata.zipFileSizeInBytes
133133
codeScanTelemetryEntry.codewhispererCodeScanLines = zipMetadata.lines
134+
if (zipMetadata.language) {
135+
codeScanTelemetryEntry.codewhispererLanguage = zipMetadata.language
136+
}
134137

135138
/**
136139
* Step 2: Get presigned Url, upload and clean up

packages/core/src/codewhisperer/util/runtimeLanguageContext.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -180,6 +180,17 @@ export class RuntimeLanguageContext {
180180
return { language: this.normalizeLanguage(languageId) ?? 'plaintext' }
181181
}
182182

183+
/**
184+
*
185+
* @param fileExtension File extension i.e. py, js, java
186+
* @returns The corresponding {@link CodewhispererLanguage} of the file extension
187+
*/
188+
public getLanguageFromFileExtension(fileExtension: string): CodewhispererLanguage | undefined {
189+
return [...this.supportedLanguageExtensionMap.entries()].find(
190+
([, extension]) => extension === fileExtension
191+
)?.[0]
192+
}
193+
183194
/**
184195
* Mapping the field ProgrammingLanguage of codewhisperer generateRecommendationRequest | listRecommendationRequest to
185196
* its Codewhisperer runtime language e.g. jsx -> typescript, typescript -> typescript

packages/core/src/codewhisperer/util/zipUtil.ts

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ import { ToolkitError } from '../../shared/errors'
1212
import { fsCommon } from '../../srcShared/fs'
1313
import { collectFiles } from '../../amazonqFeatureDev/util/files'
1414
import { getLoggerForScope } from '../service/securityScanHandler'
15+
import { runtimeLanguageContext } from './runtimeLanguageContext'
16+
import { CodewhispererLanguage } from '../../shared/telemetry/telemetry.gen'
1517

1618
export interface ZipMetadata {
1719
rootDir: string
@@ -21,6 +23,7 @@ export interface ZipMetadata {
2123
buildPayloadSizeInBytes: number
2224
zipFileSizeInBytes: number
2325
lines: number
26+
language: CodewhispererLanguage | undefined
2427
}
2528

2629
export const ZipConstants = {
@@ -38,6 +41,7 @@ export class ZipUtil {
3841
protected _zipDir: string = ''
3942
protected _totalLines: number = 0
4043
protected _fetchedDirs: Set<string> = new Set<string>()
44+
protected _language: CodewhispererLanguage | undefined
4145

4246
constructor() {}
4347

@@ -119,6 +123,7 @@ export class ZipUtil {
119123
}
120124

121125
const files = await collectFiles([projectPath], [workspaceFolder])
126+
const languageCount = new Map<CodewhispererLanguage, number>()
122127
for (const file of files) {
123128
const isFileOpenAndDirty = this.isFileOpenAndDirty(file.fileUri)
124129
const fileContent = isFileOpenAndDirty ? await this.getTextContent(file.fileUri) : file.fileContent
@@ -137,6 +142,13 @@ export class ZipUtil {
137142
this._pickedSourceFiles.add(file.fileUri.fsPath)
138143
this._totalSize += fileSize
139144
this._totalLines += fileContent.split(ZipConstants.newlineRegex).length
145+
146+
const language = runtimeLanguageContext.getLanguageFromFileExtension(
147+
path.extname(file.fileUri.fsPath).slice(1)
148+
)
149+
if (language) {
150+
languageCount.set(language, (languageCount.get(language) || 0) + 1)
151+
}
140152
}
141153

142154
if (isFileOpenAndDirty) {
@@ -146,6 +158,10 @@ export class ZipUtil {
146158
}
147159
}
148160

161+
if (languageCount.size === 0) {
162+
throw new ToolkitError('Project does not contain valid files to scan')
163+
}
164+
this._language = [...languageCount.entries()].reduce((a, b) => (b[1] > a[1] ? b : a))[0]
149165
const zipFilePath = this.getZipDirPath() + CodeWhispererConstants.codeScanZipExt
150166
zip.writeZip(zipFilePath)
151167
return zipFilePath
@@ -193,6 +209,7 @@ export class ZipUtil {
193209
zipFileSizeInBytes: zipFileSize,
194210
buildPayloadSizeInBytes: this._totalBuildSize,
195211
lines: this._totalLines,
212+
language: this._language,
196213
}
197214
} catch (error) {
198215
getLogger().error('Zip error caused by:', error)

packages/core/src/test/codewhisperer/commands/startSecurityScan.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -284,7 +284,7 @@ describe('startSecurityScan', function () {
284284
CodeAnalysisScope.PROJECT
285285
)
286286
assertTelemetry('codewhisperer_securityScan', {
287-
codewhispererLanguage: 'python',
287+
codewhispererLanguage: 'yaml',
288288
codewhispererCodeScanTotalIssues: 1,
289289
codewhispererCodeScanIssuesWithFixes: 0,
290290
} as CodewhispererSecurityScan)

0 commit comments

Comments
 (0)