Skip to content

Commit ec81010

Browse files
authored
feat: Allow including binary files when auto build is enabled (#5299)
* feat: Allow including binary files when auto build is enabled * attempt fixing test failures on windows * fix windows tests
1 parent e3f7adb commit ec81010

File tree

4 files changed

+104
-62
lines changed

4 files changed

+104
-62
lines changed

plugins/amazonq/chat/jetbrains-community/tst/software/aws/toolkits/jetbrains/services/amazonqFeatureDev/FeatureDevSessionContextTest.kt

Lines changed: 51 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
import com.intellij.openapi.vfs.VirtualFile
55
import com.intellij.testFramework.RuleChain
6+
import org.junit.Assert.assertEquals
67
import org.junit.Assert.assertFalse
78
import org.junit.Assert.assertTrue
89
import org.junit.Before
@@ -72,7 +73,51 @@ class FeatureDevSessionContextTest : FeatureDevTestBase(HeavyJavaCodeInsightTest
7273
}
7374

7475
@Test
75-
fun testZipProject() {
76+
fun testZipProjectWithoutAutoDev() {
77+
checkZipProject(
78+
false,
79+
setOf(
80+
"src/MyClass.java",
81+
"gradlew",
82+
"gradlew.bat",
83+
"README.md",
84+
"gradle/wrapper/gradle-wrapper.properties",
85+
"builder/GetTestBuilder.java",
86+
"settings.gradle",
87+
"build.gradle",
88+
".gitignore",
89+
)
90+
)
91+
}
92+
93+
@Test
94+
fun testZipProjectWithAutoDev() {
95+
checkZipProject(
96+
true,
97+
setOf(
98+
"src/MyClass.java",
99+
"icons/menu.svg",
100+
"assets/header.jpg",
101+
"gradle/wrapper/gradle-wrapper.jar",
102+
"gradle/wrapper/gradle-wrapper.properties",
103+
"images/logo.png",
104+
"builder/GetTestBuilder.java",
105+
"gradlew",
106+
"README.md",
107+
".gitignore",
108+
"License.md",
109+
"output.bin",
110+
"archive.zip",
111+
"gradlew.bat",
112+
"license.txt",
113+
"build.gradle",
114+
"devfile.yaml",
115+
"settings.gradle"
116+
)
117+
)
118+
}
119+
120+
fun checkZipProject(autoBuildEnabled: Boolean, expectedFiles: Set<String>) {
76121
addFilesToProjectModule(
77122
".gitignore",
78123
".gradle/cached.jar",
@@ -97,9 +142,12 @@ class FeatureDevSessionContextTest : FeatureDevTestBase(HeavyJavaCodeInsightTest
97142
"build/outputs",
98143
"dist/bundle.js",
99144
"gradle/wrapper/gradle-wrapper.jar",
145+
"devfile.yaml",
100146
)
101147

102-
val zipResult = featureDevSessionContext.getProjectZip(false)
148+
projectRule.fixture.addFileToModule(module, "large-file.txt", "loblob".repeat(1024 * 1024))
149+
150+
val zipResult = featureDevSessionContext.getProjectZip(autoBuildEnabled)
103151
val zipPath = zipResult.payload.path
104152

105153
val zippedFiles = mutableSetOf<String>()
@@ -111,19 +159,6 @@ class FeatureDevSessionContextTest : FeatureDevTestBase(HeavyJavaCodeInsightTest
111159
}
112160
}
113161

114-
val expectedFiles = setOf(
115-
"src/MyClass.java",
116-
"gradlew",
117-
"gradlew.bat",
118-
"README.md",
119-
"gradle/wrapper/gradle-wrapper.properties",
120-
"builder/GetTestBuilder.java",
121-
"settings.gradle",
122-
"build.gradle",
123-
"gradle/wrapper/gradle-wrapper.jar",
124-
".gitignore",
125-
)
126-
127-
assertTrue(zippedFiles == expectedFiles)
162+
assertEquals(zippedFiles, expectedFiles)
128163
}
129164
}

plugins/core/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/FeatureDevSessionContext.kt

Lines changed: 52 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -10,14 +10,14 @@ import com.intellij.openapi.vfs.VirtualFile
1010
import com.intellij.openapi.vfs.VirtualFileVisitor
1111
import com.intellij.openapi.vfs.isFile
1212
import com.intellij.platform.ide.progress.withBackgroundProgress
13-
import kotlinx.coroutines.async
1413
import kotlinx.coroutines.flow.channelFlow
1514
import kotlinx.coroutines.launch
1615
import kotlinx.coroutines.runBlocking
1716
import kotlinx.coroutines.withContext
1817
import org.apache.commons.codec.digest.DigestUtils
1918
import org.apache.commons.io.FileUtils
2019
import software.aws.toolkits.jetbrains.core.coroutines.getCoroutineBgContext
20+
import software.aws.toolkits.jetbrains.services.amazonq.QConstants.MAX_FILE_SIZE_BYTES
2121
import software.aws.toolkits.jetbrains.services.telemetry.ALLOWED_CODE_EXTENSIONS
2222
import software.aws.toolkits.jetbrains.utils.isDevFile
2323
import software.aws.toolkits.resources.AwsCoreBundle
@@ -33,7 +33,6 @@ import java.nio.file.Paths
3333
import java.nio.file.StandardCopyOption
3434
import java.util.Base64
3535
import java.util.UUID
36-
import kotlin.coroutines.coroutineContext
3736
import kotlin.io.path.Path
3837
import kotlin.io.path.createParentDirectories
3938
import kotlin.io.path.getPosixFilePermissions
@@ -47,8 +46,7 @@ class RepoSizeLimitError(override val message: String) : RuntimeException(), Rep
4746
class FeatureDevSessionContext(val project: Project, val maxProjectSizeBytes: Long? = null) {
4847
// TODO: Need to correct this class location in the modules going further to support both amazonq and codescan.
4948

50-
private val requiredFilesForExecution = setOf("gradle/wrapper/gradle-wrapper.jar")
51-
private val additionalGitIgnoreRules = setOf(
49+
private val additionalGitIgnoreFolderRules = setOf(
5250
".aws-sam",
5351
".gem",
5452
".git",
@@ -58,6 +56,12 @@ class FeatureDevSessionContext(val project: Project, val maxProjectSizeBytes: Lo
5856
".project",
5957
".rvm",
6058
".svn",
59+
"node_modules",
60+
"build",
61+
"dist",
62+
)
63+
64+
private val additionalGitIgnoreBinaryFilesRules = setOf(
6165
"*.zip",
6266
"*.bin",
6367
"*.png",
@@ -70,9 +74,6 @@ class FeatureDevSessionContext(val project: Project, val maxProjectSizeBytes: Lo
7074
"license.md",
7175
"License.md",
7276
"LICENSE.md",
73-
"node_modules",
74-
"build",
75-
"dist"
7677
)
7778

7879
// well known source files that do not have extensions
@@ -90,12 +91,17 @@ class FeatureDevSessionContext(val project: Project, val maxProjectSizeBytes: Lo
9091
// selectedSourceFolder: is the directory selected in replacement of the root, this happens when the project is too big to bundle for uploading.
9192
private var _selectedSourceFolder = projectRoot
9293
private var ignorePatternsWithGitIgnore = emptyList<Regex>()
94+
private var ignorePatternsForBinaryFiles = additionalGitIgnoreBinaryFilesRules
95+
.map { convertGitIgnorePatternToRegex(it) }
96+
.mapNotNull { pattern ->
97+
runCatching { Regex(pattern) }.getOrNull()
98+
}
9399
private val gitIgnoreFile = File(selectedSourceFolder.path, ".gitignore")
94100

95101
init {
96102
ignorePatternsWithGitIgnore = try {
97103
buildList {
98-
addAll(additionalGitIgnoreRules.map { convertGitIgnorePatternToRegex(it) })
104+
addAll(additionalGitIgnoreFolderRules.map { convertGitIgnorePatternToRegex(it) })
99105
addAll(parseGitIgnore())
100106
}.mapNotNull { pattern ->
101107
runCatching { Regex(pattern) }.getOrNull()
@@ -130,35 +136,49 @@ class FeatureDevSessionContext(val project: Project, val maxProjectSizeBytes: Lo
130136
return ALLOWED_CODE_EXTENSIONS.contains(extension)
131137
}
132138

133-
private fun ignoreFileByExtension(file: VirtualFile) =
134-
!isFileExtensionAllowed(file)
139+
fun ignoreFile(file: VirtualFile, applyExtraBinaryFilesRules: Boolean = true): Boolean = ignoreFile(file.presentableUrl, applyExtraBinaryFilesRules)
140+
141+
fun ignoreFile(path: String, applyExtraBinaryFilesRules: Boolean = true): Boolean {
142+
val allIgnoreRules = if (applyExtraBinaryFilesRules) ignorePatternsWithGitIgnore + ignorePatternsForBinaryFiles else ignorePatternsWithGitIgnore
143+
val matchedRules = allIgnoreRules.map { pattern ->
144+
// avoid partial match (pattern.containsMatchIn) since it causes us matching files
145+
// against folder patterns. (e.g. settings.gradle ignored by .gradle rule!)
146+
// we convert the glob rules to regex, add a trailing /* to all rules and then match
147+
// entries against them by adding a trailing /.
148+
// TODO: Add unit tests for gitignore matching
149+
val relative = if (path.startsWith(projectRootPath.toString())) Paths.get(path).relativeTo(projectRootPath) else path
150+
pattern.matches("$relative/")
151+
}
152+
return matchedRules.any { it }
153+
}
135154

136-
suspend fun ignoreFile(file: VirtualFile): Boolean = ignoreFile(file.presentableUrl)
155+
private fun wellKnown(file: VirtualFile): Boolean = wellKnownSourceFiles.contains(file.name)
137156

138-
suspend fun ignoreFile(path: String): Boolean {
139-
if (requiredFilesForExecution.any { path.endsWith(it) }) {
157+
private fun shouldIncludeInZipFile(file: VirtualFile, isAutoBuildFeatureEnabled: Boolean): Boolean {
158+
// large files always ignored
159+
if (file.length > MAX_FILE_SIZE_BYTES) {
140160
return false
141161
}
142-
// this method reads like something a JS dev would write and doesn't do what the author thinks
143-
val deferredResults = ignorePatternsWithGitIgnore.map { pattern ->
144-
withContext(coroutineContext) {
145-
// avoid partial match (pattern.containsMatchIn) since it causes us matching files
146-
// against folder patterns. (e.g. settings.gradle ignored by .gradle rule!)
147-
// we convert the glob rules to regex, add a trailing /* to all rules and then match
148-
// entries against them by adding a trailing /.
149-
// TODO: Add unit tests for gitignore matching
150-
val relative = if (path.startsWith(projectRootPath.toString())) Paths.get(path).relativeTo(projectRootPath) else path
151-
async { pattern.matches("$relative/") }
152-
}
162+
163+
// always respect gitignore rules and remove binary files if auto build is disabled
164+
val isFileIgnoredByPattern = ignoreFile(file, !isAutoBuildFeatureEnabled)
165+
if (isFileIgnoredByPattern) {
166+
return false
153167
}
154168

155-
// this will serially iterate over and block
156-
// ideally we race the results https://github.com/Kotlin/kotlinx.coroutines/issues/2867
157-
// i.e. Promise.any(...)
158-
return deferredResults.any { it.await() }
159-
}
169+
// all other files are included when auto build enabled
170+
if (isAutoBuildFeatureEnabled) {
171+
return true
172+
}
173+
174+
// when auto build is disabled, only include files with well known extensions and names except "devfile.yam"
175+
if (!isDevFile(file) && (wellKnown(file) || isFileExtensionAllowed(file))) {
176+
return true
177+
}
160178

161-
fun wellKnown(file: VirtualFile): Boolean = wellKnownSourceFiles.contains(file.name)
179+
// Any other files should not be included
180+
return false
181+
}
162182

163183
suspend fun zipFiles(projectRoot: VirtualFile, isAutoBuildFeatureEnabled: Boolean?): File = withContext(getCoroutineBgContext()) {
164184
val files = mutableListOf<VirtualFile>()
@@ -169,26 +189,13 @@ class FeatureDevSessionContext(val project: Project, val maxProjectSizeBytes: Lo
169189
projectRoot,
170190
object : VirtualFileVisitor<Unit>() {
171191
override fun visitFile(file: VirtualFile): Boolean {
172-
val isWellKnown = runBlocking { wellKnown(file) }
173-
val isFileIgnoredByExtension = runBlocking { ignoreFileByExtension(file) }
174-
// if `isAutoBuildFeatureEnabled` is false, then filter devfile
175-
val isFilterDevFile = if (isAutoBuildFeatureEnabled == true) false else isDevFile(file)
176-
177-
if (!isWellKnown && isFileIgnoredByExtension) {
192+
val isIncluded = shouldIncludeInZipFile(file, isAutoBuildFeatureEnabled == true)
193+
if (!isIncluded) {
178194
val extension = file.extension.orEmpty()
179195
ignoredExtensionMap[extension] = (ignoredExtensionMap[extension] ?: 0) + 1
180196
return false
181197
}
182198

183-
if (isFilterDevFile) {
184-
return false
185-
}
186-
187-
val isFileIgnoredByPattern = runBlocking { ignoreFile(file.name) }
188-
if (isFileIgnoredByPattern) {
189-
return false
190-
}
191-
192199
if (file.isFile) {
193200
totalSize += file.length
194201
files.add(file)

plugins/core/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/QConstants.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,4 +6,5 @@ package software.aws.toolkits.jetbrains.services.amazonq
66
object QConstants {
77
const val Q_MARKETPLACE_URI = "https://aws.amazon.com/q/developer/"
88
const val CODEWHISPERER_LOGIN_HELP_URI = "https://docs.aws.amazon.com/toolkit-for-jetbrains/latest/userguide/auth-access.html"
9+
const val MAX_FILE_SIZE_BYTES = 1024 * 1024
910
}

plugins/core/jetbrains-community/src/software/aws/toolkits/jetbrains/services/telemetry/TelemetryUtils.kt

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,6 @@ val ALLOWED_CODE_EXTENSIONS = setOf(
8888
"idl",
8989
"ini",
9090
"io",
91-
"jar",
9291
"java",
9392
"jl",
9493
"js",

0 commit comments

Comments
 (0)