Github: share project on github refactoring: split into more functions

This commit is contained in:
Aleksey Pivovarov
2013-07-02 19:03:26 +04:00
parent aefde8b5c2
commit 1258f62fd3
2 changed files with 209 additions and 168 deletions
@@ -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<HashSet<String>> repoNamesRef = new Ref<HashSet<String>>();
final Ref<GithubUser> userInfoRef = new Ref<GithubUser>();
final Ref<GithubAuthData> authRef = new Ref<GithubAuthData>();
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<GithubInfo> githubInfoRef = new Ref<GithubInfo>();
final Ref<IOException> exceptionRef = new Ref<IOException>();
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:<br/>" +
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<VirtualFile> untrackedFiles = new ArrayList<VirtualFile>(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:<br/>" +
result.getErrorOutputAsHtmlString());
return false;
}
return true;
}
public static class GithubUntrackedFilesDialog extends SelectFilesDialog {
public GithubUntrackedFilesDialog(@NotNull Project project, @NotNull List<VirtualFile> 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<String> myRepositoryNames;
GithubInfo(@NotNull GithubAuthData auth, @NotNull GithubUser user, @NotNull HashSet<String> repositoryNames) {
myUser = user;
myAuthData = auth;
myRepositoryNames = repositoryNames;
}
@NotNull
public GithubUser getUser() {
return myUser;
}
@NotNull
public GithubAuthData getAuthData() {
return myAuthData;
}
@NotNull
public HashSet<String> getRepositoryNames() {
return myRepositoryNames;
}
}
}
@@ -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;
}
}