Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ dependencies {
implementation(project(":azure-intellij-plugin-lib-java"))
// runtimeOnly project(path: ":azure-intellij-plugin-lib", configuration: "instrumentedJar")
implementation("com.microsoft.azure:azure-toolkit-ide-common-lib")
testImplementation("org.mockito:mockito-core:5.20.0")
intellijPlatform {
// Plugin Dependencies. Uses `platformBundledPlugins` property from the gradle.properties file for bundled IntelliJ Platform plugins.
bundledPlugin("com.intellij.java")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
import kotlin.Unit;
import kotlin.coroutines.Continuation;
import lombok.extern.slf4j.Slf4j;
import org.jetbrains.idea.maven.project.MavenProjectsManager;
import reactor.core.publisher.Mono;

import javax.annotation.Nonnull;
Expand All @@ -39,6 +40,16 @@ public class JavaUpgradeCheckStartupActivity implements ProjectActivity, DumbAwa

@Override
public Object execute(@Nonnull Project project, @Nonnull Continuation<? super Unit> continuation) {
MavenProjectsManager.getInstance(project).addManagerListener(
new MavenProjectsManager.Listener() {
@Override
public void projectImportCompleted() {
performJavaUpgradeCheck(project, false);
}
},
project
);

// Wait for indexing to complete before running the check
DumbService.getInstance(project).runWhenSmart(() -> {
// Add a small delay after smart mode to ensure Maven/Gradle sync is done
Expand All @@ -48,7 +59,7 @@ public Object execute(@Nonnull Project project, @Nonnull Continuation<? super Un
if (project.isDisposed()) {
return;
}
performJavaUpgradeCheck(project);
performJavaUpgradeCheck(project, true);
},
error -> {
/* Error during Java upgrade check startup */
Expand All @@ -63,7 +74,7 @@ public Object execute(@Nonnull Project project, @Nonnull Continuation<? super Un
/**
* Performs the jdk version, framework version and CVE issue check and shows notifications for any issues found.
*/
private void performJavaUpgradeCheck(@Nonnull Project project) {
private void performJavaUpgradeCheck(@Nonnull Project project, boolean showNotification) {
try {
log.info("Starting Java upgrade issues detection for project: {}", project.getName());
// Run the analysis in a background thread
Expand Down Expand Up @@ -99,7 +110,7 @@ private void performJavaUpgradeCheck(@Nonnull Project project) {
DaemonCodeAnalyzer.getInstance(project).restart();

// Show notifications if there are issues
if (!allIssues.isEmpty()) {
if (showNotification && !allIssues.isEmpty()) {
final JavaVersionNotificationService notificationService = JavaVersionNotificationService.getInstance();
notificationService.showNotifications(project, allIssues);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,9 @@

import java.util.Map;

import static com.microsoft.azure.toolkit.intellij.appmod.javaupgrade.utils.Constants.APPMOD_CVE_AGENT_NAME;
import static com.microsoft.azure.toolkit.intellij.appmod.javaupgrade.utils.Constants.APPMOD_UPGRADE_AGENT_NAME;
import static com.microsoft.azure.toolkit.intellij.appmod.javaupgrade.utils.Constants.FIX_VULNERABLE_DEPENDENCY_WITH_COPILOT_PROMPT;
import static com.microsoft.azure.toolkit.intellij.appmod.javaupgrade.utils.Constants.UPGRADE_JAVA_FRAMEWORK_PROMPT;

/**
Expand Down Expand Up @@ -56,14 +58,23 @@ public String getName() {
public void applyFix(@NotNull Project project, @NotNull ProblemDescriptor descriptor) {
try {
String prompt = buildPromptForIssue(issue);
JavaVersionNotificationService.getInstance().openCopilotChatWithPrompt(project, prompt, APPMOD_UPGRADE_AGENT_NAME);
final String agentName = issue.getUpgradeReason() == JavaUpgradeIssue.UpgradeReason.CVE
? APPMOD_CVE_AGENT_NAME
: APPMOD_UPGRADE_AGENT_NAME;
JavaVersionNotificationService.getInstance().openCopilotChatWithPrompt(project, prompt, agentName);
AppModUtils.logTelemetryEvent("openCopilotChatForJavaUpgradeQuickFix", Map.of("appmodPluginInstalled", String.valueOf(AppModPluginInstaller.isAppModPluginInstalled())));
} catch (Throwable ex) {
log.error("Failed to apply Java upgrade quick fix", ex);
}
}

private String buildPromptForIssue(@NotNull JavaUpgradeIssue issue) {
if (issue.getUpgradeReason() == JavaUpgradeIssue.UpgradeReason.CVE) {
final String coordinate = issue.getCurrentVersion() == null
? issue.getPackageId()
: issue.getPackageId() + ":" + issue.getCurrentVersion();
return String.format(FIX_VULNERABLE_DEPENDENCY_WITH_COPILOT_PROMPT, coordinate);
}
return String.format(
UPGRADE_JAVA_FRAMEWORK_PROMPT,
issue.getPackageDisplayName(), issue.getCurrentVersion(), issue.getSuggestedVersion()
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
/*
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*/

package com.microsoft.azure.toolkit.intellij.appmod.javaupgrade.action;

import com.intellij.openapi.actionSystem.ActionUpdateThread;
import com.intellij.openapi.actionSystem.AnAction;
import com.intellij.openapi.actionSystem.AnActionEvent;
import com.intellij.openapi.actionSystem.CommonDataKeys;
import com.intellij.openapi.project.DumbAware;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.vfs.VirtualFile;
import com.microsoft.azure.toolkit.intellij.appmod.common.AppModPluginInstaller;
import com.microsoft.azure.toolkit.intellij.appmod.javaupgrade.service.JavaVersionNotificationService;
import com.microsoft.azure.toolkit.intellij.appmod.javaupgrade.utils.ProblemsViewUtils;
import com.microsoft.azure.toolkit.intellij.appmod.utils.AppModUtils;
import lombok.extern.slf4j.Slf4j;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import static com.microsoft.azure.toolkit.intellij.appmod.javaupgrade.utils.Constants.APPMOD_UPGRADE_AGENT_NAME;
import static com.microsoft.azure.toolkit.intellij.appmod.javaupgrade.utils.Constants.UPGRADE_JAVA_FRAMEWORK_PROMPT;

/**
* Promotes Java and framework upgrade quick fixes to the Problems View context menu.
*/
@Slf4j
public class UpgradeInProblemsViewAction extends AnAction implements DumbAware {

private static final Pattern UPGRADE_DESCRIPTION_PATTERN = Pattern.compile(
"Your project uses (.+?) (\\S+)\\. Consider upgrading (.+?) to (\\S+), " +
"the latest LTS version, for better performance and support"
);

@Override
public void actionPerformed(@NotNull AnActionEvent event) {
final Project project = event.getData(CommonDataKeys.PROJECT);
if (project == null || project.isDisposed()) {
return;
}
final UpgradeDescription upgrade =
parseUpgradeDescription(ProblemsViewUtils.extractProblemDescription(event));
if (upgrade == null) {
return;
}

try {
JavaVersionNotificationService.getInstance().openCopilotChatWithPrompt(
project,
String.format(
UPGRADE_JAVA_FRAMEWORK_PROMPT,
upgrade.packageDisplayName(),
upgrade.currentVersion(),
upgrade.suggestedVersion()
),
APPMOD_UPGRADE_AGENT_NAME
);
AppModUtils.logTelemetryEvent(
"openCopilotChatForUpgradeInProblemsViewAction",
Map.of(
"appmodPluginInstalled",
String.valueOf(AppModPluginInstaller.isAppModPluginInstalled())
)
);
} catch (Throwable throwable) {
log.error("Failed to open Copilot chat for Java or framework upgrade", throwable);
}
}

@Override
public void update(@NotNull AnActionEvent event) {
final Project project = event.getData(CommonDataKeys.PROJECT);
final VirtualFile file = event.getData(CommonDataKeys.VIRTUAL_FILE);
final UpgradeDescription upgrade =
parseUpgradeDescription(ProblemsViewUtils.extractProblemDescription(event));
final boolean visible = project != null && !project.isDisposed() &&
isBuildFile(file) && upgrade != null;
event.getPresentation().setEnabledAndVisible(visible);
if (!visible) {
return;
}

String actionName = getActionName(upgrade);
if (!AppModPluginInstaller.isAppModPluginInstalled()) {
actionName += AppModPluginInstaller.TO_INSTALL_APP_MODE_PLUGIN;
}
event.getPresentation().setText(actionName);
}

@Override
public @NotNull ActionUpdateThread getActionUpdateThread() {
return ActionUpdateThread.BGT;
}

@Nullable
static UpgradeDescription parseUpgradeDescription(@Nullable String description) {
if (description == null) {
return null;
}
final Matcher matcher = UPGRADE_DESCRIPTION_PATTERN.matcher(description);
if (!matcher.find() || !matcher.group(1).equals(matcher.group(3))) {
return null;
}
return new UpgradeDescription(matcher.group(1), matcher.group(2), matcher.group(4));
}

@NotNull
static String getActionName(@NotNull UpgradeDescription upgrade) {
return "Upgrade " + upgrade.packageDisplayName() + " with Copilot";
}

private static boolean isBuildFile(@Nullable VirtualFile file) {
return file != null && (
file.getName().equals("pom.xml") ||
file.getName().endsWith(".gradle") ||
file.getName().endsWith(".gradle.kts")
);
}

record UpgradeDescription(
@NotNull String packageDisplayName,
@NotNull String currentVersion,
@NotNull String suggestedVersion
) {
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,11 @@

import lombok.extern.slf4j.Slf4j;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.idea.maven.project.MavenProject;
import org.jetbrains.idea.maven.project.MavenProjectsManager;

import java.util.List;
import java.util.Properties;

/**
* Inspection that displays Java upgrade issues detected by JavaUpgradeDetectionService.
Expand Down Expand Up @@ -53,19 +56,19 @@ public PsiElementVisitor buildVisitor(@NotNull ProblemsHolder holder, boolean is
return PsiElementVisitor.EMPTY_VISITOR;
}

// Get cached issues (computed once at project startup)
final JavaUpgradeIssue jdkIssue = cache.getJdkIssue();
final List<JavaUpgradeIssue> dependencyIssues = cache.getDependencyIssues();
final Properties mavenProperties = getMavenProperties(project, file);

return new XmlElementVisitor() {
@Override
public void visitXmlTag(@NotNull XmlTag tag) {
super.visitXmlTag(tag);

// Check for JDK version tags
if (jdkIssue != null) {
if (isJavaVersionProperty(tag) || isCompilerPluginVersionTag(tag)) {
registerProblem(holder, tag, jdkIssue);
if (isJavaVersionProperty(tag) || isCompilerPluginVersionTag(tag)) {
final JavaUpgradeIssue javaIssue =
JavaUpgradeProblemLocator.createJavaVersionIssue(tag, mavenProperties);
if (javaIssue != null) {
registerProblem(holder, tag, javaIssue);
}
}

Expand Down Expand Up @@ -93,6 +96,16 @@ public void visitXmlTag(@NotNull XmlTag tag) {
};
}

@NotNull
private Properties getMavenProperties(@NotNull Project project, @NotNull PsiFile file) {
if (file.getVirtualFile() == null) {
return new Properties();
}
final MavenProjectsManager manager = MavenProjectsManager.getInstanceIfCreated(project);
final MavenProject mavenProject =
manager == null ? null : manager.findProject(file.getVirtualFile());
return mavenProject == null ? new Properties() : mavenProject.getProperties();
}
private void registerProblem(@NotNull ProblemsHolder holder, @NotNull XmlTag tag, @NotNull JavaUpgradeIssue issue) {
log.info("Registering Java upgrade issue in inspection: {}", issue);
holder.registerProblem(
Expand Down
Loading