in packages/core/src/amazonq/util/files.ts [56:186]
export async function prepareRepoData(
repoRootPaths: string[],
workspaceFolders: CurrentWsFolders,
span: Span<AmazonqCreateUpload>,
options?: PrepareRepoDataOptions
) {
try {
const telemetry = options?.telemetry
const isIncludeInfraDiagram = options?.isIncludeInfraDiagram ?? false
const zip = options?.zip ?? new ZipStream()
const autoBuildSetting = CodeWhispererSettings.instance.getAutoBuildSetting()
const useAutoBuildFeature = autoBuildSetting[repoRootPaths[0]] ?? false
const excludePatterns: string[] = []
let filterFn: CollectFilesFilter | undefined = undefined
// 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)
if (!useAutoBuildFeature) {
if (isIncludeInfraDiagram) {
// ensure svg is not filtered out by files search
excludePatterns.push(...defaultExcludePatterns.filter((p) => !p.endsWith(SvgFileExtension)))
// ensure only infra diagram is included from all svg files
filterFn = (relativePath: string) => {
if (!relativePath.toLowerCase().endsWith(SvgFileExtension)) {
return false
}
return !isInfraDiagramFile(relativePath)
}
} else {
excludePatterns.push(...defaultExcludePatterns)
}
}
const files = await collectFiles(repoRootPaths, workspaceFolders, {
maxTotalSizeBytes: maxRepoSizeBytes,
excludeByGitIgnore: true,
excludePatterns: excludePatterns,
filterFn: filterFn,
})
let totalBytes = 0
const ignoredExtensionMap = new Map<string, number>()
for (const file of files) {
let fileSize
try {
fileSize = (await fs.stat(file.fileUri)).size
} catch (error) {
if (hasCode(error) && error.code === 'ENOENT') {
// No-op: Skip if file does not exist
continue
}
throw error
}
const isCodeFile_ = isCodeFile(file.relativeFilePath)
const isDevFile = file.relativeFilePath === 'devfile.yaml'
const isInfraDiagramFileExt = isInfraDiagramFile(file.relativeFilePath)
let isExcludeFile = fileSize >= maxFileSizeBytes
// When useAutoBuildFeature is on, only respect the gitignore rules filtered earlier and apply the size limit
if (!isExcludeFile && !useAutoBuildFeature) {
isExcludeFile = isDevFile || (!isCodeFile_ && (!isIncludeInfraDiagram || !isInfraDiagramFileExt))
}
if (isExcludeFile) {
if (!isCodeFile_) {
const re = /(?:\.([^.]+))?$/
const extensionArray = re.exec(file.relativeFilePath)
const extension = extensionArray?.length ? extensionArray[1] : undefined
if (extension) {
const currentCount = ignoredExtensionMap.get(extension)
ignoredExtensionMap.set(extension, (currentCount ?? 0) + 1)
}
}
continue
}
totalBytes += fileSize
// Paths in zip should be POSIX compliant regardless of OS
// Reference: https://pkware.cachefly.net/webdocs/casestudies/APPNOTE.TXT
const posixPath = file.zipFilePath.split(path.sep).join(path.posix.sep)
try {
zip.writeFile(file.fileUri.fsPath, posixPath)
} catch (error) {
if (error instanceof Error && error.message.includes('File not found')) {
// No-op: Skip if file was deleted or does not exist
// Reference: https://github.com/cthackers/adm-zip/blob/1cd32f7e0ad3c540142a76609bb538a5cda2292f/adm-zip.js#L296-L321
continue
}
throw error
}
}
const iterator = ignoredExtensionMap.entries()
for (let i = 0; i < ignoredExtensionMap.size; i++) {
const iteratorValue = iterator.next().value
if (iteratorValue) {
const [key, value] = iteratorValue
await amznTelemetry.amazonq_bundleExtensionIgnored.run(async (bundleSpan) => {
const event = {
filenameExt: key,
count: value,
}
bundleSpan.record(event)
})
}
}
if (telemetry) {
telemetry.setRepositorySize(totalBytes)
}
span.record({ amazonqRepositorySize: totalBytes })
const zipResult = await zip.finalize()
const zipFileBuffer = zipResult.streamBuffer.getContents() || Buffer.from('')
return {
zipFileBuffer,
zipFileChecksum: zipResult.hash,
}
} catch (error) {
getLogger().debug(`Failed to prepare repo: ${error}`)
if (error instanceof ToolkitError && error.code === 'ContentLengthError') {
throw new ContentLengthError(error.message)
}
throw new PrepareRepoFailedError()
}
}