Skip to content

Commit 6c9fe93

Browse files
committed
Update azd install process
1 parent a755ec7 commit 6c9fe93

File tree

9 files changed

+229
-93
lines changed

9 files changed

+229
-93
lines changed

PluginsAndFeatures/azure-toolkit-for-intellij/azure-intellij-plugin-lib/src/main/java/com/microsoft/azure/toolkit/intellij/common/TerminalUtils.java

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,19 @@ public static TerminalWidget getOrCreateTerminalWidget(@Nonnull Project project,
7878
.orElseGet(() -> manager.createShellWidget(workingDirectory, terminalTabTitle, true, true));
7979
}
8080

81+
@Nonnull
82+
public static TerminalWidget getTerminalWidget(@Nonnull Project project, @Nullable Path workingDir, String terminalTabTitle) {
83+
final TerminalToolWindowManager manager = TerminalToolWindowManager.getInstance(project);
84+
final String workingDirectory = Optional.ofNullable(workingDir).map(Path::toString).orElse(null);
85+
return manager.getTerminalWidgets().stream()
86+
.filter(widget -> StringUtils.isBlank(terminalTabTitle) ||
87+
StringUtils.equals(widget.getTerminalTitle().buildTitle(), terminalTabTitle))
88+
.findFirst()
89+
.orElseGet(() -> manager.createShellWidget(workingDirectory, terminalTabTitle, true, true));
90+
}
91+
92+
93+
8194
public static boolean hasRunningCommands(TerminalWidget widget) throws IllegalStateException {
8295
final TtyConnector connector = widget.getTtyConnector();
8396
if (connector == null) {

PluginsAndFeatures/azure-toolkit-for-intellij/azure-intellij-plugin-service-explorer/src/main/java/com/microsoft/azure/toolkit/intellij/explorer/AzureExplorer.java

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,10 +50,13 @@
5050

5151
import javax.annotation.Nonnull;
5252
import javax.swing.tree.DefaultTreeModel;
53+
import javax.swing.tree.TreeNode;
5354
import javax.swing.tree.TreePath;
5455
import java.awt.event.MouseEvent;
5556
import java.util.Arrays;
5657
import java.util.Comparator;
58+
import java.util.Enumeration;
59+
import java.util.Iterator;
5760
import java.util.List;
5861
import java.util.Objects;
5962
import java.util.Optional;
@@ -105,6 +108,28 @@ private AzureExplorer(Project project) {
105108
appGroupedResourcesRoot.clearChildren();
106109
typeGroupedResourcesRoot.clearChildren();
107110
}));
111+
112+
AzureEventBus.on("azd.installed", new AzureEventBus.EventListener(e -> {
113+
final DefaultTreeModel model = (DefaultTreeModel) this.getModel();
114+
final TreeNode<?> root = (TreeNode<?>) model.getRoot();
115+
if (root != null && root.children() != null) {
116+
Iterator<javax.swing.tree.TreeNode> iterator = root.children().asIterator();
117+
while (iterator.hasNext()) {
118+
final TreeNode<?> childNode = (TreeNode<?>) iterator.next();
119+
final Node<?> childInnerNode = childNode.getInner();
120+
if (childInnerNode instanceof AzdNode) {
121+
final AzdNode azdNode = (AzdNode) childInnerNode;
122+
childNode.setAllowsChildren(true);
123+
azdNode.clearClickHandlers();
124+
azdNode.withDescription("");
125+
azdNode.showAzdActions();
126+
azdNode.refreshView();
127+
childNode.updateChildren(true);
128+
break;
129+
}
130+
}
131+
}
132+
}));
108133
}
109134

110135
@Override
Original file line numberDiff line numberDiff line change
@@ -1,94 +1,167 @@
11
package com.microsoft.azure.toolkit.intellij.explorer.azd;
22

33
import com.fasterxml.jackson.annotation.JsonInclude;
4-
import com.fasterxml.jackson.core.JsonProcessingException;
54
import com.fasterxml.jackson.databind.DeserializationFeature;
65
import com.fasterxml.jackson.databind.ObjectMapper;
6+
import com.intellij.notification.NotificationGroupManager;
7+
import com.intellij.notification.NotificationType;
8+
import com.intellij.openapi.progress.ProgressManager;
9+
import com.intellij.openapi.progress.Task;
710
import com.intellij.openapi.project.Project;
811
import com.microsoft.azure.toolkit.ide.common.component.Node;
912
import com.microsoft.azure.toolkit.ide.common.icon.AzureIcons;
10-
import com.microsoft.azure.toolkit.intellij.common.TerminalUtils;
13+
import com.microsoft.azure.toolkit.lib.common.event.AzureEventBus;
14+
import org.apache.commons.lang3.SystemUtils;
1115

1216
import java.io.BufferedReader;
1317
import java.io.InputStreamReader;
14-
import java.util.Map;
1518
import java.util.stream.Collectors;
1619

20+
import static com.microsoft.azure.toolkit.intellij.explorer.azd.AzdUtils.executeInTerminal;
21+
1722
public final class AzdNode extends Node<String> {
1823
private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper()
1924
.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
2025
.setSerializationInclusion(JsonInclude.Include.NON_NULL);
26+
private static final String WIN_REFRESH_PATH_COMMAND = "$env:PATH = [System.Environment]::GetEnvironmentVariable(\"PATH\",\"Machine\") + \";\" + [System.Environment]::GetEnvironmentVariable(\"PATH\",\"User\"); ";
27+
private static final String LINUX_REFRESH_PATH_COMMAND = "source ~/.bashrc";
28+
private static final String MAC_REFRESH_PATH_COMMAND = "source ~/.zshrc";
29+
30+
private static final String WIN_AZD_INSTALL_COMMAND = "winget install microsoft.azd";
31+
private static final String LINUX_AZD_INSTALL_COMMAND = "curl -fsSL https://aka.ms/install-azd.sh | bash";
32+
private static final String MAC_AZD_INSTALL_COMMAND = "brew tap azure/azd && brew install azd";
2133

2234
private final Project project;
2335

2436
public AzdNode(Project project) {
2537
super("Azure Developer (Preview)");
2638
this.project = project;
2739
withIcon(AzureIcons.Common.SERVICES);
28-
addChildren();
40+
initializeNode();
2941
}
3042

31-
public void addChildren() {
43+
public void initializeNode() {
3244
if (isAzdInstalled()) {
3345
AzdUtils.logTelemetryEvent("azd-installed");
34-
if (isAzdSignedIn()) {
35-
AzdUtils.logTelemetryEvent("azd-signed-in");
36-
withDescription("Signed In");
37-
addChild(getCreateFromTemplatesNode());
38-
addChild(getInitializeFromSourceNode());
39-
addChild(getProvisionResourcesNode());
40-
addChild(getDeployToAzureNode());
41-
addChild(getProvisionAndDeployToAzureNode());
46+
showAzdActions();
47+
} else {
48+
showNotInstalled();
49+
}
50+
}
51+
52+
private void showNotInstalled() {
53+
AzdUtils.logTelemetryEvent("azd-not-installed");
54+
withDescription("Install azd");
55+
onClicked(e -> {
56+
final String command;
57+
if (SystemUtils.IS_OS_WINDOWS) {
58+
command = WIN_AZD_INSTALL_COMMAND;
59+
} else if (SystemUtils.IS_OS_LINUX) {
60+
command = LINUX_AZD_INSTALL_COMMAND;
4261
} else {
43-
AzdUtils.logTelemetryEvent("azd-not-signed-in");
44-
withDescription("Not Signed In");
45-
onClicked(e -> {
46-
final ConfirmAndRunDialog confirmAndRunDialog = new ConfirmAndRunDialog(project, "Sign in", "Do you want to sign in to Azure Developer CLI (azd)?", "azd auth login");
47-
confirmAndRunDialog.setOkButtonText("Sign In");
48-
confirmAndRunDialog.show();
49-
});
62+
command = MAC_AZD_INSTALL_COMMAND;
5063
}
51-
} else {
52-
AzdUtils.logTelemetryEvent("azd-not-installed");
53-
withDescription("Install azd");
54-
onClicked(e -> {
55-
final String command;
56-
if (System.getProperties().getProperty("os.name").toLowerCase().contains("windows")) {
57-
command = "winget install microsoft.azd";
58-
} else if (System.getProperties().getProperty("os.name").toLowerCase().contains("linux")) {
59-
command = "curl -fsSL https://aka.ms/install-azd.sh | bash";
64+
65+
final ConfirmAndRunDialog installDialog = new ConfirmAndRunDialog(project, "Install azd");
66+
installDialog.setLabel("Do you want to install Azure Developer CLI (azd)?");
67+
installDialog.setOkButtonText("Install");
68+
installDialog.setEventName("install");
69+
installDialog.setOnOkAction(() -> {
70+
installAzd(command);
71+
});
72+
installDialog.show();
73+
});
74+
}
75+
76+
private void installAzd(String command) {
77+
ProgressManager.getInstance().run(new Task.Backgroundable(project, "Install azd", false) {
78+
@Override
79+
public void run(com.intellij.openapi.progress.ProgressIndicator indicator) {
80+
indicator.setIndeterminate(true);
81+
indicator.setText(getTitle() + "...");
82+
final int exitCode = runAsBackgroundTask(command);
83+
if (exitCode == 0) {
84+
AzureEventBus.emit("azd.installed");
85+
NotificationGroupManager.getInstance()
86+
.getNotificationGroup("Azure Developer")
87+
.createNotification("Installation of azd is successful.", NotificationType.INFORMATION)
88+
.notify(project);
89+
90+
if (SystemUtils.IS_OS_WINDOWS) {
91+
executeInTerminal(project, WIN_REFRESH_PATH_COMMAND);
92+
} else if (SystemUtils.IS_OS_LINUX) {
93+
executeInTerminal(project, LINUX_REFRESH_PATH_COMMAND);
94+
} else {
95+
executeInTerminal(project, MAC_REFRESH_PATH_COMMAND);
96+
}
97+
indicator.stop();
6098
} else {
61-
command = "brew tap azure/azd && brew install azd";
99+
indicator.setText("Installation of azd failed.");
100+
NotificationGroupManager.getInstance()
101+
.getNotificationGroup("Azure Developer")
102+
.createNotification("Installation of azd failed.", NotificationType.ERROR)
103+
.notify(project);
104+
indicator.stop();
62105
}
63-
final ConfirmAndRunDialog installDialog = new ConfirmAndRunDialog(project, "Install azd", "Do you want to install Azure Developer CLI (azd)?", command);
64-
installDialog.setOkButtonText("Install");
65-
installDialog.show();
66-
});
67-
}
106+
}
107+
});
108+
}
109+
110+
public void showAzdActions() {
111+
AzdUtils.logTelemetryEvent("azd-signed-in");
112+
addChild(getCreateFromTemplatesNode());
113+
addChild(getInitializeFromSourceNode());
114+
addChild(getProvisionResourcesNode());
115+
addChild(getDeployToAzureNode());
116+
addChild(getProvisionAndDeployToAzureNode());
117+
}
118+
119+
@Override
120+
public synchronized void refreshView() {
121+
super.refreshView();
122+
refreshChildrenLater(true);
68123
}
69124

70125
private Node<String> getProvisionAndDeployToAzureNode() {
71126
return new Node<>("Provision and Deploy")
72127
.withIcon(AzureIcons.Action.START)
73-
.onClicked(e -> new ConfirmAndRunDialog(project, "Provision and deploy", "Do you want to provision and deploy to Azure?", "azd up").show());
128+
.onClicked(e -> {
129+
new ConfirmAndRunDialog(project, "Provision and deploy")
130+
.setLabel("Do you want to provision and deploy to Azure?")
131+
.setOnOkAction(() -> executeInTerminal(project, "azd up"))
132+
.setEventName("provision-and-deploy")
133+
.show();
134+
});
74135
}
75136

76137
private Node<String> getDeployToAzureNode() {
77138
return new Node<>("Deploy to Azure")
78139
.withIcon(AzureIcons.Action.DEPLOY)
79-
.onClicked(e -> new ConfirmAndRunDialog(project, "Deploy to Azure", "Do you want to start deployment to Azure?", "azd deploy").show());
140+
.onClicked(e -> new ConfirmAndRunDialog(project, "Deploy to Azure")
141+
.setLabel("Do you want to start deployment to Azure?")
142+
.setOnOkAction(() -> executeInTerminal(project, "azd deploy"))
143+
.setEventName("deploy-to-azure")
144+
.show());
80145
}
81146

82147
private Node<String> getProvisionResourcesNode() {
83148
return new Node<>("Provision resources")
84149
.withIcon(AzureIcons.Action.EXPORT)
85-
.onClicked(e -> new ConfirmAndRunDialog(project, "Provision Resources", "Do you want to provision Azure resources?", "azd provision").show());
150+
.onClicked(e -> new ConfirmAndRunDialog(project, "Provision Resources")
151+
.setLabel("Do you want to provision Azure resources?")
152+
.setOnOkAction(() -> executeInTerminal(project, "azd provision"))
153+
.setEventName("provision-resources")
154+
.show());
86155
}
87156

88157
private Node<String> getInitializeFromSourceNode() {
89158
return new Node<>("Initialize from source")
90159
.withIcon(AzureIcons.Action.EDIT)
91-
.onClicked(e -> new ConfirmAndRunDialog(project, "Initialize from source", "Do you want to initialize using existing code?", "azd init").show());
160+
.onClicked(e -> new ConfirmAndRunDialog(project, "Initialize from source")
161+
.setLabel("Do you want to initialize using existing code?")
162+
.setOnOkAction(() -> executeInTerminal(project, "azd init"))
163+
.setEventName("initialize-from-source")
164+
.show());
92165
}
93166

94167
private Node<String> getCreateFromTemplatesNode() {
@@ -100,40 +173,16 @@ private Node<String> getCreateFromTemplatesNode() {
100173
});
101174
}
102175

103-
private static boolean isAzdInstalled() {
104-
final String azdVersionJson = runCommand("azd version -o json");
105-
if (azdVersionJson != null && !azdVersionJson.isEmpty()) {
106-
try {
107-
final Map<String, String> response = OBJECT_MAPPER.readValue(azdVersionJson, Map.class);
108-
if (response.containsKey("azd")) {
109-
return true;
110-
}
111-
} catch (JsonProcessingException e) {
112-
}
113-
}
114-
return false;
176+
public static boolean isAzdInstalled() {
177+
return runAsBackgroundTask("azd version -o json") == 0;
115178
}
116179

117-
private static boolean isAzdSignedIn() {
118-
final String loginStatusJson = runCommand("azd auth login --check-status -o json");
119-
if (loginStatusJson != null && !loginStatusJson.isEmpty()) {
120-
try {
121-
final Map<String, String> response = OBJECT_MAPPER.readValue(loginStatusJson, Map.class);
122-
if (response.containsKey("status") && "success".equals(response.get("status"))) {
123-
return true;
124-
}
125-
} catch (JsonProcessingException e) {
126-
}
127-
}
128-
return false;
129-
}
130-
131-
public static String runCommand(String command) {
180+
private static int runAsBackgroundTask(String command) {
132181
try {
133182
final ProcessBuilder processBuilder = new ProcessBuilder();
134183
// Detect OS and set the appropriate command
135-
String os = System.getProperty("os.name").toLowerCase();
136-
if (os.contains("win")) {
184+
final String os = System.getProperty("os.name").toLowerCase();
185+
if (SystemUtils.IS_OS_WINDOWS) {
137186
processBuilder.command("cmd", "/c", command); // Windows
138187
} else {
139188
processBuilder.command("bash", "-c", command); // Linux/Unix
@@ -145,13 +194,10 @@ public static String runCommand(String command) {
145194
try (BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()))) {
146195
String output = reader.lines().collect(Collectors.joining("\n"));
147196
int exitCode = process.waitFor();
148-
if (exitCode != 0) {
149-
return null;
150-
}
151-
return output;
197+
return exitCode;
152198
}
153199
} catch (Exception e) {
154-
return null; // Handle error appropriately
200+
return 1; // Handle error appropriately
155201
}
156202
}
157203
}

PluginsAndFeatures/azure-toolkit-for-intellij/azure-intellij-plugin-service-explorer/src/main/java/com/microsoft/azure/toolkit/intellij/explorer/azd/AzdTemplatesDialog.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ public AzdTemplatesDialog(@Nullable Project project) {
2020

2121
@Override
2222
protected @Nullable JComponent createCenterPanel() {
23-
return new AzdTemplatesLibrary(project);
23+
return new AzdTemplatesLibrary(project, this);
2424
}
2525

2626
@Override

PluginsAndFeatures/azure-toolkit-for-intellij/azure-intellij-plugin-service-explorer/src/main/java/com/microsoft/azure/toolkit/intellij/explorer/azd/AzdTemplatesLibrary.java

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
import com.intellij.icons.AllIcons;
88
import com.intellij.ide.BrowserUtil;
99
import com.intellij.openapi.project.Project;
10+
import com.intellij.openapi.ui.DialogWrapper;
1011
import com.intellij.openapi.ui.VerticalFlowLayout;
1112
import com.intellij.ui.HyperlinkLabel;
1213
import com.intellij.ui.ScrollPaneFactory;
@@ -38,10 +39,12 @@ public class AzdTemplatesLibrary extends JPanel {
3839
private final Project project;
3940
private final List<AzdTemplate> templates;
4041
private final List<JBCheckBox> selectedFilters = new ArrayList<>();
42+
private final DialogWrapper dialogWrapper;
4143

42-
public AzdTemplatesLibrary(Project project) {
44+
public AzdTemplatesLibrary(Project project, DialogWrapper dialogWrapper) {
4345
super();
4446
this.project = project;
47+
this.dialogWrapper = dialogWrapper;
4548
setLayout(new BorderLayout());
4649
setBorder(JBUI.Borders.empty(10));
4750
this.templates = readFromGitHub("https://raw.githubusercontent.com/Azure/awesome-azd/refs/heads/main/website/static/templates.json");
@@ -154,9 +157,17 @@ private void createTemplateTile(AzdTemplate item, JPanel tilesPanel) {
154157
return;
155158
}
156159
// Show confirmation dialog
157-
ConfirmAndRunDialog createDialog = new ConfirmAndRunDialog(project, "Create from template", "Create a new project from the selected template?", command);
158-
createDialog.setOkButtonText("Create");
160+
final ConfirmAndRunDialog createDialog = new ConfirmAndRunDialog(project, "Create from template")
161+
.setLabel("Create a new project from the selected template?")
162+
.setEventName("create-from-template")
163+
.setOnOkAction(() -> {
164+
dialogWrapper.close(DialogWrapper.OK_EXIT_CODE);
165+
AzdUtils.executeInTerminal(project, command);
166+
})
167+
.setOkButtonText("Create");
168+
159169
createDialog.show();
170+
160171
});
161172
commandPanel.add(runButton);
162173

0 commit comments

Comments
 (0)