Skip to content
Merged
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
@@ -0,0 +1,15 @@
dependencies {
implementation(project(":azure-intellij-plugin-lib"))
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")
intellijPlatform {
// Plugin Dependencies. Uses `platformBundledPlugins` property from the gradle.properties file for bundled IntelliJ Platform plugins.
bundledPlugin("com.intellij.java")
bundledPlugin("org.jetbrains.idea.maven")
// bundledPlugin("org.jetbrains.idea.maven.model")
bundledPlugin("com.intellij.gradle")
// Copilot plugin for Java upgrade integration
plugin("com.github.copilot:1.5.59-243")
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
/*
* 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.common;

import com.intellij.ide.plugins.IdeaPluginDescriptor;
import com.intellij.ide.plugins.PluginManagerCore;
import com.intellij.openapi.extensions.PluginId;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.Messages;
import com.intellij.openapi.updateSettings.impl.pluginsAdvertisement.PluginsAdvertiser;
import com.microsoft.azure.toolkit.intellij.appmod.utils.AppModUtils;
import lombok.extern.slf4j.Slf4j;

import javax.annotation.Nonnull;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Set;

/**
* Utility class for managing App Modernization plugin installation.
* This centralizes all plugin detection and installation logic to avoid code duplication
* between MigrateToAzureNode and MigrateToAzureAction.
*/
@Slf4j
public class AppModPluginInstaller {
private static final String PLUGIN_ID = "com.github.copilot.appmod";
private static final String COPILOT_PLUGIN_ID = "com.github.copilot";
public static final String TO_INSTALL_APP_MODE_PLUGIN = " (Install Github Copilot app modernization)";
private AppModPluginInstaller() {
// Utility class - prevent instantiation
}

/**
* Checks if the App Modernization plugin is installed and enabled.
*/
public static boolean isAppModPluginInstalled() {
try {
final PluginId pluginId = PluginId.getId(PLUGIN_ID);
final IdeaPluginDescriptor plugin = PluginManagerCore.getPlugin(pluginId);
final boolean result = plugin != null && plugin.isEnabled();
log.debug("[AppModPluginInstaller] isAppModPluginInstalled: {}", result);
return result;
} catch (Exception e) {
log.error("[AppModPluginInstaller] Failed to check AppMod plugin status", e);
return false;
}
}

/**
* Checks if GitHub Copilot plugin is installed and enabled.
*/
public static boolean isCopilotInstalled() {
try {
final PluginId pluginId = PluginId.getId(COPILOT_PLUGIN_ID);
final IdeaPluginDescriptor plugin = PluginManagerCore.getPlugin(pluginId);
final boolean result = plugin != null && plugin.isEnabled();
log.debug("[AppModPluginInstaller] isCopilotInstalled: {}", result);
return result;
} catch (Exception e) {
log.error("[AppModPluginInstaller] Failed to check Copilot plugin status", e);
return false;
}
}

/**
* Detects if running in development mode (runIde task).
* This helps determine whether to show restart options or dev-mode instructions.
*/
public static boolean isRunningInDevMode() {
try {
final PluginId azureToolkitId = PluginId.getId("com.microsoft.tooling.msservices.intellij.azure");
final IdeaPluginDescriptor descriptor = PluginManagerCore.getPlugin(azureToolkitId);
if (descriptor != null) {
final String path = descriptor.getPluginPath().toString();
final boolean result = path.contains("build") || path.contains("sandbox") || path.contains("out");
log.debug("[AppModPluginInstaller] isRunningInDevMode: {}, path: {}", result, path);
return result;
}
} catch (Exception ex) {
log.error("[AppModPluginInstaller] Failed to check dev mode status", ex);
}
return false;
}

/**
* Shows a confirmation dialog for plugin installation.
*
* @param project The current project
* @param forUpgrade true for "upgrade" scenario, false for "migrate to Azure" scenario
* @param onConfirm Callback to execute when user confirms installation (if null, calls installPlugin directly)
*/
public static void showInstallConfirmation(@Nonnull Project project, boolean forUpgrade, @Nonnull Runnable onConfirm) {
final boolean copilotInstalled = isCopilotInstalled();
final String action = forUpgrade ? "upgrade" : "migration";
log.debug("[AppModPluginInstaller] showInstallConfirmation - forUpgrade: {}, copilotInstalled: {}", forUpgrade, copilotInstalled);

final String title = copilotInstalled
? "Install Github Copilot app modernization"
: "Install GitHub Copilot and app modernization";

final String message;
if (copilotInstalled) {
message = forUpgrade
? "Install this plugin to upgrade your apps with Copilot."
: "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.";
}

if (Messages.showOkCancelDialog(project, message, title, "Install", "Cancel", Messages.getQuestionIcon()) == Messages.OK) {
log.info("[AppModPluginInstaller] User confirmed plugin installation for {}", action);
AppModUtils.logTelemetryEvent("plugin." + action + ".install-confirmed");
onConfirm.run();
} else {
log.info("[AppModPluginInstaller] User cancelled plugin installation for {}", action);
AppModUtils.logTelemetryEvent("plugin." + action + ".install-cancelled");
}
}


/**
* Installs the App Modernization plugin.
* IntelliJ platform will automatically install Copilot as a dependency if AppMod declares it.
*
* @param project The current project
* @param forUpgrade true for "upgrade" scenario, false for "migrate to Azure" scenario
*/
public static void installPlugin(@Nonnull Project project, boolean forUpgrade) {
log.debug("[AppModPluginInstaller] installPlugin - forUpgrade: {}", forUpgrade);
if (isAppModPluginInstalled()) {
log.debug("[AppModPluginInstaller] installPlugin - skipping, already installed");
return;
}

// Only pass AppMod ID - IntelliJ will automatically install Copilot as dependency
final Set<PluginId> pluginsToInstall = new LinkedHashSet<>();
pluginsToInstall.add(PluginId.getId(PLUGIN_ID));

final String source = forUpgrade ? "upgrade" : "migration";
log.info("[AppModPluginInstaller] Starting plugin installation via PluginsAdvertiser");
PluginsAdvertiser.installAndEnable(
project,
pluginsToInstall,
true, // showDialog
true, // selectAllInDialog - pre-select all plugins
null, // modalityState
() -> {
log.info("[AppModPluginInstaller] Plugin installation completed");
AppModUtils.logTelemetryEvent("plugin." + source + ".install-complete");
}
);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
/*
* 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.javamigration;

import com.intellij.openapi.project.Project;

import javax.annotation.Nonnull;
import java.util.List;

/**
* Extension point interface for providing migration nodes.
*
* Implementations of this interface will be discovered via IntelliJ's extension point mechanism
* and used to dynamically construct the migration options tree in:
* - Service Explorer (MigrateToAzureNode)
* - Context Menu (MigrateToAzureAction)
* - Project Explorer (MigrateToAzureFacetNode)
*
* Example implementation:
* <pre>
* public class MyMigrationProvider implements IMigrateOptionProvider {
* @Override
* public List&lt;MigrateNodeData&gt; createNodeData(@Nonnull Project project) {
* return List.of(
* MigrateNodeData.builder("My Migration Option")
* .onClick(() -> performMigration(project))
* .build()
* );
* }
* }
* </pre>
*
* Registration in plugin.xml:
* <pre>
* &lt;extensions defaultExtensionNs="com.microsoft.tooling.msservices.intellij.azure"&gt;
* &lt;migrateOptionProvider implementation="your.package.MyMigrationProvider"/&gt;
* &lt;/extensions&gt;
* </pre>
*/
public interface IMigrateOptionProvider {

/**
* Creates migration node data for the Migrate to Azure section.
*
* This method is called each time the migration menu/tree is constructed.
* The returned list can contain multiple MigrateNodeData instances,
* each representing a single action or a group of options.
*
* @param project The current IntelliJ project
* @return A list of MigrateNodeData instances representing the migration option(s)
*/
@Nonnull
List<MigrateNodeData> createNodeData(@Nonnull Project project);

/**
* Returns the priority/order of this node provider.
* Nodes will be displayed in ascending order of priority.
* Lower numbers appear first.
*
* @return Priority value (default: 100)
*/
default int getPriority() {
return 100;
}

/**
* Determines whether this provider should create a node.
* Can be used to conditionally show/hide migration options based on context.
*
* @param project The current IntelliJ project
* @return true if this provider should contribute a node, false otherwise
*/
default boolean isApplicable(@Nonnull Project project) {
return true;
}
}
Loading