Skip to content
Original file line number Diff line number Diff line change
Expand Up @@ -107,8 +107,8 @@ public static void showInstallConfirmation(@Nonnull Project project, boolean for
: "Install this plugin to automate migrating your apps to Azure with Copilot.";
} else {
message = forUpgrade
? "To upgrade your apps, you'll need two plugins: GitHub Copilot and app modernization."
: "To migrate to Azure, you'll need two plugins: GitHub Copilot and app modernization.";
? "To upgrade your apps, you'll need to install GitHub Copilot modernization."
: "To migrate to Azure, you'll need to install GitHub Copilot modernization.";
}
AppModUtils.logTelemetryEvent("plugin." + action + ".install-prompt-shown", Map.of("copilotInstalled", String.valueOf(copilotInstalled)));
if (Messages.showOkCancelDialog(project, message, title, "Install", "Cancel", Messages.getQuestionIcon()) == Messages.OK) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -93,8 +93,11 @@ public void update(@NotNull AnActionEvent e) {
}
e.getPresentation().setEnabledAndVisible(true);
// e.getPresentation().setText(SCAN_AND_RESOLVE_CVES_WITH_COPILOT_DISPLAY_NAME);
final String baseText = getTemplatePresentation().getText();
if (!AppModPluginInstaller.isAppModPluginInstalled()) {
e.getPresentation().setText(e.getPresentation().getText() + AppModPluginInstaller.TO_INSTALL_APP_MODE_PLUGIN);
e.getPresentation().setText(baseText + AppModPluginInstaller.TO_INSTALL_APP_MODE_PLUGIN);
} else {
e.getPresentation().setText(baseText);
}
} catch (Throwable ex) {
// In case of any error, hide the action
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,8 +56,11 @@ public void update(@NotNull AnActionEvent e) {
isMavenBuildFile(file) ||
isGradleBuildFile(file);
}
final String baseText = getTemplatePresentation().getText();
if (!isAppModPluginInstalled()) {
e.getPresentation().setText(e.getPresentation().getText() + TO_INSTALL_APP_MODE_PLUGIN);
e.getPresentation().setText(baseText + TO_INSTALL_APP_MODE_PLUGIN);
} else {
e.getPresentation().setText(baseText);
}
if (visible){
AppModUtils.logTelemetryEvent("showJavaUpgradeContextMenuAction", Map.of("appmodPluginInstalled", String.valueOf(isAppModPluginInstalled())));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,15 @@
package com.microsoft.azure.toolkit.intellij.appmod.javaupgrade.action;

import com.intellij.openapi.actionSystem.ActionManager;
import com.intellij.openapi.actionSystem.ActionPlaces;
import com.intellij.openapi.actionSystem.ActionPopupMenu;
import com.intellij.openapi.actionSystem.AnAction;
import com.intellij.openapi.actionSystem.DefaultActionGroup;
import com.intellij.openapi.actionSystem.Presentation;
import com.intellij.openapi.actionSystem.Separator;
import com.intellij.openapi.actionSystem.ex.ActionManagerEx;
import com.intellij.openapi.actionSystem.ex.ActionPopupMenuListener;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.startup.ProjectActivity;
import com.microsoft.azure.toolkit.intellij.appmod.common.AppModPluginInstaller;
Expand All @@ -21,6 +26,8 @@
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import java.util.concurrent.atomic.AtomicBoolean;

/**
* Registers the Upgrade action into the GitHub Copilot context menu at runtime.
* This is needed because the Copilot plugin creates its context menu groups dynamically.
Expand All @@ -31,17 +38,67 @@ public class UpgradeActionRegistrar implements ProjectActivity {
private static final String UPGRADE_ACTION_ID = "AzureToolkit.JavaUpgradeContextMenu";
private static final String PROJECT_VIEW_POPUP_MENU = "ProjectViewPopupMenu";

// Application-level guard so we install the popup listener only once per IDE process,
// even if multiple projects are opened (this ProjectActivity runs per-project).
private static final AtomicBoolean POPUP_LISTENER_INSTALLED = new AtomicBoolean(false);

@Nullable
@Override
public Object execute(@NotNull Project project, @NotNull Continuation<? super Unit> continuation) {
try{
// Eager attempt: works on 2nd+ project open within the same IDE process,
// after the GitHub Copilot plugin has populated its dynamic submenu.
discoverAndRegisterAction();
// Lazy fallback (fixes the first-open race): re-attempt the registration
// every time the Project View popup is created. The Copilot submenu is
// guaranteed to exist by the time the user right-clicks, and the call
// is cheap + idempotent thanks to the containsAction guard.
installLazyRegistrationListener();
} catch (Throwable e) {
log.error("Failed to register Upgrade action in Copilot context menu.", e);
}
return Unit.INSTANCE;
}

/**
* Installs an application-scoped {@link ActionPopupMenuListener} (only once per IDE
* process) that re-runs {@link #discoverAndRegisterAction()} whenever the Project
* View popup menu is opened. This is the lazy fallback for the first project open
* after IDE launch, where {@link ProjectActivity}s from us and from the GitHub Copilot
* plugin race and our discovery can miss Copilot's not-yet-created submenu.
*/
private void installLazyRegistrationListener() {
if (!POPUP_LISTENER_INSTALLED.compareAndSet(false, true)) {
return;
}
try {
ActionManagerEx.getInstanceEx().addActionPopupMenuListener(new ActionPopupMenuListener() {
@Override
public void actionPopupMenuCreated(@NotNull ActionPopupMenu menu) {
// Only react to the Project View right-click popup; ignore all
// other popups (editor, tool windows, etc.) to keep this cheap.
if (!ActionPlaces.PROJECT_VIEW_POPUP.equals(menu.getPlace())) {
return;
}
try {
discoverAndRegisterAction();
} catch (Throwable ex) {
log.warn("Lazy registration of Upgrade action into Copilot submenu failed.", ex);
}
}

@Override
public void actionPopupMenuReleased(@NotNull ActionPopupMenu menu) {
// no-op
}
}, ApplicationManager.getApplication());
} catch (Throwable e) {
// Roll back the flag so a later project open can try installing again.
POPUP_LISTENER_INSTALLED.set(false);
log.warn("Failed to install lazy registration listener for Upgrade action.", e);
}
}

private void discoverAndRegisterAction() {
// Only proceed if Copilot plugin is installed
if (!AppModPluginInstaller.isCopilotInstalled()) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ public class JavaVersionNotificationService {
private static final String NOTIFICATIONS_ENABLED_KEY = "azure.toolkit.java.version.notifications.enabled";
private static final String DEFERRED_UNTIL_KEY = "azure.toolkit.java.version.deferred_until";
private static final long DEFER_INTERVAL_MS = 10 * 24 * 60 * 60 * 1000L; // 10 days in milliseconds
private static final String DEFAULT_MODEL_NAME = "Claude Sonnet 4.5";
private static final String DEFAULT_MODEL_NAME = "Claude Sonnet 4.6";

// GitHub Copilot plugin ID
private static final String COPILOT_PLUGIN_ID = "com.github.copilot";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ public class Constants {
public static final String UPGRADE_JAVA_AND_FRAMEWORK_PROMPT = "Upgrade java runtime and java framework dependencies of this project to the latest LTS version using java upgrade tools by invoking #appmod-generate-upgrade-plan";
public static final String UPGRADE_JAVA_VERSION_PROMPT = "Upgrade Java runtime from version %s to the latest LTS version using java upgrade tools by invoking #appmod-generate-upgrade-plan";
public static final String UPGRADE_JAVA_FRAMEWORK_PROMPT = "Upgrade %s from version %s to the latest LTS version using java upgrade tools by invoking #appmod-generate-upgrade-plan";
public static final String SCAN_AND_RESOLVE_CVES_PROMPT = "run CVE scan for this project using java upgrade tools by invoking #validate_cves_for_java";
public static final String SCAN_AND_RESOLVE_CVES_PROMPT = "run CVE scan for this project using java upgrade tools by invoking #appmod-validate-cves-for-java";
public static final String UPGRADE_JDK_WITH_COPILOT_DISPLAY_NAME = "Upgrade JDK with Copilot";
public static final String UPGRADE_SPRING_BOOT_WITH_COPILOT_DISPLAY_NAME = "Upgrade Spring Boot with Copilot";
public static final String UPGRADE_SPRING_FRAMEWORK_WITH_COPILOT_DISPLAY_NAME = "Upgrade Spring Framework with Copilot";
Expand Down