From 21532aa8314a2b5bd318e9af17b380841c8fa81f Mon Sep 17 00:00:00 2001 From: Benjamin Wulfe Date: Tue, 15 Jul 2014 11:40:10 -0400 Subject: Creates a simple deploy dialog using appcfg. Added deploy to App Engine from dialog. On demand login using oauth2 Hides auth tokens and id's in output pane using '*****'. Validation/Error messages when no modules to upload or bad login Auto select if only one module to upload Touched up layout of dialog (alignment, etc) Note: We need to add the login module to tools/idea/.idea/modules.xml so that its exposed as a possible reference from the appengine module. Change-Id: I5ed88539a2dc588758d0cfcb73cc7a93585f54fe --- google-cloud-tools.iml | 1 + .../com/google/gct/login/OAuthScopeRegistry.java | 1 + src/META-INF/plugin.xml | 6 + .../appengine/deploy/AppEngineUpdateAction.java | 33 +++ .../appengine/deploy/AppEngineUpdateDialog.form | 75 ++++++ .../appengine/deploy/AppEngineUpdateDialog.java | 275 +++++++++++++++++++++ .../idea/appengine/deploy/AppEngineUpdater.java | 218 ++++++++++++++++ .../gct/idea/appengine/dom/AppEngineWebApp.java | 27 ++ .../appengine/dom/AppEngineWebFileDescription.java | 39 +++ .../gradle/facet/AppEngineGradleFacet.java | 34 +++ .../initialization/CloudPluginRegistration.java | 64 +++++ 11 files changed, 773 insertions(+) create mode 100644 src/com/google/gct/idea/appengine/deploy/AppEngineUpdateAction.java create mode 100644 src/com/google/gct/idea/appengine/deploy/AppEngineUpdateDialog.form create mode 100644 src/com/google/gct/idea/appengine/deploy/AppEngineUpdateDialog.java create mode 100644 src/com/google/gct/idea/appengine/deploy/AppEngineUpdater.java create mode 100644 src/com/google/gct/idea/appengine/dom/AppEngineWebApp.java create mode 100644 src/com/google/gct/idea/appengine/dom/AppEngineWebFileDescription.java create mode 100644 src/com/google/gct/idea/appengine/initialization/CloudPluginRegistration.java diff --git a/google-cloud-tools.iml b/google-cloud-tools.iml index e07f852..9126f4b 100644 --- a/google-cloud-tools.iml +++ b/google-cloud-tools.iml @@ -53,6 +53,7 @@ + diff --git a/login/src/com/google/gct/login/OAuthScopeRegistry.java b/login/src/com/google/gct/login/OAuthScopeRegistry.java index ca2fe2e..47b9686 100644 --- a/login/src/com/google/gct/login/OAuthScopeRegistry.java +++ b/login/src/com/google/gct/login/OAuthScopeRegistry.java @@ -32,6 +32,7 @@ class OAuthScopeRegistry { static { SortedSet scopes = new TreeSet(); scopes.add("https://www.googleapis.com/auth/userinfo#email"); + scopes.add("https://www.googleapis.com/auth/appengine.admin"); sScopes = Collections.unmodifiableSortedSet(scopes); } diff --git a/src/META-INF/plugin.xml b/src/META-INF/plugin.xml index c34f079..c6bc850 100644 --- a/src/META-INF/plugin.xml +++ b/src/META-INF/plugin.xml @@ -18,6 +18,9 @@ com.google.gct.idea.appengine.synchronization.SampleSyncRegistration --> + + com.google.gct.idea.appengine.initialization.CloudPluginRegistration + @@ -44,6 +47,9 @@ --> + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
diff --git a/src/com/google/gct/idea/appengine/deploy/AppEngineUpdateDialog.java b/src/com/google/gct/idea/appengine/deploy/AppEngineUpdateDialog.java new file mode 100644 index 0000000..bd8c2f1 --- /dev/null +++ b/src/com/google/gct/idea/appengine/deploy/AppEngineUpdateDialog.java @@ -0,0 +1,275 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.google.gct.idea.appengine.deploy; + +import com.google.common.base.Strings; +import com.google.gct.idea.appengine.dom.AppEngineWebApp; +import com.google.gct.idea.appengine.dom.AppEngineWebFileDescription; +import com.google.gct.idea.appengine.gradle.facet.AppEngineConfigurationProperties; +import com.google.gct.idea.appengine.gradle.facet.AppEngineGradleFacet; +import com.google.gct.login.GoogleLogin; +import com.google.gct.login.IGoogleLoginCompletedCallback; +import com.intellij.openapi.diagnostic.Logger; +import com.intellij.openapi.module.Module; +import com.intellij.openapi.module.ModuleManager; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.roots.ui.configuration.ModulesCombobox; +import com.intellij.openapi.ui.DialogWrapper; +import com.intellij.openapi.ui.Messages; +import com.intellij.openapi.ui.ValidationInfo; +import com.intellij.openapi.vfs.LocalFileSystem; +import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.packaging.artifacts.Artifact; +import com.intellij.packaging.artifacts.ArtifactManager; +import com.intellij.packaging.elements.PackagingElementResolvingContext; +import com.intellij.packaging.impl.artifacts.ArtifactUtil; +import com.intellij.psi.PsiFile; +import com.intellij.psi.PsiManager; +import com.intellij.psi.xml.XmlFile; +import com.intellij.ui.SortedComboBoxModel; +import com.intellij.util.xml.DomElement; +import com.intellij.util.xml.DomFileElement; +import com.intellij.util.xml.DomManager; +import com.intellij.xml.util.XmlStringUtil; +import org.eclipse.jgit.util.StringUtils; +import org.jetbrains.annotations.Nullable; + +import javax.swing.*; +import java.awt.*; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.io.File; +import java.util.ArrayList; +import java.util.List; + +/** + * AppEngineUpdateDialog shows a dialog allowing the user to select a module and deploy. + */ +public class AppEngineUpdateDialog extends DialogWrapper { + private static final Logger LOG = Logger.getInstance(AppEngineUpdateDialog.class); + + private ModulesCombobox myModuleComboBox; + private JTextField myProjectId; + private JTextField myVersion; + private JPanel myPanel; + private List myDeployableModules; + private Project myProject; + private Module myInitiallySelectedModule; + + private AppEngineUpdateDialog(Project project, List deployableModules, Module selectedModule) { + super(project, true); + myDeployableModules = deployableModules; + myProject = project; + myInitiallySelectedModule = selectedModule; + + init(); + initValidation(); + setTitle("Deploy to App Engine"); + setOKButtonText("Deploy"); + + Window myWindow = getWindow(); + if (myWindow != null) { + myWindow.setPreferredSize(new Dimension(285, 135)); + } + } + + /** + * Shows a dialog to deploy a module to AppEngine. Will force a login if required + * If either the login fails or there are no valid modules to upload, it will return after + * displaying an error. + * + * @param project The project whose modules will be uploaded. + * @param selectedModule The module selected by default in the deploy dialog. Can be null. If null or not a valid app engine module, + * no module will be selected by default. + */ + static void show(final Project project, Module selectedModule) { + + final java.util.List modules = new ArrayList(); + + // Filter the module list by whether we can actually deploy them to appengine. + for (Module module : ModuleManager.getInstance(project).getModules()) { + AppEngineGradleFacet facet = AppEngineGradleFacet.getAppEngineFacetByModule(module); + if (facet != null) { + modules.add(module); + } + } + + // Tell the user what he has to do if he has none. + if (modules.size() == 0) { + //there are no modules to upload -- or we hit a bug due to gradle sync. + //TODO do we need to use the mainwindow as owner? + Messages.showErrorDialog( + XmlStringUtil.wrapInHtml( + "This project does not contain any App Engine modules. To add an App Engine module for your project,
open “File > New Module…” menu and choose one of App Engine modules.") + , "Error"); + return; + } + + if (selectedModule != null && !modules.contains(selectedModule)) { + selectedModule = null; + } + + if (selectedModule == null && modules.size() == 1) { + selectedModule = modules.get(0); + } + + // To invoke later, we need a final local. + final Module passedSelectedModule = selectedModule; + + // Login on demand and queue up the dialog to show after a successful login. + //if login fails, it already shows an error. + if (!GoogleLogin.getInstance().isLoggedIn()) { + // log in on demand... + GoogleLogin.getInstance().logIn(null, new IGoogleLoginCompletedCallback() { + @Override + public void onLoginCompleted() { + if (GoogleLogin.getInstance().isLoggedIn()) { + EventQueue.invokeLater(new Runnable() { + @Override + public void run() { + // Success!, lets run the deploy now. + AppEngineUpdateDialog dialog = new AppEngineUpdateDialog(project, modules, passedSelectedModule); + dialog.show(); + } + }); + } + } + }); + } + else { + AppEngineUpdateDialog dialog = new AppEngineUpdateDialog(project, modules, passedSelectedModule); + dialog.show(); + } + } + + @Nullable + @Override + protected JComponent createCenterPanel() { + @SuppressWarnings("unchecked") + final SortedComboBoxModel model = (SortedComboBoxModel)myModuleComboBox.getModel(); + model.clear(); + model.addAll(myDeployableModules); + + if (myInitiallySelectedModule != null) { + // Auto select if there is only one item + model.setSelectedItem(myInitiallySelectedModule); + populateFields(); + } + + myModuleComboBox.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + populateFields(); + } + }); + return myPanel; + } + + private void populateFields() { + myProjectId.setText(""); + myVersion.setText(""); + + Module appEngineModule = myModuleComboBox.getSelectedModule(); + if (appEngineModule != null) { + AppEngineGradleFacet facet = AppEngineGradleFacet.getAppEngineFacetByModule(appEngineModule); + if (facet == null) { + Messages.showErrorDialog(this.getPeer().getOwner(), "Could not acquire App Engine module information.", "Deploy"); + return; + } + + final AppEngineWebApp appEngineWebApp = facet.getAppEngineWebXml(); + if (appEngineWebApp == null) { + Messages.showErrorDialog(this.getPeer().getOwner(), "Could not locate or parse the appengine-web.xml fle.", "Deploy"); + return; + } + + myProjectId.setText(appEngineWebApp.getApplication().getRawText()); + myVersion.setText(appEngineWebApp.getVersion().getRawText()); + } + } + + @Override + protected void doOKAction() { + if (getOKAction().isEnabled()) { + GoogleLogin login = GoogleLogin.getInstance(); + Module selectedModule = myModuleComboBox.getSelectedModule(); + String sdk = ""; + String war = ""; + AppEngineGradleFacet facet = AppEngineGradleFacet.getAppEngineFacetByModule(selectedModule); + if (facet != null) { + AppEngineConfigurationProperties model = facet.getConfiguration().getState(); + sdk = model.APPENGINE_SDKROOT; + war = model.WAR_DIR; + } + + String client_secret = login.fetchOAuth2ClientSecret(); + String client_id = login.fetchOAuth2ClientId(); + String refresh_token = login.fetchOAuth2RefreshToken(); + + if (StringUtils.isEmptyOrNull(client_secret) || + StringUtils.isEmptyOrNull(client_id) || + StringUtils.isEmptyOrNull(refresh_token)) { + // The login is somehow invalid, bail -- this shouldn't happen. + LOG.error("StartUploading while logged in, but it doesn't have full credentials."); + Messages.showErrorDialog(this.getPeer().getOwner(), "Login credentials are not valid.", "Login"); + return; + } + + // These should not fail as they are a part of the dialog validation. + if (Strings.isNullOrEmpty(sdk) || + Strings.isNullOrEmpty(war) || + Strings.isNullOrEmpty(myProjectId.getText()) || + selectedModule == null) { + Messages.showErrorDialog(this.getPeer().getOwner(), "Could not deploy due to missing information (sdk/war/projectid).", "Deploy"); + LOG.error("StartUploading was called with bad module/sdk/war"); + return; + } + + close(OK_EXIT_CODE); // We close before kicking off the update so it doesn't interfere with the output window coming to focus. + + // Kick off the upload. detailed status will be shown in an output window. + new AppEngineUpdater(myProject, selectedModule, sdk, war, myProjectId.getText(), myVersion.getText(), + client_secret, client_id, refresh_token).startUploading(); + } + } + + @Override + protected ValidationInfo doValidate() { + // These should not normally occur.. + if (!GoogleLogin.getInstance().isLoggedIn()) { + return new ValidationInfo("You must be logged in to perform this action."); + } + + Module module = myModuleComboBox.getSelectedModule(); + if (module == null) { + return new ValidationInfo("Select a module"); + } + + AppEngineGradleFacet facet = AppEngineGradleFacet.getAppEngineFacetByModule(module); + if (facet == null) { + return new ValidationInfo("Could not find App Engine gradle configuration on Module"); + } + + // We'll let AppCfg error if the project is wrong. The user can see this in the console window. + // Note that version can be blank to indicate current version. + if (Strings.isNullOrEmpty(myProjectId.getText())) { + return new ValidationInfo("Please enter a Project ID."); + } + + return null; + } + +} diff --git a/src/com/google/gct/idea/appengine/deploy/AppEngineUpdater.java b/src/com/google/gct/idea/appengine/deploy/AppEngineUpdater.java new file mode 100644 index 0000000..aa48dd4 --- /dev/null +++ b/src/com/google/gct/idea/appengine/deploy/AppEngineUpdater.java @@ -0,0 +1,218 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.google.gct.idea.appengine.deploy; + +import com.android.tools.idea.gradle.invoker.GradleInvoker; +import com.google.common.base.Strings; +import com.google.gct.idea.appengine.sdk.AppEngineSdk; +import com.intellij.execution.ExecutionException; +import com.intellij.execution.ExecutionManager; +import com.intellij.execution.Executor; +import com.intellij.execution.configurations.CommandLineBuilder; +import com.intellij.execution.configurations.GeneralCommandLine; +import com.intellij.execution.configurations.JavaParameters; +import com.intellij.execution.configurations.ParametersList; +import com.intellij.execution.executors.DefaultRunExecutor; +import com.intellij.execution.filters.TextConsoleBuilderFactory; +import com.intellij.execution.process.*; +import com.intellij.execution.ui.ConsoleView; +import com.intellij.execution.ui.RunContentDescriptor; +import com.intellij.execution.ui.RunnerLayoutUi; +import com.intellij.execution.ui.actions.CloseAction; +import com.intellij.openapi.actionSystem.ActionManager; +import com.intellij.openapi.actionSystem.ActionPlaces; +import com.intellij.openapi.actionSystem.DefaultActionGroup; +import com.intellij.openapi.actionSystem.IdeActions; +import com.intellij.openapi.application.ApplicationManager; +import com.intellij.openapi.diagnostic.Logger; +import com.intellij.openapi.fileEditor.FileDocumentManager; +import com.intellij.openapi.module.Module; +import com.intellij.openapi.progress.ProgressIndicator; +import com.intellij.openapi.progress.ProgressManager; +import com.intellij.openapi.progress.Task; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.ui.Messages; +import com.intellij.openapi.util.Key; +import com.intellij.openapi.util.KeyValue; +import com.intellij.openapi.util.io.FileUtil; +import com.intellij.util.net.HttpConfigurable; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.awt.*; +import java.util.List; + +/** + * Compiles and deploys a module to AppEngine using AppCfg. + * + * @author benwu + */ +class AppEngineUpdater { + private static final Logger LOG = Logger.getInstance("#com.google.gct.idea.appengine.deploy.AppEngineUpdater"); + private final Project myProject; + private final Module myModule; + private final String myExplodedWarPath; + private final String mySdkPath; + private final String myClientSecret; + private final String myClientId; + private final String myRefreshToken; + private final String myVersion; + private final String myAppEngineProject; + + AppEngineUpdater(Project project, + Module module, + String sdkPath, + String explodedWarPath, + String appEngineProject, + String version, + String clientSecret, + String clientId, + String refreshToken) { + myProject = project; + myModule = module; + mySdkPath = sdkPath; + myExplodedWarPath = explodedWarPath; + myClientSecret = clientSecret; + myClientId = clientId; + myRefreshToken = refreshToken; + myVersion = version; + myAppEngineProject = appEngineProject; + } + + /** + * Starts the compile and upload async process. + */ + void startUploading() { + FileDocumentManager.getInstance().saveAllDocuments(); + ProgressManager.getInstance().run(new Task.Backgroundable(myModule.getProject(), "Deploying application", true, null) { + @Override + public void run(@NotNull ProgressIndicator indicator) { + compileAndUpload(); + } + }); + } + + private void compileAndUpload() { + final Runnable startUploading = new Runnable() { + @Override + public void run() { + ApplicationManager.getApplication().invokeLater(new Runnable() { + @Override + public void run() { + startUploadingProcess(); + } + }); + } + }; + + GradleInvoker.getInstance(myProject).compileJava(new Module[]{myModule}); + startUploading.run(); + } + + private void startUploadingProcess() { + final Process process; + final GeneralCommandLine commandLine; + + try { + JavaParameters parameters = new JavaParameters(); + parameters.configureByModule(myModule, JavaParameters.JDK_ONLY); + parameters.setMainClass("com.google.appengine.tools.admin.AppCfg"); + AppEngineSdk mySdk = new AppEngineSdk(mySdkPath); + parameters.getClassPath().add(mySdk.getToolsApiJarFile().getAbsolutePath()); + + final List> list = HttpConfigurable.getJvmPropertiesList(false, null); + if (!list.isEmpty()) { + final ParametersList parametersList = parameters.getVMParametersList(); + for (KeyValue value : list) { + parametersList.defineProperty(value.getKey(), value.getValue()); + } + } + + final ParametersList programParameters = parameters.getProgramParametersList(); + programParameters.add("--application=" + myAppEngineProject); + if (!Strings.isNullOrEmpty(myVersion)) { + programParameters.add("--version=" + myVersion); + } + programParameters.add("--oauth2"); + programParameters.add("--oauth2_client_secret=" + myClientSecret); + programParameters.add("--oauth2_client_id=" + myClientId); + programParameters.add("--oauth2_refresh_token=" + myRefreshToken); + programParameters.add("update"); + programParameters.add(FileUtil.toSystemDependentName(myExplodedWarPath)); + + commandLine = CommandLineBuilder.createFromJavaParameters(parameters); + + process = commandLine.createProcess(); + } + catch (ExecutionException e) { + final String message = e.getMessage(); + LOG.error("Cannot start uploading: " + message); + + if (!EventQueue.isDispatchThread()) { + EventQueue.invokeLater(new Runnable() { + @Override + public void run() { + Messages.showErrorDialog("Cannot start uploading: " + message, "Error"); + } + }); + } + else { + Messages.showErrorDialog("Cannot start uploading: " + message, "Error"); + } + + return; + } + + final ProcessHandler processHandler = new FilteredOSProcessHandler(process, commandLine.getCommandLineString(), + new String[]{myRefreshToken, myClientSecret, myClientId}); + final Executor executor = DefaultRunExecutor.getRunExecutorInstance(); + final ConsoleView console = TextConsoleBuilderFactory.getInstance().createBuilder(myModule.getProject()).getConsole(); + final RunnerLayoutUi ui = RunnerLayoutUi.Factory.getInstance(myModule.getProject()) + .create("Deploy", "Deploy to AppEngine", "Deploy Application", myModule.getProject()); + final DefaultActionGroup group = new DefaultActionGroup(); + ui.getOptions().setLeftToolbar(group, ActionPlaces.UNKNOWN); + ui.addContent(ui.createContent("upload", console.getComponent(), "Deploy Application", null, console.getPreferredFocusableComponent())); + + console.attachToProcess(processHandler); + final RunContentDescriptor contentDescriptor = + new RunContentDescriptor(console, processHandler, ui.getComponent(), "Deploy to AppEngine"); + group.add(ActionManager.getInstance().getAction(IdeActions.ACTION_STOP_PROGRAM)); + group.add(new CloseAction(executor, contentDescriptor, myModule.getProject())); + + ExecutionManager.getInstance(myModule.getProject()).getContentManager().showRunContent(executor, contentDescriptor); + processHandler.startNotify(); + } + + private class FilteredOSProcessHandler extends OSProcessHandler { + String[] tokensToFilter; + + FilteredOSProcessHandler(@NotNull final Process process, @Nullable final String commandLine, String[] filteredTokens) { + super(process, commandLine); + tokensToFilter = filteredTokens; + } + + @Override + public void notifyTextAvailable(final String text, final Key outputType) { + String newText = text; + if (tokensToFilter != null) { + for (String token : tokensToFilter) { + newText = newText.replace(token, "*****"); + } + } + super.notifyTextAvailable(newText, outputType); + } + } +} diff --git a/src/com/google/gct/idea/appengine/dom/AppEngineWebApp.java b/src/com/google/gct/idea/appengine/dom/AppEngineWebApp.java new file mode 100644 index 0000000..ce3ba7d --- /dev/null +++ b/src/com/google/gct/idea/appengine/dom/AppEngineWebApp.java @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.google.gct.idea.appengine.dom; + +import com.intellij.util.xml.DomElement; +import com.intellij.util.xml.GenericDomValue; + +/** + * This is the Dom for the App Engine config file. + */ +public interface AppEngineWebApp extends DomElement { + GenericDomValue getApplication(); + GenericDomValue getVersion(); +} diff --git a/src/com/google/gct/idea/appengine/dom/AppEngineWebFileDescription.java b/src/com/google/gct/idea/appengine/dom/AppEngineWebFileDescription.java new file mode 100644 index 0000000..9efe86f --- /dev/null +++ b/src/com/google/gct/idea/appengine/dom/AppEngineWebFileDescription.java @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.google.gct.idea.appengine.dom; + +import com.intellij.openapi.module.Module; +import com.intellij.psi.xml.XmlFile; +import com.intellij.util.xml.DomFileDescription; +import org.jetbrains.annotations.NonNls; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** + * This is the file description for the App Engine xml config file + */ +public class AppEngineWebFileDescription extends DomFileDescription { + @NonNls public static final String APP_ENGINE_WEB_XML_NAME = "appengine-web.xml"; + + public AppEngineWebFileDescription() { + super(AppEngineWebApp.class, "appengine-web-app"); + } + + @Override + public boolean isMyFile(@NotNull XmlFile file, @Nullable Module module) { + return file.getName().equals(APP_ENGINE_WEB_XML_NAME); + } +} diff --git a/src/com/google/gct/idea/appengine/gradle/facet/AppEngineGradleFacet.java b/src/com/google/gct/idea/appengine/gradle/facet/AppEngineGradleFacet.java index 48df14f..198f472 100644 --- a/src/com/google/gct/idea/appengine/gradle/facet/AppEngineGradleFacet.java +++ b/src/com/google/gct/idea/appengine/gradle/facet/AppEngineGradleFacet.java @@ -15,6 +15,8 @@ */ package com.google.gct.idea.appengine.gradle.facet; +import com.google.common.base.Strings; +import com.google.gct.idea.appengine.dom.AppEngineWebApp; import com.intellij.facet.Facet; import com.intellij.facet.FacetManager; import com.intellij.facet.FacetType; @@ -22,10 +24,18 @@ import com.intellij.facet.FacetTypeId; import com.intellij.facet.FacetTypeRegistry; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.module.Module; +import com.intellij.openapi.vfs.LocalFileSystem; +import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.psi.PsiFile; +import com.intellij.psi.PsiManager; +import com.intellij.psi.xml.XmlFile; +import com.intellij.util.xml.DomManager; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import java.io.File; + /** * App Engine Gradle facet for App Engine Modules with a Gradle build file */ @@ -50,6 +60,30 @@ public class AppEngineGradleFacet extends Facet getFacetType() { return FacetTypeRegistry.getInstance().findFacetType(ID); } diff --git a/src/com/google/gct/idea/appengine/initialization/CloudPluginRegistration.java b/src/com/google/gct/idea/appengine/initialization/CloudPluginRegistration.java new file mode 100644 index 0000000..1f103af --- /dev/null +++ b/src/com/google/gct/idea/appengine/initialization/CloudPluginRegistration.java @@ -0,0 +1,64 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.google.gct.idea.appengine.initialization; + +import com.google.gct.idea.appengine.deploy.AppEngineUpdateAction; +import com.intellij.openapi.actionSystem.ActionManager; +import com.intellij.openapi.actionSystem.Anchor; +import com.intellij.openapi.actionSystem.Constraints; +import com.intellij.openapi.actionSystem.DefaultActionGroup; +import com.intellij.openapi.components.ApplicationComponent; +import org.jetbrains.annotations.NotNull; + +/** + * Initializes the menus for deploy. + */ +public class CloudPluginRegistration implements ApplicationComponent { + + // We are reusing the flag for login. + private final static String SHOW_DEPLOY = "show.google.login.button"; + + public CloudPluginRegistration() { + } + + @Override + public void initComponent() { + if (Boolean.getBoolean(SHOW_DEPLOY)) { + ActionManager am = ActionManager.getInstance(); + + AppEngineUpdateAction action = new AppEngineUpdateAction(); + action.getTemplatePresentation().setText("Deploy Module to App Engine..."); + + am.registerAction("GoogleCloudTools.AppEngineUpdate", action); + DefaultActionGroup buildMenu = (DefaultActionGroup)am.getAction("BuildMenu"); + + DefaultActionGroup appEngineUpdateGroup = new DefaultActionGroup(); + appEngineUpdateGroup.addSeparator(); + appEngineUpdateGroup.add(action); + buildMenu.add(appEngineUpdateGroup, new Constraints(Anchor.AFTER, "Compile")); + } + } + + @Override + public void disposeComponent() { + } + + @NotNull + @Override + public String getComponentName() { + return "CloudPluginRegistration"; + } +} -- cgit v1.2.3