Skip to content

Commit

Permalink
fix(CodeTransform): smart select Java version (#4129)
Browse files Browse the repository at this point in the history
* fix(CodeTransform): smart select Java version

* add unit tests

* fix: fix if condition

* comment out tests

* fix: Java version selection bug

* remove print statements

* fix detekt issues

* fix detekt (again)

* fix detektMain task failure

* add back test

---------

Co-authored-by: David Hasani <[email protected]>
  • Loading branch information
dhasani23 and David Hasani authored Feb 13, 2024
1 parent 9aa26a3 commit 1aa0d2c
Show file tree
Hide file tree
Showing 13 changed files with 163 additions and 119 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"type" : "feature",
"description" : "CodeTransform: smart select Java version of project"
}
2 changes: 1 addition & 1 deletion gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ mockito = "5.10.0"
mockitoKotlin = "5.2.1"
mockk = "1.13.8"
node-gradle = "7.0.1"
telemetryGenerator = "1.0.182"
telemetryGenerator = "1.0.186"
testLogger = "3.1.0"
testRetry = "1.5.2"
# test-only; platform provides slf4j transitively at runtime. <233, 1.7.36; >=233, 2.0.9
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import software.aws.toolkits.jetbrains.core.coroutines.projectCoroutineScope
import software.aws.toolkits.jetbrains.services.codemodernizer.client.GumbyClient
import software.aws.toolkits.jetbrains.services.codemodernizer.model.CodeModernizerArtifact
import software.aws.toolkits.jetbrains.services.codemodernizer.model.JobId
import software.aws.toolkits.jetbrains.services.codemodernizer.state.CodeModernizerSessionState
import software.aws.toolkits.jetbrains.services.codemodernizer.state.CodeTransformTelemetryState
import software.aws.toolkits.jetbrains.services.codemodernizer.summary.CodeModernizerSummaryEditorProvider
import software.aws.toolkits.jetbrains.utils.notifyInfo
Expand Down Expand Up @@ -147,13 +148,15 @@ class ArtifactHandler(private val project: Project, private val clientAdaptor: G
if (dialog.showAndGet()) {
CodetransformTelemetry.vcsViewerSubmitted(
codeTransformSessionId = CodeTransformTelemetryState.instance.getSessionId(),
codeTransformJobId = jobId.id
codeTransformJobId = jobId.id,
codeTransformStatus = CodeModernizerSessionState.getInstance(project).currentJobStatus.toString()
)
} else {
CodetransformTelemetry.vcsViewerCanceled(
codeTransformPatchViewerCancelSrcComponents = CodeTransformPatchViewerCancelSrcComponents.CancelButton,
codeTransformSessionId = CodeTransformTelemetryState.instance.getSessionId(),
codeTransformJobId = jobId.id
codeTransformJobId = jobId.id,
codeTransformStatus = CodeModernizerSessionState.getInstance(project).currentJobStatus.toString()
)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,12 @@ import com.intellij.notification.NotificationAction
import com.intellij.openapi.Disposable
import com.intellij.openapi.application.ApplicationManager
import com.intellij.openapi.application.runInEdt
import com.intellij.openapi.application.runReadAction
import com.intellij.openapi.components.PersistentStateComponent
import com.intellij.openapi.components.RoamingType
import com.intellij.openapi.components.State
import com.intellij.openapi.components.Storage
import com.intellij.openapi.components.service
import com.intellij.openapi.project.Project
import com.intellij.openapi.project.modules
import com.intellij.openapi.projectRoots.JavaSdkVersion
import com.intellij.openapi.roots.ProjectRootManager
import com.intellij.openapi.util.Disposer
import com.intellij.openapi.vfs.VirtualFile
Expand Down Expand Up @@ -70,7 +67,6 @@ import software.aws.toolkits.telemetry.CodeTransformPreValidationError
import software.aws.toolkits.telemetry.CodeTransformStartSrcComponents
import software.aws.toolkits.telemetry.CodetransformTelemetry
import software.aws.toolkits.telemetry.Result
import java.io.File
import java.time.Instant
import java.util.concurrent.atomic.AtomicBoolean
import javax.swing.Icon
Expand All @@ -91,10 +87,6 @@ class CodeModernizerManager(private val project: Project) : PersistentStateCompo
}
}
private val supportedBuildFileNames = listOf(MAVEN_CONFIGURATION_FILE_NAME)
private val supportedJavaMappings = mapOf(
JavaSdkVersion.JDK_1_8 to setOf(JavaSdkVersion.JDK_17),
JavaSdkVersion.JDK_11 to setOf(JavaSdkVersion.JDK_17),
)
private val isModernizationInProgress = AtomicBoolean(false)
private val isResumingJob = AtomicBoolean(false)

Expand Down Expand Up @@ -142,19 +134,7 @@ class CodeModernizerManager(private val project: Project) : PersistentStateCompo
)
)
}
val supportedModules = getSupportedModulesInProject().toSet()
val validProjectJdk = project.getSupportedJavaMappingsForProject(supportedJavaMappings).isNotEmpty()
if (supportedModules.isEmpty() && !validProjectJdk) {
return ValidationResult(
false,
message("codemodernizer.notification.warn.invalid_project.description.reason.invalid_jdk_versions", supportedJavaMappings.keys.joinToString()),
InvalidTelemetryReason(
CodeTransformPreValidationError.UnsupportedJavaVersion,
project.tryGetJdk().toString()
)
)
}
val validatedBuildFiles = getSupportedBuildFilesInProject()
val validatedBuildFiles = project.getSupportedBuildModules(supportedBuildFileNames)
return if (validatedBuildFiles.isNotEmpty()) {
ValidationResult(true, validatedBuildFiles = validatedBuildFiles)
} else {
Expand Down Expand Up @@ -275,7 +255,6 @@ class CodeModernizerManager(private val project: Project) : PersistentStateCompo
fun getCustomerSelection(validatedBuildFiles: List<VirtualFile>): CustomerSelection? = PreCodeTransformUserDialog(
project,
validatedBuildFiles,
supportedJavaMappings,
).create()

private fun notifyJobFailure(failureReason: String?, retryable: Boolean) {
Expand Down Expand Up @@ -442,7 +421,9 @@ class CodeModernizerManager(private val project: Project) : PersistentStateCompo
fun tryResumeJob() = projectCoroutineScope(project).launch {
try {
val notYetResumed = isResumingJob.compareAndSet(false, true)
if (!notYetResumed) return@launch
if (!notYetResumed) {
return@launch
}

LOG.info { "Attempting to resume job, current state is: $managerState" }
if (!managerState.flags.getOrDefault(StateFlags.IS_ONGOING, false)) return@launch
Expand Down Expand Up @@ -649,57 +630,6 @@ class CodeModernizerManager(private val project: Project) : PersistentStateCompo
managerState.flags.putAll(state.flags)
}

private fun findBuildFiles(sourceFolder: File): List<File> {
/**
* For every dir, check if any supported build files (pom.xml etc) exists.
* If found store it and stop further recursion.
*/
val buildFiles = mutableListOf<File>()
sourceFolder.walkTopDown()
.maxDepth(5)
.onEnter { currentDir ->
supportedBuildFileNames.forEach {
val maybeSupportedFile = currentDir.resolve(MAVEN_CONFIGURATION_FILE_NAME)
if (maybeSupportedFile.exists()) {
buildFiles.add(maybeSupportedFile)
return@onEnter false
}
}
return@onEnter true
}.forEach {
// noop, collects the sequence
}
return buildFiles
}

private fun getSupportedBuildFilesInProject(): List<VirtualFile> {
/**
* Strategy:
* 1. Find folders with pom.xml or build.gradle.kts or build.gradle
* 2. Filter out subdirectories
*/
val projectRootManager = ProjectRootManager.getInstance(project)
val probableProjectRoot = project.basePath?.toVirtualFile() // May point to only one intellij module (the first opened one)
val probableContentRoots = projectRootManager.contentRoots.toMutableSet() // May not point to the topmost folder of modules
probableContentRoots.add(probableProjectRoot) // dedupe
val topLevelRoots = filterOnlyParentFiles(probableContentRoots)
val detectedBuildFiles = topLevelRoots.flatMap { root ->
findBuildFiles(root.toNioPath().toFile()).mapNotNull { it.path.toVirtualFile() }
}

val supportedModules = getSupportedModulesInProject().toSet()
val validProjectJdk = project.getSupportedJavaMappingsForProject(supportedJavaMappings).isNotEmpty()
return detectedBuildFiles.filter {
val moduleOfFile = runReadAction { projectRootManager.fileIndex.getModuleForFile(it) }
return@filter (moduleOfFile in supportedModules) || (moduleOfFile == null && validProjectJdk)
}
}

private fun getSupportedModulesInProject() = project.modules.filter {
val moduleJdk = it.tryGetJdk(project) ?: return@filter false
moduleJdk in supportedJavaMappings
}

fun warnUnsupportedProject(reason: String?) {
addCodeModernizeUI(true)
val maybeUnknownReason = reason ?: message("codemodernizer.notification.warn.invalid_project.description.reason.unknown")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ import java.util.concurrent.CancellationException
import java.util.concurrent.atomic.AtomicBoolean

const val ZIP_SOURCES_PATH = "sources"
const val BUILD_LOG_PATH = "build-logs.txt"
const val UPLOAD_ZIP_MANIFEST_VERSION = 1.0F

class CodeModernizerSession(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import com.intellij.execution.util.ExecUtil
import com.intellij.openapi.application.ApplicationInfo
import com.intellij.openapi.module.ModuleUtil
import com.intellij.openapi.project.Project
import com.intellij.openapi.projectRoots.JavaSdkVersion
import com.intellij.openapi.util.SystemInfo
import com.intellij.openapi.vfs.VfsUtilCore
import com.intellij.openapi.vfs.VirtualFile
Expand Down Expand Up @@ -44,9 +45,13 @@ import software.aws.toolkits.jetbrains.core.gettingstarted.editor.ActiveConnecti
import software.aws.toolkits.jetbrains.core.gettingstarted.editor.BearerTokenFeatureSet
import software.aws.toolkits.jetbrains.core.gettingstarted.editor.checkBearerConnectionValidity
import software.aws.toolkits.jetbrains.services.codemodernizer.client.GumbyClient
import software.aws.toolkits.jetbrains.services.codemodernizer.constants.CodeModernizerUIConstants
import software.aws.toolkits.jetbrains.services.codemodernizer.model.JobId
import software.aws.toolkits.jetbrains.services.codemodernizer.model.MAVEN_CONFIGURATION_FILE_NAME
import software.aws.toolkits.jetbrains.services.codemodernizer.state.CodeTransformTelemetryState
import software.aws.toolkits.resources.message
import software.aws.toolkits.telemetry.CodetransformTelemetry
import java.io.File
import java.io.FileOutputStream
import java.lang.Thread.sleep
import java.nio.file.Path
Expand Down Expand Up @@ -127,6 +132,17 @@ fun String.toTransformationLanguage() = when (this) {
else -> TransformationLanguage.UNKNOWN_TO_SDK_VERSION
}

fun getJdkVersionText(version: JavaSdkVersion?): String {
val jdkVersionText: String = if (version == null) {
message("codemodernizer.customerselectiondialog.unknown_jdk")
} else if (CodeModernizerUIConstants.supportedSourceJDKs.contains(version)) { // detected java version is supported
message("codemodernizer.customerselectiondialog.found_supported_jdk", version)
} else {
message("codemodernizer.customerselectiondialog.found_unsupported_jdk", version) // found the version, but unsupported
}
return jdkVersionText
}

fun calculateTotalLatency(startTime: Instant, endTime: Instant) = (endTime.toEpochMilli() - startTime.toEpochMilli()).toInt()

data class PollingResult(
Expand Down Expand Up @@ -247,6 +263,29 @@ fun filterOnlyParentFiles(filePaths: Set<VirtualFile>): List<VirtualFile> {
return shortestRoots.toList()
}

/**
* @description For every directory, check if any supported build files (pom.xml etc) exists.
* If we find a valid build file, store it and stop further recursion.
*/
fun findBuildFiles(sourceFolder: File, supportedBuildFileNames: List<String>): List<File> {
val buildFiles = mutableListOf<File>()
sourceFolder.walkTopDown()
.maxDepth(5)
.onEnter { currentDir ->
supportedBuildFileNames.forEach {
val maybeSupportedFile = currentDir.resolve(MAVEN_CONFIGURATION_FILE_NAME)
if (maybeSupportedFile.exists()) {
buildFiles.add(maybeSupportedFile)
return@onEnter false
}
}
return@onEnter true
}.forEach {
// noop, collects the sequence
}
return buildFiles
}

fun isIntellij(): Boolean {
val productCode = ApplicationInfo.getInstance().build.productCode
return productCode == "IC" || productCode == "IU"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,22 +12,35 @@ import com.intellij.openapi.roots.ModuleRootManager
import com.intellij.openapi.roots.ProjectRootManager
import com.intellij.openapi.vfs.VirtualFile

/**
* @description Try to get the module SDK version and fallback to the project SDK version from the "project structure" settings.
*/
fun Module.tryGetJdk(project: Project): JavaSdkVersion? {
val sdk = ModuleRootManager.getInstance(this).sdk ?: ProjectRootManager.getInstance(project).projectSdk ?: return null
val javaSdk = JavaSdkImpl.getInstance()
return javaSdk.getVersion(sdk)
}

fun Project.getSupportedJavaMappingsForProject(supportedJavaMappings: Map<JavaSdkVersion, Set<JavaSdkVersion>>): List<String> {
val projectSdk = ProjectRootManager.getInstance(this).projectSdk
val javaSdk = JavaSdkImpl.getInstance()
return if (projectSdk == null) {
listOf()
} else {
supportedJavaMappings.getOrDefault(javaSdk.getVersion(projectSdk), listOf()).map { it.name }.toList()
/**
* @description Strategy:
* 1. Find folders with pom.xml or build.gradle.kts or build.gradle
* 2. Filter out subdirectories
*/
fun Project.getSupportedBuildModules(supportedBuildFileNames: List<String>): List<VirtualFile> {

This comment has been minimized.

Copy link
@dhasani23

dhasani23 Feb 14, 2024

Author Contributor

@damntrecky what was the purpose of adding this? In Leo's latest PR this gets reverted, which I'm assuming is safe to do: https://github.com/aws/aws-toolkit-jetbrains/pull/4135/files#r1490199366

This comment has been minimized.

Copy link
@dhasani23

dhasani23 Feb 14, 2024

Author Contributor

EDIT: sorry, it does not get reverted, it just got moved

val projectRootManager = ProjectRootManager.getInstance(this)
val probableProjectRoot = this.basePath?.toVirtualFile() // May point to only one intellij module (the first opened one)
val probableContentRoots = projectRootManager.contentRoots.toMutableSet() // May not point to the topmost folder of modules
probableContentRoots.add(probableProjectRoot) // dedupe
val topLevelRoots = filterOnlyParentFiles(probableContentRoots)
val detectedBuildFiles = topLevelRoots.flatMap { root ->
findBuildFiles(root.toNioPath().toFile(), supportedBuildFileNames).mapNotNull { it.path.toVirtualFile() }
}
return detectedBuildFiles
}

/**
* @description Try to get the project SDK version from the "project structure" settings
*/
fun Project.tryGetJdk(): JavaSdkVersion? {
val projectSdk = ProjectRootManager.getInstance(this).projectSdk
val javaSdk = JavaSdkImpl.getInstance()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

package software.aws.toolkits.jetbrains.services.codemodernizer.constants

import com.intellij.openapi.projectRoots.JavaSdkVersion
import com.intellij.ui.JBColor
import icons.AwsIcons
import software.aws.toolkits.jetbrains.services.codewhisperer.util.CodeWhispererColorUtil
Expand Down Expand Up @@ -54,6 +55,7 @@ class CodeModernizerUIConstants {
}

companion object {
val supportedSourceJDKs = listOf(JavaSdkVersion.JDK_1_8, JavaSdkVersion.JDK_11)
const val SINGLE_SPACE_STRING: String = " "
const val EMPTY_SPACE_STRING: String = ""
val transformationPlanPlaneConstraint = GridBagConstraints().apply {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -176,13 +176,15 @@ data class CodeModernizerSessionContext(
val installCommandList = listOf(
"clean",
"install",
"-q",
)
val copyCommandList = listOf(
"dependency:copy-dependencies",
"-DoutputDirectory=$destinationDir",
"-Dmdep.useRepositoryLayout=true",
"-Dmdep.copyPom=true",
"-Dmdep.addParentPoms=true"
"-Dmdep.addParentPoms=true",
"-q",
)
fun runInstallCommand(mavenCommand: String): ProcessOutput {
buildlogBuilder.appendLine("Command Run: $mavenCommand clean install")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,13 @@

package software.aws.toolkits.jetbrains.services.codemodernizer.model

import software.aws.toolkits.jetbrains.services.codemodernizer.BUILD_LOG_PATH
import software.aws.toolkits.jetbrains.services.codemodernizer.UPLOAD_ZIP_MANIFEST_VERSION
import software.aws.toolkits.jetbrains.services.codemodernizer.ZIP_SOURCES_PATH

data class ZipManifest(
val sourcesRoot: String = ZIP_SOURCES_PATH,
val dependenciesRoot: String? = null,
val buildLogs: String = BUILD_LOG_PATH,
val version: String = UPLOAD_ZIP_MANIFEST_VERSION.toString()
)
Loading

0 comments on commit 1aa0d2c

Please sign in to comment.