Skip to content

Commit 5068a17

Browse files
author
Hamed Soleimani
committed
Allow including binary files when execution is enabled
1 parent 24bb920 commit 5068a17

File tree

4 files changed

+70
-28
lines changed

4 files changed

+70
-28
lines changed

packages/amazonq/test/unit/amazonqFeatureDev/util/files.test.ts

Lines changed: 43 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -16,23 +16,45 @@ import { MetricName, Span } from 'aws-core-vscode/telemetry'
1616
import sinon from 'sinon'
1717
import { CodeWhispererSettings } from 'aws-core-vscode/codewhisperer'
1818

19-
const testDevfilePrepareRepo = async (expectedRepoSize: number, devfileEnabled: boolean) => {
19+
import AdmZip from 'adm-zip'
20+
21+
const testDevfilePrepareRepo = async (devfileEnabled: boolean) => {
22+
const files: Record<string, string> = {
23+
'file.md': 'test content',
24+
// only include when execution is enabled
25+
'devfile.yaml': 'test',
26+
// .git folder is always dropped (because of vscode global exclude rules)
27+
'.git/ref': '####',
28+
// .gitignore should always be included
29+
'.gitignore': 'node_models/*',
30+
// non code files only when dev execution is enabled
31+
'abc.jar': 'jar-content',
32+
'data/logo.ico': 'binary-content',
33+
}
2034
const folder = await TestFolder.create()
21-
await folder.write('devfile.yaml', 'test')
22-
await folder.write('file.md', 'test content')
35+
36+
for (const [fileName, content] of Object.entries(files)) {
37+
await folder.write(fileName, content)
38+
}
39+
40+
const expectedFiles = !devfileEnabled
41+
? ['./file.md', './.gitignore']
42+
: ['./devfile.yaml', './file.md', './.gitignore', './abc.jar', 'data/logo.ico']
43+
2344
const workspace = getWorkspaceFolder(folder.path)
2445
sinon
2546
.stub(CodeWhispererSettings.instance, 'getDevCommandWorkspaceConfigurations')
2647
.returns(devfileEnabled ? { [workspace.uri.fsPath]: true } : {})
2748

28-
await testPrepareRepoData(workspace, expectedRepoSize)
49+
await testPrepareRepoData(workspace, expectedFiles)
2950
}
3051

3152
const testPrepareRepoData = async (
3253
workspace: vscode.WorkspaceFolder,
33-
expectedRepoSize: number,
54+
expectedFiles: string[],
3455
expectedTelemetryMetrics?: Array<{ metricName: MetricName; value: any }>
3556
) => {
57+
expectedFiles.sort((a, b) => a.localeCompare(b))
3658
const telemetry = new TelemetryHelper()
3759
const result = await prepareRepoData([workspace.uri.fsPath], [workspace], telemetry, {
3860
record: () => {},
@@ -41,13 +63,18 @@ const testPrepareRepoData = async (
4163
assert.strictEqual(Buffer.isBuffer(result.zipFileBuffer), true)
4264
// checksum is not the same across different test executions because some unique random folder names are generated
4365
assert.strictEqual(result.zipFileChecksum.length, 44)
44-
assert.strictEqual(telemetry.repositorySize, expectedRepoSize)
4566

4667
if (expectedTelemetryMetrics) {
47-
expectedTelemetryMetrics.forEach((metric) => {
68+
for (const metric of expectedTelemetryMetrics) {
4869
assertTelemetry(metric.metricName, metric.value)
49-
})
70+
}
5071
}
72+
73+
// Unzip the buffer and compare the entry names
74+
const zip = new AdmZip(result.zipFileBuffer)
75+
const actualZipEntries = zip.getEntries().map((entry) => entry.entryName)
76+
actualZipEntries.sort((a, b) => a.localeCompare(b))
77+
assert.deepStrictEqual(actualZipEntries, expectedFiles)
5178
}
5279

5380
describe('file utils', () => {
@@ -62,25 +89,27 @@ describe('file utils', () => {
6289
await folder.write('file2.md', 'test content')
6390
const workspace = getWorkspaceFolder(folder.path)
6491

65-
await testPrepareRepoData(workspace, 24)
92+
await testPrepareRepoData(workspace, ['./file1.md', './file2.md'])
6693
})
6794

6895
it('prepareRepoData ignores denied file extensions', async function () {
6996
const folder = await TestFolder.create()
7097
await folder.write('file.mp4', 'test content')
7198
const workspace = getWorkspaceFolder(folder.path)
7299

73-
await testPrepareRepoData(workspace, 0, [
74-
{ metricName: 'amazonq_bundleExtensionIgnored', value: { filenameExt: 'mp4', count: 1 } },
75-
])
100+
await testPrepareRepoData(
101+
workspace,
102+
[],
103+
[{ metricName: 'amazonq_bundleExtensionIgnored', value: { filenameExt: 'mp4', count: 1 } }]
104+
)
76105
})
77106

78107
it('should ignore devfile.yaml when setting is disabled', async function () {
79-
await testDevfilePrepareRepo(12, false)
108+
await testDevfilePrepareRepo(false)
80109
})
81110

82111
it('should include devfile.yaml when setting is enabled', async function () {
83-
await testDevfilePrepareRepo(16, true)
112+
await testDevfilePrepareRepo(true)
84113
})
85114

86115
// Test the logic that allows the customer to modify root source folder

packages/core/src/amazonqFeatureDev/util/files.ts

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -40,9 +40,10 @@ export async function prepareRepoData(
4040
zip: AdmZip = new AdmZip()
4141
) {
4242
try {
43-
const files = await collectFiles(repoRootPaths, workspaceFolders, true, maxRepoSizeBytes)
4443
const devCommandWorkspaceConfigurations = CodeWhispererSettings.instance.getDevCommandWorkspaceConfigurations()
4544
const useAutoBuildFeature = devCommandWorkspaceConfigurations[repoRootPaths[0]] ?? false
45+
// We only respect gitignore file rules if useAutoBuildFeature is on, this is to avoid dropping necessary files for building the code (e.g. png files imported in js code)
46+
const files = await collectFiles(repoRootPaths, workspaceFolders, true, maxRepoSizeBytes, !useAutoBuildFeature)
4647

4748
let totalBytes = 0
4849
const ignoredExtensionMap = new Map<string, number>()
@@ -59,10 +60,10 @@ export async function prepareRepoData(
5960
throw error
6061
}
6162
const isCodeFile_ = isCodeFile(file.relativeFilePath)
62-
// exclude user's devfile if `useAutoBuildFeature` is set to false
63-
const excludeDevFile = useAutoBuildFeature ? false : file.relativeFilePath === 'devfile.yaml'
64-
65-
if (fileSize >= maxFileSizeBytes || !isCodeFile_ || excludeDevFile) {
63+
const isDevFile = file.relativeFilePath === 'devfile.yaml'
64+
// When useAutoBuildFeature is on, only respect the gitignore rules filtered earlier and apply the size limit, otherwise, exclude all non code files and gitignore files
65+
const isNonCodeFileAndIgnored = useAutoBuildFeature ? false : !isCodeFile_ || isDevFile
66+
if (fileSize >= maxFileSizeBytes || isNonCodeFileAndIgnored) {
6667
if (!isCodeFile_) {
6768
const re = /(?:\.([^.]+))?$/
6869
const extensionArray = re.exec(file.relativeFilePath)

packages/core/src/shared/filetypes.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -233,7 +233,6 @@ export const codefileExtensions = new Set([
233233
'.idl',
234234
'.ini',
235235
'.io',
236-
'.jar',
237236
'.java',
238237
'.jl',
239238
'.js',
@@ -362,7 +361,7 @@ export const codefileExtensions = new Set([
362361
])
363362

364363
// Code file names without an extension
365-
export const codefileNames = new Set(['Dockerfile', 'Dockerfile.build', 'gradlew', 'mvnw'])
364+
export const codefileNames = new Set(['Dockerfile', 'Dockerfile.build', 'gradlew', 'mvnw', '.gitignore'])
366365

367366
// Build file names
368367
export const buildfileNames = new Set(['gradle/wrapper/gradle-wrapper.jar'])

packages/core/src/shared/utilities/workspaceUtils.ts

Lines changed: 20 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -268,7 +268,7 @@ export function checkUnsavedChanges(): boolean {
268268
return vscode.workspace.textDocuments.some((doc) => doc.isDirty)
269269
}
270270

271-
export function getExcludePattern(additionalPatterns: string[] = []) {
271+
export function getExcludePattern(additionalPatterns: string[] = [], nonGlobalExtraPatterns: boolean = true) {
272272
const globAlwaysExcludedDirs = getGlobDirExcludedPatterns().map((pattern) => `**/${pattern}/*`)
273273
const extraPatterns = [
274274
'**/package-lock.json',
@@ -290,19 +290,28 @@ export function getExcludePattern(additionalPatterns: string[] = []) {
290290
'**/License.md',
291291
'**/LICENSE.md',
292292
]
293-
const allPatterns = [...globAlwaysExcludedDirs, ...extraPatterns, ...additionalPatterns]
293+
const allPatterns = [
294+
...globAlwaysExcludedDirs,
295+
...(nonGlobalExtraPatterns ? extraPatterns : []),
296+
...additionalPatterns,
297+
]
294298
return `{${allPatterns.join(',')}}`
295299
}
296300

297301
/**
298302
* @param rootPath root folder to look for .gitignore files
303+
* @param addExtraPatterns whether to add extra exclude patterns even if not in gitignore
299304
* @returns list of glob patterns extracted from .gitignore
300305
* These patterns are compatible with vscode exclude patterns
301306
*/
302-
async function filterOutGitignoredFiles(rootPath: string, files: vscode.Uri[]): Promise<vscode.Uri[]> {
307+
async function filterOutGitignoredFiles(
308+
rootPath: string,
309+
files: vscode.Uri[],
310+
addExtraPatterns: boolean = true
311+
): Promise<vscode.Uri[]> {
303312
const gitIgnoreFiles = await vscode.workspace.findFiles(
304313
new vscode.RelativePattern(rootPath, '**/.gitignore'),
305-
getExcludePattern()
314+
getExcludePattern([], addExtraPatterns)
306315
)
307316
const gitIgnoreFilter = await GitIgnoreFilter.build(gitIgnoreFiles)
308317
return gitIgnoreFilter.filterFiles(files)
@@ -313,13 +322,15 @@ async function filterOutGitignoredFiles(rootPath: string, files: vscode.Uri[]):
313322
* @param sourcePaths the paths where collection starts
314323
* @param workspaceFolders the current workspace folders opened
315324
* @param respectGitIgnore whether to respect gitignore file
325+
* @param addExtraIgnorePatterns whether to add extra exclude patterns even if not in gitignore
316326
* @returns all matched files
317327
*/
318328
export async function collectFiles(
319329
sourcePaths: string[],
320330
workspaceFolders: CurrentWsFolders,
321331
respectGitIgnore: boolean = true,
322-
maxSize = 200 * 1024 * 1024 // 200 MB
332+
maxSize = 200 * 1024 * 1024, // 200 MB
333+
addExtraIgnorePatterns: boolean = true
323334
): Promise<
324335
{
325336
workspaceFolder: vscode.WorkspaceFolder
@@ -356,10 +367,12 @@ export async function collectFiles(
356367
for (const rootPath of sourcePaths) {
357368
const allFiles = await vscode.workspace.findFiles(
358369
new vscode.RelativePattern(rootPath, '**'),
359-
getExcludePattern()
370+
getExcludePattern([], addExtraIgnorePatterns)
360371
)
361372

362-
const files = respectGitIgnore ? await filterOutGitignoredFiles(rootPath, allFiles) : allFiles
373+
const files = respectGitIgnore
374+
? await filterOutGitignoredFiles(rootPath, allFiles, addExtraIgnorePatterns)
375+
: allFiles
363376

364377
for (const file of files) {
365378
const relativePath = getWorkspaceRelativePath(file.fsPath, { workspaceFolders })

0 commit comments

Comments
 (0)