From 1258f62fd3167c19bba3a7d1c12c3796950bea32 Mon Sep 17 00:00:00 2001 From: Aleksey Pivovarov Date: Mon, 1 Jul 2013 12:45:45 +0400 Subject: [PATCH] Github: share project on github refactoring: split into more functions --- .../plugins/github/GithubShareAction.java | 367 ++++++++++-------- .../jetbrains/plugins/github/GithubUser.java | 10 +- 2 files changed, 209 insertions(+), 168 deletions(-) diff --git a/plugins/github/src/org/jetbrains/plugins/github/GithubShareAction.java b/plugins/github/src/org/jetbrains/plugins/github/GithubShareAction.java index cfbcc6367fb3..4403a29276e6 100644 --- a/plugins/github/src/org/jetbrains/plugins/github/GithubShareAction.java +++ b/plugins/github/src/org/jetbrains/plugins/github/GithubShareAction.java @@ -37,7 +37,6 @@ import com.intellij.util.containers.HashSet; import git4idea.DialogManager; import git4idea.GitLocalBranch; import git4idea.GitUtil; -import git4idea.GitVcs; import git4idea.actions.BasicAction; import git4idea.actions.GitInit; import git4idea.commands.*; @@ -48,6 +47,7 @@ import git4idea.util.GitFileUtils; import git4idea.util.GitUIUtil; import icons.GithubIcons; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import org.jetbrains.plugins.github.ui.GithubShareDialog; import java.io.IOException; @@ -78,24 +78,27 @@ public class GithubShareAction extends DumbAwareAction { // get gitRepository // check for existing git repo - // check available repos (net) - // check privateRepo access (net) + // check available repos and privateRepo access (net) // Show dialog (window) // create GitHub repo (net) - // create local git repo + // create local git repo (if not exist) // add GitHub as a remote host // make first commit // push everything (net) @Override public void actionPerformed(final AnActionEvent e) { - // get gitRepository final Project project = e.getData(PlatformDataKeys.PROJECT); if (project == null || !GithubUtil.testGitExecutable(project)) { return; } + shareProjectOnGithub(project); + } + + public static void shareProjectOnGithub(@NotNull final Project project) { BasicAction.saveAll(); + // get gitRepository final VirtualFile root = project.getBaseDir(); final GitRepositoryManager manager = GitUtil.getRepositoryManager(project); final GitRepository gitRepository = manager.getRepositoryForFile(root); @@ -109,15 +112,85 @@ public class GithubShareAction extends DumbAwareAction { GithubNotifications.showInfoURL(project, "Project is already on GitHub", "GitHub", githubRemote); return; } - else { - externalRemoteDetected = !gitRepository.getRemotes().isEmpty(); - } + externalRemoteDetected = !gitRepository.getRemotes().isEmpty(); } // get available GitHub repos with modal progress - final Ref> repoNamesRef = new Ref>(); - final Ref userInfoRef = new Ref(); - final Ref authRef = new Ref(); + final GithubInfo githubInfo = loadGithubInfoWithModal(project); + if (githubInfo == null) { + return; + } + + // Show dialog (window) + final GithubShareDialog shareDialog = + new GithubShareDialog(project, githubInfo.getRepositoryNames(), githubInfo.getUser().canCreatePrivateRepo()); + //shareDialog.show(); + DialogManager.show(shareDialog); + if (!shareDialog.isOK()) { + return; + } + final boolean isPrivate = shareDialog.isPrivate(); + final String name = shareDialog.getRepositoryName(); + final String description = shareDialog.getDescription(); + + // finish the job in background + final boolean finalExternalRemoteDetected = externalRemoteDetected; + new Task.Backgroundable(project, "Sharing project on GitHub") { + @Override + public void run(@NotNull ProgressIndicator indicator) { + // create GitHub repo (network) + LOG.info("Creating GitHub repository"); + indicator.setText("Creating GitHub repository"); + final String url = createGithubRepository(project, githubInfo.getAuthData(), name, description, isPrivate); + if (url == null) { + return; + } + LOG.info("Successfully created GitHub repository"); + + // creating empty git repo if git is not initialized + LOG.info("Binding local project with GitHub"); + if (!gitDetected) { + LOG.info("No git detected, creating empty git repo"); + indicator.setText("Creating empty git repo"); + if (!createEmptyGitRepository(project, root, indicator)) { + return; + } + } + + GitRepositoryManager repositoryManager = ServiceManager.getService(project, GitRepositoryManager.class); + final GitRepository repository = repositoryManager.getRepositoryForRoot(root); + LOG.assertTrue(repository != null, "GitRepository is null for root " + root); + + final String remoteUrl = GithubApiUtil.getGitHost() + "/" + githubInfo.getUser().getLogin() + "/" + name + ".git"; + final String remoteName = finalExternalRemoteDetected ? "github" : "origin"; + + //git remote add origin git@github.com:login/name.git + LOG.info("Adding GitHub as a remote host"); + indicator.setText("Adding GitHub as a remote host"); + if (!addGithubRemote(project, root, remoteName, remoteUrl, repository)) { + return; + } + + // create sample commit for binding project + if (!performFirstCommitIfRequired(project, root, repository, indicator)) { + return; + } + + //git push origin master + LOG.info("Pushing to github master"); + indicator.setText("Pushing to github master"); + if (!pushCurrentBranch(project, repository, remoteName, remoteUrl, name)) { + return; + } + + GithubNotifications.showInfoURL(project, "Successfully created project on GitHub", name, url); + } + }.queue(); + } + + @Nullable + private static GithubInfo loadGithubInfoWithModal(@NotNull final Project project) { + final Ref githubInfoRef = new Ref(); final Ref exceptionRef = new Ref(); ProgressManager.getInstance().run(new Task.Modal(project, "Access to GitHub", true) { public void run(@NotNull ProgressIndicator indicator) { @@ -138,12 +211,13 @@ public class GithubShareAction extends DumbAwareAction { for (RepositoryInfo info : availableReposRef.get()) { names.add(info.getName()); } - repoNamesRef.set(names); // check access to private repos (network) final GithubUser userInfo = GithubUtil.getCurrentUserInfo(auth); - userInfoRef.set(userInfo); - authRef.set(auth); + if (userInfo == null) { + return; + } + githubInfoRef.set(new GithubInfo(auth, userInfo, names)); } catch (IOException e) { exceptionRef.set(e); @@ -152,138 +226,58 @@ public class GithubShareAction extends DumbAwareAction { }); if (!exceptionRef.isNull()) { GithubNotifications.showErrorDialog(project, "Failed to connect to GitHub", exceptionRef.get().getMessage()); - return; + return null; } - if (repoNamesRef.isNull() || userInfoRef.isNull()) { + if (githubInfoRef.isNull()) { GithubNotifications.showErrorDialog(project, "Failed to connect to GitHub", "Failed to gather user information"); - return; - } - - // Show dialog (window) - final GithubShareDialog shareDialog = - new GithubShareDialog(project, repoNamesRef.get(), userInfoRef.get().getMaxPrivateRepos() > userInfoRef.get().getPrivateRepos()); - //shareDialog.show(); - DialogManager.show(shareDialog); - if (!shareDialog.isOK()) { - return; - } - final boolean isPrivate = shareDialog.isPrivate(); - final String name = shareDialog.getRepositoryName(); - final String description = shareDialog.getDescription(); - - // finish the job in background - final boolean finalExternalRemoteDetected = externalRemoteDetected; - new Task.Backgroundable(project, "Sharing project on GitHub") { - @Override - public void run(@NotNull ProgressIndicator indicator) { - try { - // create GitHub repo (network) - LOG.info("Creating GitHub repository"); - indicator.setText("Creating GitHub repository"); - if (createGithubRepository(authRef.get(), name, description, isPrivate)) { - LOG.info("Successfully created GitHub repository"); - } - else { - GithubNotifications.showError(project, "Creating GitHub Repository", "Failed to create new GitHub repository"); - return; - } - - LOG.info("Binding local project with GitHub"); - // creating empty git repo if git is not initialized - if (!gitDetected) { - if (!createEmptyGitRepository(project, root, indicator)) { - return; - } - } - - GitRepositoryManager repositoryManager = ServiceManager.getService(project, GitRepositoryManager.class); - final GitRepository repository = repositoryManager.getRepositoryForRoot(root); - LOG.assertTrue(repository != null, "GitRepository is null for root " + root); - - //git remote add origin git@github.com:login/name.git - LOG.info("Adding GitHub as a remote host"); - indicator.setText("Adding GitHub as a remote host"); - final GitSimpleHandler addRemoteHandler = new GitSimpleHandler(project, root, GitCommand.REMOTE); - addRemoteHandler.setSilent(true); - final String remoteUrl = GithubApiUtil.getGitHost() + "/" + userInfoRef.get().getLogin() + "/" + name + ".git"; - final String remoteName = finalExternalRemoteDetected ? "github" : "origin"; - addRemoteHandler.addParameters("add", remoteName, remoteUrl); - try { - addRemoteHandler.run(); - repository.update(); - if (addRemoteHandler.getExitCode() != 0) { - GithubNotifications - .showError(project, "Failed to add GitHub repository as remote", "Failed to add GitHub repository as remote"); - return; - } - } - catch (VcsException e) { - GithubNotifications.showError(project, "Failed to add GitHub repository as remote", e.getMessage()); - LOG.info("Failed to add GitHub as remote: " + e.getMessage()); - return; - } - - // In this case we should create sample commit for binding project - if (!performFirstCommitIfRequired(project, root, indicator)) { - return; - } - - //git push origin master - LOG.info("Pushing to github master"); - indicator.setText("Pushing to github master"); - Git git = ServiceManager.getService(Git.class); - - GitLocalBranch currentBranch = repository.getCurrentBranch(); - if (currentBranch == null) { - GithubNotifications.showError(project, "Can't finish GitHub sharing process", "Successfully created project '" + - name + - "' on GitHub, but initial push failed: " + - "no current branch"); - return; - } - GitCommandResult result = git.push(repository, remoteName, remoteUrl, currentBranch.getName()); - if (result.success()) { - GithubNotifications.showInfo(project, "Success", "Successfully created project '" + name + "' on GitHub"); - } - else { - GithubNotifications.showError(project, "Can't finish GitHub sharing process", "Successfully created project '" + - name + - "' on GitHub, but initial push failed:
" + - result.getErrorOutputAsHtmlString()); - } - } - catch (IOException e) { - exceptionRef.set(e); - } - } - }.queue(); - if (!exceptionRef.isNull()) { - GithubNotifications.showError(project, "Failed to create new GitHub repository", exceptionRef.get()); + return null; } + return githubInfoRef.get(); } - private static boolean createGithubRepository(@NotNull GithubAuthData auth, - @NotNull String name, - @NotNull String description, - boolean aPrivate) throws IOException { + @Nullable + private static String createGithubRepository(@NotNull Project project, + @NotNull GithubAuthData auth, + @NotNull String name, + @NotNull String description, + boolean isPrivate) { String path = "/user/repos"; - String requestBody = prepareRequest(name, description, aPrivate); - JsonElement result = GithubApiUtil.postRequest(auth, path, requestBody); + String requestBody = prepareRequest(name, description, isPrivate); + JsonElement result; + try { + result = GithubApiUtil.postRequest(auth, path, requestBody); + } + catch (IOException e) { + GithubNotifications.showError(project, "Creating GitHub Repository", e); + return null; + } if (result == null) { - return false; + GithubNotifications.showError(project, "Creating GitHub Repository", "Failed to create new GitHub repository"); + return null; } if (!result.isJsonObject()) { LOG.error(String.format("Unexpected JSON result format: %s", result)); - return false; + GithubNotifications.showError(project, "Creating GitHub Repository", "Failed to create new GitHub repository"); + return null; } - return result.getAsJsonObject().has("url"); + if (!result.getAsJsonObject().has("html_url")) { + GithubNotifications.showError(project, "Creating GitHub Repository", "Failed to create new GitHub repository"); + return null; + } + return result.getAsJsonObject().get("html_url").getAsString(); + } + + private static String prepareRequest(String name, String description, boolean isPrivate) { + JsonObject json = new JsonObject(); + json.addProperty("name", name); + json.addProperty("description", description); + json.addProperty("public", Boolean.toString(!isPrivate)); + return json.toString(); } private static boolean createEmptyGitRepository(@NotNull Project project, @NotNull VirtualFile root, @NotNull ProgressIndicator indicator) { - LOG.info("No git detected, creating empty git repo"); - indicator.setText("Creating empty git repo"); final GitLineHandler h = new GitLineHandler(project, root, GitCommand.INIT); GitHandlerUtil.runInCurrentThread(h, indicator, true, GitBundle.getString("initializing.title")); if (!h.errors().isEmpty()) { @@ -295,49 +289,45 @@ public class GithubShareAction extends DumbAwareAction { return true; } - private static String prepareRequest(String name, String description, boolean isPrivate) { - JsonObject json = new JsonObject(); - json.addProperty("name", name); - json.addProperty("description", description); - json.addProperty("public", Boolean.toString(!isPrivate)); - return json.toString(); - + private static boolean addGithubRemote(@NotNull Project project, + @NotNull VirtualFile root, + @NotNull String remoteName, + @NotNull String remoteUrl, + @NotNull GitRepository repository) { + final GitSimpleHandler addRemoteHandler = new GitSimpleHandler(project, root, GitCommand.REMOTE); + addRemoteHandler.setSilent(true); + addRemoteHandler.addParameters("add", remoteName, remoteUrl); + try { + addRemoteHandler.run(); + repository.update(); + if (addRemoteHandler.getExitCode() != 0) { + GithubNotifications.showError(project, "Failed to add GitHub repository as remote", "Failed to add GitHub repository as remote"); + return false; + } + } + catch (VcsException e) { + GithubNotifications.showError(project, "Failed to add GitHub repository as remote", e.getMessage()); + LOG.info("Failed to add GitHub as remote: " + e.getMessage()); + return false; + } + return true; } - // check if there is no commits - // ask for files to add - // commit private static boolean performFirstCommitIfRequired(@NotNull Project project, - @NotNull final VirtualFile root, + @NotNull VirtualFile root, + @NotNull GitRepository repository, @NotNull ProgressIndicator indicator) { - // get repository - final GitVcs gitVcs = GitVcs.getInstance(project); - if (gitVcs == null) { - GithubNotifications.showError(project, "Failed to perform initial commit", "Cannot find git initialized"); - return false; - } - - GitRepositoryManager repositoryManager = ServiceManager.getService(project, GitRepositoryManager.class); - Git git = ServiceManager.getService(Git.class); - if (repositoryManager == null || git == null) { - return false; - } - GitRepository repository = repositoryManager.getRepositoryForRoot(root); - if (repository == null) { - GithubNotifications.showError(project, "Failed to perform initial commit", "Cannot find git repository for root " + root); - return false; - } + // check if there is no commits if (!repository.isFresh()) { return true; } - // commit LOG.info("Trying to commit"); - indicator.setText("Trying to commit"); try { LOG.info("Adding files for commit"); indicator.setText("Adding files to git"); + // ask for files to add List untrackedFiles = new ArrayList(repository.getUntrackedFilesHolder().retrieveUntrackedFiles()); final GithubUntrackedFilesDialog dialog = new GithubUntrackedFilesDialog(project, untrackedFiles); ApplicationManager.getApplication().invokeAndWait(new Runnable() { @@ -353,6 +343,7 @@ public class GithubShareAction extends DumbAwareAction { } GitFileUtils.addFiles(project, root, files2add); + // commit LOG.info("Performing commit"); indicator.setText("Performing commit"); GitSimpleHandler handler = new GitSimpleHandler(project, root, GitCommand.COMMIT); @@ -365,14 +356,68 @@ public class GithubShareAction extends DumbAwareAction { GithubNotifications.showError(project, "Failed to commit file during post activities", e.getMessage()); return false; } + LOG.info("Successfully created initial commit"); return true; } - private static class GithubUntrackedFilesDialog extends SelectFilesDialog { + private static boolean pushCurrentBranch(@NotNull Project project, + @NotNull GitRepository repository, + @NotNull String remoteName, + @NotNull String remoteUrl, + @NotNull String name) { + Git git = ServiceManager.getService(Git.class); + + GitLocalBranch currentBranch = repository.getCurrentBranch(); + if (currentBranch == null) { + GithubNotifications.showError(project, "Can't finish GitHub sharing process", "Successfully created project '" + + name + + "' on GitHub, but initial push failed: " + + "no current branch"); + return false; + } + GitCommandResult result = git.push(repository, remoteName, remoteUrl, currentBranch.getName()); + if (!result.success()) { + GithubNotifications.showError(project, "Can't finish GitHub sharing process", "Successfully created project '" + + name + + "' on GitHub, but initial push failed:
" + + result.getErrorOutputAsHtmlString()); + return false; + } + return true; + } + + public static class GithubUntrackedFilesDialog extends SelectFilesDialog { public GithubUntrackedFilesDialog(@NotNull Project project, @NotNull List untrackedFiles) { super(project, untrackedFiles, "Add files to Git", VcsShowConfirmationOption.STATIC_SHOW_CONFIRMATION, true, false, false); init(); } } + + private static class GithubInfo { + @NotNull private final GithubUser myUser; + @NotNull private final GithubAuthData myAuthData; + @NotNull private final HashSet myRepositoryNames; + + GithubInfo(@NotNull GithubAuthData auth, @NotNull GithubUser user, @NotNull HashSet repositoryNames) { + myUser = user; + myAuthData = auth; + myRepositoryNames = repositoryNames; + } + + @NotNull + public GithubUser getUser() { + return myUser; + } + + @NotNull + public GithubAuthData getAuthData() { + return myAuthData; + } + + @NotNull + public HashSet getRepositoryNames() { + return myRepositoryNames; + } + } } diff --git a/plugins/github/src/org/jetbrains/plugins/github/GithubUser.java b/plugins/github/src/org/jetbrains/plugins/github/GithubUser.java index 6695dfa0f95a..28cc33b1df03 100644 --- a/plugins/github/src/org/jetbrains/plugins/github/GithubUser.java +++ b/plugins/github/src/org/jetbrains/plugins/github/GithubUser.java @@ -35,16 +35,12 @@ class GithubUser { } @NotNull - String getLogin() { + public String getLogin() { return myLogin; } - int getMaxPrivateRepos() { - return myMaxPrivateRepos; - } - - int getPrivateRepos() { - return myPrivateRepos; + public boolean canCreatePrivateRepo() { + return myMaxPrivateRepos > myPrivateRepos; } }