Skip to content

Commit ea6bdb5

Browse files
Merge pull request #10757 from srnagar/azd-integration
Integrate azd to Azure Explorer
2 parents 1eeef02 + 4909383 commit ea6bdb5

File tree

7 files changed

+657
-3
lines changed

7 files changed

+657
-3
lines changed

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

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
import com.microsoft.azure.toolkit.ide.common.icon.AzureIcons;
3232
import com.microsoft.azure.toolkit.intellij.common.component.Tree;
3333
import com.microsoft.azure.toolkit.intellij.common.component.TreeUtils;
34+
import com.microsoft.azure.toolkit.intellij.explorer.azd.AzdNode;
3435
import com.microsoft.azure.toolkit.lib.Azure;
3536
import com.microsoft.azure.toolkit.lib.auth.AzureAccount;
3637
import com.microsoft.azure.toolkit.lib.auth.IAccountActions;
@@ -67,16 +68,20 @@ public class AzureExplorer extends Tree {
6768
@Getter
6869
public static final AzureExplorerNodeProviderManager manager = new AzureExplorerNodeProviderManager();
6970
public static final String AZURE_ICON = AzureIcons.Common.AZURE.getIconPath();
71+
private final AzdNode azdNode;
7072

71-
private AzureExplorer() {
73+
private AzureExplorer(Project project) {
7274
super();
7375
this.putClientProperty(PLACE, ResourceCommonActionsContributor.AZURE_EXPLORER);
76+
this.azdNode = new AzdNode(project);
7477
this.root = new Node<>("Azure")
7578
.withChildrenLoadLazily(false)
7679
.addChild(buildFavoriteRoot())
7780
.addChild(buildAppGroupedResourcesRoot())
7881
.addChild(buildTypeGroupedResourcesRoot())
79-
.addChildren(buildNonAzServiceNodes());
82+
.addChildren(buildNonAzServiceNodes())
83+
.addChild(azdNode);
84+
8085
this.init(this.root);
8186
this.setRootVisible(false);
8287
//noinspection UnstableApiUsage
@@ -172,7 +177,7 @@ public void refreshAll() {
172177
public static class ToolWindowFactory implements com.intellij.openapi.wm.ToolWindowFactory, DumbAware {
173178
public void createToolWindowContent(@Nonnull Project project, @Nonnull ToolWindow toolWindow) {
174179
final SimpleToolWindowPanel windowPanel = new SimpleToolWindowPanel(true, true);
175-
final AzureExplorer explorer = new AzureExplorer();
180+
final AzureExplorer explorer = new AzureExplorer(project);
176181
final JBScrollPane scrollPane = new JBScrollPane(explorer);
177182
explorer.putClientProperty(KEY_SCROLL_PANE, scrollPane);
178183
windowPanel.setContent(scrollPane);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,160 @@
1+
package com.microsoft.azure.toolkit.intellij.explorer.azd;
2+
3+
import com.fasterxml.jackson.annotation.JsonInclude;
4+
import com.fasterxml.jackson.core.JsonProcessingException;
5+
import com.fasterxml.jackson.databind.DeserializationFeature;
6+
import com.fasterxml.jackson.databind.ObjectMapper;
7+
import com.intellij.openapi.project.Project;
8+
import com.microsoft.azure.toolkit.ide.common.component.Node;
9+
import com.microsoft.azure.toolkit.ide.common.icon.AzureIcons;
10+
import com.microsoft.azure.toolkit.intellij.common.TerminalUtils;
11+
12+
import java.io.BufferedReader;
13+
import java.io.InputStreamReader;
14+
import java.util.Map;
15+
import java.util.stream.Collectors;
16+
17+
public final class AzdNode extends Node<String> {
18+
private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper()
19+
.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
20+
.setSerializationInclusion(JsonInclude.Include.NON_NULL);
21+
22+
private final Project project;
23+
24+
public AzdNode(Project project) {
25+
super("Azure Developer (Preview)");
26+
this.project = project;
27+
withIcon(AzureIcons.Common.SERVICES);
28+
addChildren();
29+
}
30+
31+
public void addChildren() {
32+
if (isAzdInstalled()) {
33+
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());
42+
} 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+
});
50+
}
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+
TerminalUtils.executeInTerminal(project, "winget install microsoft.azd", "azd");
59+
} else if (System.getProperties().getProperty("os.name").toLowerCase().contains("linux")) {
60+
command = "curl -fsSL https://aka.ms/install-azd.sh | bash";
61+
TerminalUtils.executeInTerminal(project, "curl -fsSL https://aka.ms/install-azd.sh | bash", "azd");
62+
} else {
63+
command = "brew tap azure/azd && brew install azd";
64+
TerminalUtils.executeInTerminal(project, "brew tap azure/azd && brew install azd", "azd");
65+
}
66+
final ConfirmAndRunDialog installDialog = new ConfirmAndRunDialog(project, "Install azd", "Do you want to install Azure Developer CLI (azd)?", command);
67+
installDialog.setOkButtonText("Install");
68+
installDialog.show();
69+
});
70+
}
71+
}
72+
73+
private Node<String> getProvisionAndDeployToAzureNode() {
74+
return new Node<>("Provision and Deploy")
75+
.withIcon(AzureIcons.Action.START)
76+
.onClicked(e -> new ConfirmAndRunDialog(project, "Provision and deploy", "Do you want to provision and deploy to Azure?", "azd up").show());
77+
}
78+
79+
private Node<String> getDeployToAzureNode() {
80+
return new Node<>("Deploy to Azure")
81+
.withIcon(AzureIcons.Action.DEPLOY)
82+
.onClicked(e -> new ConfirmAndRunDialog(project, "Deploy to Azure", "Do you want to start deployment to Azure?", "azd deploy").show());
83+
}
84+
85+
private Node<String> getProvisionResourcesNode() {
86+
return new Node<>("Provision resources")
87+
.withIcon(AzureIcons.Action.EXPORT)
88+
.onClicked(e -> new ConfirmAndRunDialog(project, "Provision Resources", "Do you want to provision Azure resources?", "azd provision").show());
89+
}
90+
91+
private Node<String> getInitializeFromSourceNode() {
92+
return new Node<>("Initialize from source")
93+
.withIcon(AzureIcons.Action.EDIT)
94+
.onClicked(e -> new ConfirmAndRunDialog(project, "Initialize from source", "Do you want to initialize using existing code?", "azd init").show());
95+
}
96+
97+
private Node<String> getCreateFromTemplatesNode() {
98+
return new Node<>("Create from templates")
99+
.withIcon(AzureIcons.Common.CREATE)
100+
.onClicked(e -> {
101+
AzdUtils.logTelemetryEvent("azd-show-templates");
102+
new AzdTemplatesDialog(project).show();
103+
});
104+
}
105+
106+
private static boolean isAzdInstalled() {
107+
final String azdVersionJson = runCommand("azd version -o json");
108+
if (azdVersionJson != null && !azdVersionJson.isEmpty()) {
109+
try {
110+
final Map<String, String> response = OBJECT_MAPPER.readValue(azdVersionJson, Map.class);
111+
if (response.containsKey("azd")) {
112+
return true;
113+
}
114+
} catch (JsonProcessingException e) {
115+
}
116+
}
117+
return false;
118+
}
119+
120+
private static boolean isAzdSignedIn() {
121+
final String loginStatusJson = runCommand("azd auth login --check-status -o json");
122+
if (loginStatusJson != null && !loginStatusJson.isEmpty()) {
123+
try {
124+
final Map<String, String> response = OBJECT_MAPPER.readValue(loginStatusJson, Map.class);
125+
if (response.containsKey("status") && "success".equals(response.get("status"))) {
126+
return true;
127+
}
128+
} catch (JsonProcessingException e) {
129+
}
130+
}
131+
return false;
132+
}
133+
134+
public static String runCommand(String command) {
135+
try {
136+
final ProcessBuilder processBuilder = new ProcessBuilder();
137+
// Detect OS and set the appropriate command
138+
String os = System.getProperty("os.name").toLowerCase();
139+
if (os.contains("win")) {
140+
processBuilder.command("cmd", "/c", command); // Windows
141+
} else {
142+
processBuilder.command("bash", "-c", command); // Linux/Unix
143+
}
144+
145+
Process process = processBuilder.start();
146+
147+
// Read the command output
148+
try (BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()))) {
149+
String output = reader.lines().collect(Collectors.joining("\n"));
150+
int exitCode = process.waitFor();
151+
if (exitCode != 0) {
152+
return null;
153+
}
154+
return output;
155+
}
156+
} catch (Exception e) {
157+
return null; // Handle error appropriately
158+
}
159+
}
160+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,182 @@
1+
package com.microsoft.azure.toolkit.intellij.explorer.azd;
2+
3+
import java.util.List;
4+
5+
/**
6+
* Represents a template in the Azure Developer CLI (azd) ecosystem.
7+
* <p>
8+
* This class encapsulates metadata for an azd template, including its title, description, preview image, author information,
9+
* source, tags, and supported languages. It provides getter and setter methods for each property, allowing for fluent configuration.
10+
* </p>
11+
*/
12+
public final class AzdTemplate {
13+
14+
private String title;
15+
private String description;
16+
private String preview;
17+
private String authorUrl;
18+
private String author;
19+
private String source;
20+
private List<String> tags;
21+
private List<String> languages;
22+
23+
/**
24+
* Gets the title of the azd template.
25+
*
26+
* @return the template title
27+
*/
28+
public String getTitle() {
29+
return title;
30+
}
31+
32+
/**
33+
* Sets the title of the azd template.
34+
*
35+
* @param title the template title
36+
* @return the current AzdTemplate instance
37+
*/
38+
public AzdTemplate setTitle(String title) {
39+
this.title = title;
40+
return this;
41+
}
42+
43+
/**
44+
* Gets the description of the azd template.
45+
*
46+
* @return the template description
47+
*/
48+
public String getDescription() {
49+
return description;
50+
}
51+
52+
/**
53+
* Sets the description of the azd template.
54+
*
55+
* @param description the template description
56+
* @return the current AzdTemplate instance
57+
*/
58+
public AzdTemplate setDescription(String description) {
59+
this.description = description;
60+
return this;
61+
}
62+
63+
/**
64+
* Gets the preview image URL of the azd template.
65+
*
66+
* @return the preview image URL
67+
*/
68+
public String getPreview() {
69+
return preview;
70+
}
71+
72+
/**
73+
* Sets the preview image URL of the azd template.
74+
*
75+
* @param preview the preview image URL
76+
* @return the current AzdTemplate instance
77+
*/
78+
public AzdTemplate setPreview(String preview) {
79+
this.preview = preview;
80+
return this;
81+
}
82+
83+
/**
84+
* Gets the author URL of the azd template.
85+
*
86+
* @return the author URL
87+
*/
88+
public String getAuthorUrl() {
89+
return authorUrl;
90+
}
91+
92+
/**
93+
* Sets the author URL of the azd template.
94+
*
95+
* @param authorUrl the author URL
96+
* @return the current AzdTemplate instance
97+
*/
98+
public AzdTemplate setAuthorUrl(String authorUrl) {
99+
this.authorUrl = authorUrl;
100+
return this;
101+
}
102+
103+
/**
104+
* Gets the author of the azd template.
105+
*
106+
* @return the author name
107+
*/
108+
public String getAuthor() {
109+
return author;
110+
}
111+
112+
/**
113+
* Sets the author of the azd template.
114+
*
115+
* @param author the author name
116+
* @return the current AzdTemplate instance
117+
*/
118+
public AzdTemplate setAuthor(String author) {
119+
this.author = author;
120+
return this;
121+
}
122+
123+
/**
124+
* Gets the source URL of the azd template.
125+
*
126+
* @return the source URL
127+
*/
128+
public String getSource() {
129+
return source;
130+
}
131+
132+
/**
133+
* Sets the source URL of the azd template.
134+
*
135+
* @param source the source URL
136+
* @return the current AzdTemplate instance
137+
*/
138+
public AzdTemplate setSource(String source) {
139+
this.source = source;
140+
return this;
141+
}
142+
143+
/**
144+
* Gets the tags associated with the azd template.
145+
*
146+
* @return a list of tags
147+
*/
148+
public List<String> getTags() {
149+
return tags;
150+
}
151+
152+
/**
153+
* Sets the tags associated with the azd template.
154+
*
155+
* @param tags a list of tags
156+
* @return the current AzdTemplate instance
157+
*/
158+
public AzdTemplate setTags(List<String> tags) {
159+
this.tags = tags;
160+
return this;
161+
}
162+
163+
/**
164+
* Gets the supported languages for the azd template.
165+
*
166+
* @return a list of supported languages
167+
*/
168+
public List<String> getLanguages() {
169+
return languages;
170+
}
171+
172+
/**
173+
* Sets the supported languages for the azd template.
174+
*
175+
* @param languages a list of supported languages
176+
* @return the current AzdTemplate instance
177+
*/
178+
public AzdTemplate setLanguages(List<String> languages) {
179+
this.languages = languages;
180+
return this;
181+
}
182+
}

0 commit comments

Comments
 (0)