mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
[github] Pull request merge actions
This commit is contained in:
@@ -175,14 +175,12 @@ public final class HttpRequests {
|
||||
|
||||
@NotNull
|
||||
public static RequestBuilder post(@NotNull String url, @Nullable String contentType) {
|
||||
return new RequestBuilderImpl(url, rawConnection -> {
|
||||
HttpURLConnection connection = (HttpURLConnection)rawConnection;
|
||||
connection.setRequestMethod("POST");
|
||||
connection.setDoOutput(true);
|
||||
if (contentType != null) {
|
||||
connection.setRequestProperty("Content-Type", contentType);
|
||||
}
|
||||
});
|
||||
return requestWithBody(url, "POST", contentType, null);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static RequestBuilder put(@NotNull String url, @Nullable String contentType) {
|
||||
return requestWithBody(url, "PUT", contentType, null);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -193,14 +191,23 @@ public final class HttpRequests {
|
||||
*/
|
||||
@NotNull
|
||||
public static RequestBuilder patch(@NotNull String url, @Nullable String contentType) {
|
||||
return requestWithBody(url, "POST", contentType,
|
||||
connection -> connection.setRequestProperty("X-HTTP-Method-Override", "PATCH"));
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static RequestBuilder requestWithBody(@NotNull String url,
|
||||
@NotNull String requestMethod,
|
||||
@Nullable String contentType,
|
||||
@Nullable ConnectionTuner tuner) {
|
||||
return new RequestBuilderImpl(url, rawConnection -> {
|
||||
HttpURLConnection connection = (HttpURLConnection)rawConnection;
|
||||
connection.setRequestMethod("POST");
|
||||
connection.setRequestProperty("X-HTTP-Method-Override", "PATCH");
|
||||
connection.setRequestMethod(requestMethod);
|
||||
connection.setDoOutput(true);
|
||||
if (contentType != null) {
|
||||
connection.setRequestProperty("Content-Type", contentType);
|
||||
}
|
||||
if (tuner != null) tuner.tune(connection);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -592,12 +599,12 @@ public final class HttpRequests {
|
||||
|
||||
HttpURLConnection httpURLConnection = (HttpURLConnection)connection;
|
||||
String method = httpURLConnection.getRequestMethod();
|
||||
if (method.equals("POST")) {
|
||||
if (method.equals("POST") || method.equals("PUT")) {
|
||||
return connection;
|
||||
}
|
||||
|
||||
LOG.assertTrue(method.equals("GET") || method.equals("HEAD") || method.equals("DELETE"),
|
||||
"'" + method + "' not supported; please use GET, HEAD, DELETE or POST");
|
||||
"'" + method + "' not supported; please use GET, HEAD, DELETE, PUT or POST");
|
||||
|
||||
if (LOG.isDebugEnabled()) LOG.debug("connecting to " + url);
|
||||
int responseCode = httpURLConnection.getResponseCode();
|
||||
|
||||
@@ -75,11 +75,27 @@
|
||||
<add-to-group group-id="EditorTabPopupMenu"/>
|
||||
<add-to-group group-id="ConsoleEditorPopupMenu"/>
|
||||
</action>
|
||||
|
||||
<group id="Github.PullRequest.Merge.Group">
|
||||
<action id="Github.PullRequest.Merge.Commit"
|
||||
class="org.jetbrains.plugins.github.pullrequest.action.GithubPullRequestMergeCommitAction"/>
|
||||
<action id="Github.PullRequest.Merge.Squash" class="org.jetbrains.plugins.github.pullrequest.action.GithubPullRequestSquashAction"/>
|
||||
<action id="Github.PullRequest.Merge.Rebase" class="org.jetbrains.plugins.github.pullrequest.action.GithubPullRequestRebaseAction"/>
|
||||
</group>
|
||||
|
||||
<group id="Github.PullRequest.ToolWindow.List.Toolbar">
|
||||
<group class="org.jetbrains.plugins.github.pullrequest.action.merge.GithubPullRequestMergeActionGroup"
|
||||
popup="true"
|
||||
text="Merge Pull Request"
|
||||
icon="AllIcons.Vcs.Merge">
|
||||
<reference id="Github.PullRequest.Merge.Group"/>
|
||||
</group>
|
||||
<action id="Github.PullRequest.List.Refresh"
|
||||
class="org.jetbrains.plugins.github.pullrequest.action.GithubPullRequestRefreshListAction"/>
|
||||
</group>
|
||||
<group id="Github.PullRequest.ToolWindow.List.Popup">
|
||||
<reference id="Github.PullRequest.Merge.Group"/>
|
||||
<separator/>
|
||||
<action id="Github.PullRequest.Branch.Create"
|
||||
class="org.jetbrains.plugins.github.pullrequest.action.GithubPullRequestCreateBranchAction"/>
|
||||
<reference id="Github.Open.In.Browser"/>
|
||||
|
||||
@@ -118,6 +118,24 @@ sealed class GithubApiRequest<T>(val url: String) {
|
||||
}
|
||||
}
|
||||
|
||||
abstract class Put<T> @JvmOverloads constructor(override val body: String,
|
||||
override val bodyMimeType: String,
|
||||
url: String,
|
||||
override val acceptMimeType: String? = null) : GithubApiRequest.WithBody<T>(url) {
|
||||
companion object {
|
||||
inline fun <reified T> json(url: String, body: Any): Put<T> = Json(url, body, T::class.java)
|
||||
}
|
||||
|
||||
open class Json<T>(url: String, body: Any, clazz: Class<T>) : Put<T>(GithubApiContentHelper.toJson(body),
|
||||
GithubApiContentHelper.JSON_MIME_TYPE,
|
||||
url,
|
||||
GithubApiContentHelper.V3_JSON_MIME_TYPE) {
|
||||
private val typeToken = TypeToken.get(clazz)
|
||||
|
||||
override fun extractResult(response: GithubApiResponse): T = parseJsonResponse(response, typeToken)
|
||||
}
|
||||
}
|
||||
|
||||
abstract class Patch<T> @JvmOverloads constructor(override val body: String,
|
||||
override val bodyMimeType: String,
|
||||
url: String,
|
||||
|
||||
@@ -142,6 +142,7 @@ sealed class GithubApiRequestExecutor {
|
||||
return when (request) {
|
||||
is GithubApiRequest.Get -> HttpRequests.request(request.url)
|
||||
is GithubApiRequest.Post -> HttpRequests.post(request.url, request.bodyMimeType)
|
||||
is GithubApiRequest.Put -> HttpRequests.put(request.url, request.bodyMimeType)
|
||||
is GithubApiRequest.Patch -> HttpRequests.patch(request.url, request.bodyMimeType)
|
||||
is GithubApiRequest.Head -> HttpRequests.head(request.url)
|
||||
is GithubApiRequest.Delete -> HttpRequests.delete(request.url)
|
||||
|
||||
@@ -163,6 +163,26 @@ object GithubApiRequests {
|
||||
Post.json<GithubPullRequestDetailed>(getUrl(server, Repos.urlSuffix, "/$username/$repoName", urlSuffix),
|
||||
GithubPullRequestRequest(title, description, head, base))
|
||||
.withOperationName("create pull request in $username/$repoName")
|
||||
|
||||
@JvmStatic
|
||||
fun merge(pullRequest: GithubPullRequest, commitSubject: String, commitBody: String, headSha: String) =
|
||||
Put.json<Unit>(getMergeUrl(pullRequest),
|
||||
GithubPullRequestMergeRequest(commitSubject, commitBody, headSha, GithubPullRequestMergeMethod.merge))
|
||||
.withOperationName("merge pull request ${pullRequest.number}")
|
||||
|
||||
@JvmStatic
|
||||
fun squashMerge(pullRequest: GithubPullRequest, commitSubject: String, commitBody: String, headSha: String) =
|
||||
Put.json<Unit>(getMergeUrl(pullRequest),
|
||||
GithubPullRequestMergeRequest(commitSubject, commitBody, headSha, GithubPullRequestMergeMethod.squash))
|
||||
.withOperationName("squash and merge pull request ${pullRequest.number}")
|
||||
|
||||
@JvmStatic
|
||||
fun rebaseMerge(pullRequest: GithubPullRequest, headSha: String) =
|
||||
Put.json<Unit>(getMergeUrl(pullRequest),
|
||||
GithubPullRequestMergeRebaseRequest(headSha))
|
||||
.withOperationName("rebase and merge pull request ${pullRequest.number}")
|
||||
|
||||
private fun getMergeUrl(pullRequest: GithubPullRequest) = pullRequest.url + "/merge"
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -46,6 +46,11 @@ public class GithubPullRequest {
|
||||
@Mandatory private Links _links;
|
||||
private String authorAssociation;
|
||||
|
||||
@NotNull
|
||||
public String getUrl() {
|
||||
return url;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public String getHtmlUrl() {
|
||||
return htmlUrl;
|
||||
|
||||
+14
-1
@@ -1,12 +1,13 @@
|
||||
// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
package org.jetbrains.plugins.github.api.data;
|
||||
|
||||
import org.jetbrains.io.mandatory.Mandatory;
|
||||
import org.jetbrains.io.mandatory.RestModel;
|
||||
|
||||
@RestModel
|
||||
@SuppressWarnings("UnusedDeclaration")
|
||||
public class GithubPullRequestDetailed extends GithubPullRequest {
|
||||
private Boolean merged;
|
||||
@Mandatory private Boolean merged;
|
||||
private Boolean mergeable;
|
||||
private Boolean rebaseable;
|
||||
private String mergeableState;
|
||||
@@ -19,4 +20,16 @@ public class GithubPullRequestDetailed extends GithubPullRequest {
|
||||
private Integer additions;
|
||||
private Integer deletions;
|
||||
private Integer changedFiles;
|
||||
|
||||
public boolean getMerged() {
|
||||
return merged;
|
||||
}
|
||||
|
||||
public boolean getMergeable() {
|
||||
return mergeable != null && mergeable;
|
||||
}
|
||||
|
||||
public boolean getRebaseable() {
|
||||
return rebaseable != null && rebaseable;
|
||||
}
|
||||
}
|
||||
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
package org.jetbrains.plugins.github.api.data
|
||||
|
||||
@Suppress("EnumEntryName")
|
||||
enum class GithubPullRequestMergeMethod {
|
||||
merge, squash, rebase
|
||||
}
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
package org.jetbrains.plugins.github.api.requests;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.plugins.github.api.data.GithubPullRequestMergeMethod;
|
||||
|
||||
@SuppressWarnings({"FieldCanBeLocal", "UnusedDeclaration"})
|
||||
public class GithubPullRequestMergeRebaseRequest {
|
||||
@NotNull private final String sha;
|
||||
@NotNull private final GithubPullRequestMergeMethod method;
|
||||
|
||||
public GithubPullRequestMergeRebaseRequest(@NotNull String sha) {
|
||||
this.sha = sha;
|
||||
this.method = GithubPullRequestMergeMethod.rebase;
|
||||
}
|
||||
}
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
package org.jetbrains.plugins.github.api.requests;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.plugins.github.api.data.GithubPullRequestMergeMethod;
|
||||
|
||||
@SuppressWarnings({"FieldCanBeLocal", "UnusedDeclaration"})
|
||||
public class GithubPullRequestMergeRequest {
|
||||
@NotNull private final String commitTitle;
|
||||
@NotNull private final String commitMessage;
|
||||
@NotNull private final String sha;
|
||||
@NotNull private final GithubPullRequestMergeMethod method;
|
||||
|
||||
public GithubPullRequestMergeRequest(@NotNull String commitTitle,
|
||||
@NotNull String commitMessage,
|
||||
@NotNull String sha,
|
||||
@NotNull GithubPullRequestMergeMethod method) {
|
||||
if (method != GithubPullRequestMergeMethod.merge && method != GithubPullRequestMergeMethod.squash) {
|
||||
throw new IllegalArgumentException("Invalid merge method");
|
||||
}
|
||||
|
||||
this.commitTitle = commitTitle;
|
||||
this.commitMessage = commitMessage;
|
||||
this.sha = sha;
|
||||
this.method = method;
|
||||
}
|
||||
}
|
||||
+2
-1
@@ -48,7 +48,7 @@ class GithubPullRequestsComponentFactory(private val project: Project,
|
||||
return GithubPullRequestsComponent(requestExecutor, avatarIconsProviderFactory, repository, remote, repoDetails, account)
|
||||
}
|
||||
|
||||
private inner class GithubPullRequestsComponent(requestExecutor: GithubApiRequestExecutor,
|
||||
private inner class GithubPullRequestsComponent(private val requestExecutor: GithubApiRequestExecutor,
|
||||
avatarIconsProviderFactory: CachingGithubAvatarIconsProvider.Factory,
|
||||
private val repository: GitRepository, private val remote: GitRemote,
|
||||
private val repoDetails: GithubRepoDetailed,
|
||||
@@ -99,6 +99,7 @@ class GithubPullRequestsComponentFactory(private val project: Project,
|
||||
GithubPullRequestKeys.REMOTE.`is`(dataId) -> remote
|
||||
GithubPullRequestKeys.REPO_DETAILS.`is`(dataId) -> repoDetails
|
||||
GithubPullRequestKeys.SERVER_PATH.`is`(dataId) -> account.server
|
||||
GithubPullRequestKeys.API_REQUEST_EXECUTOR.`is`(dataId) -> requestExecutor
|
||||
GithubPullRequestKeys.PULL_REQUESTS_LIST_COMPONENT.`is`(dataId) -> list
|
||||
GithubPullRequestKeys.SELECTED_PULL_REQUEST_DATA_PROVIDER.`is`(dataId) ->
|
||||
list.selectionModel.current?.let(dataLoader::getDataProvider)
|
||||
|
||||
+4
@@ -4,6 +4,7 @@ package org.jetbrains.plugins.github.pullrequest.action
|
||||
import com.intellij.openapi.actionSystem.DataKey
|
||||
import git4idea.repo.GitRemote
|
||||
import git4idea.repo.GitRepository
|
||||
import org.jetbrains.plugins.github.api.GithubApiRequestExecutor
|
||||
import org.jetbrains.plugins.github.api.GithubServerPath
|
||||
import org.jetbrains.plugins.github.api.data.GithubRepoDetailed
|
||||
import org.jetbrains.plugins.github.api.data.GithubSearchedIssue
|
||||
@@ -12,6 +13,9 @@ import org.jetbrains.plugins.github.pullrequest.ui.GithubPullRequestsListCompone
|
||||
|
||||
object GithubPullRequestKeys {
|
||||
@JvmStatic
|
||||
val API_REQUEST_EXECUTOR =
|
||||
DataKey.create<GithubApiRequestExecutor>("org.jetbrains.plugins.github.pullrequest.requestexecutor")
|
||||
@JvmStatic
|
||||
val PULL_REQUESTS_LIST_COMPONENT =
|
||||
DataKey.create<GithubPullRequestsListComponent>("org.jetbrains.plugins.github.pullrequest.list.component")
|
||||
@JvmStatic
|
||||
|
||||
+46
@@ -0,0 +1,46 @@
|
||||
// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
package org.jetbrains.plugins.github.pullrequest.action
|
||||
|
||||
import com.intellij.openapi.actionSystem.AnActionEvent
|
||||
import com.intellij.openapi.progress.ProgressIndicator
|
||||
import com.intellij.openapi.progress.Task
|
||||
import com.intellij.openapi.project.Project
|
||||
import org.jetbrains.plugins.github.api.GithubApiRequestExecutor
|
||||
import org.jetbrains.plugins.github.api.GithubApiRequests
|
||||
import org.jetbrains.plugins.github.api.data.GithubPullRequestDetailed
|
||||
import org.jetbrains.plugins.github.pullrequest.action.merge.GithubPullRequestMergeActionBase
|
||||
import org.jetbrains.plugins.github.pullrequest.action.ui.GithubMergeCommitMessageDialog
|
||||
import org.jetbrains.plugins.github.util.GithubNotifications
|
||||
|
||||
class GithubPullRequestMergeCommitAction : GithubPullRequestMergeActionBase("Create a Merge Commit...") {
|
||||
override fun canMerge(details: GithubPullRequestDetailed) = details.mergeable
|
||||
|
||||
override fun mergePullRequest(e: AnActionEvent,
|
||||
project: Project, requestExecutor: GithubApiRequestExecutor, details: GithubPullRequestDetailed) {
|
||||
val dialog = GithubMergeCommitMessageDialog(project,
|
||||
"Merge Pull Request",
|
||||
"Merge pull request #${details.number} from ${details.head.label}",
|
||||
details.title)
|
||||
if (!dialog.showAndGet()) return
|
||||
|
||||
val commitMessage = dialog.message
|
||||
|
||||
object : Task.Backgroundable(project, "Merging Pull Request", true) {
|
||||
override fun run(indicator: ProgressIndicator) {
|
||||
requestExecutor.execute(indicator,
|
||||
GithubApiRequests.Repos.PullRequests.merge(details,
|
||||
commitMessage.first, commitMessage.second,
|
||||
details.head.sha))
|
||||
}
|
||||
|
||||
override fun onSuccess() {
|
||||
GithubNotifications.showInfo(project, "Pull Request Merged", "Successfully merged pull request #${details.number}")
|
||||
//TODO: refresh table and details
|
||||
}
|
||||
|
||||
override fun onThrowable(error: Throwable) {
|
||||
GithubNotifications.showError(project, "Failed To Merge Pull Request", error)
|
||||
}
|
||||
}.queue()
|
||||
}
|
||||
}
|
||||
+35
@@ -0,0 +1,35 @@
|
||||
// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
package org.jetbrains.plugins.github.pullrequest.action
|
||||
|
||||
import com.intellij.openapi.actionSystem.AnActionEvent
|
||||
import com.intellij.openapi.progress.ProgressIndicator
|
||||
import com.intellij.openapi.progress.Task
|
||||
import com.intellij.openapi.project.Project
|
||||
import org.jetbrains.plugins.github.api.GithubApiRequestExecutor
|
||||
import org.jetbrains.plugins.github.api.GithubApiRequests
|
||||
import org.jetbrains.plugins.github.api.data.GithubPullRequestDetailed
|
||||
import org.jetbrains.plugins.github.pullrequest.action.merge.GithubPullRequestMergeActionBase
|
||||
import org.jetbrains.plugins.github.util.GithubNotifications
|
||||
|
||||
class GithubPullRequestRebaseAction : GithubPullRequestMergeActionBase("Rebase and Merge") {
|
||||
override fun canMerge(details: GithubPullRequestDetailed) = details.rebaseable
|
||||
|
||||
override fun mergePullRequest(e: AnActionEvent,
|
||||
project: Project, requestExecutor: GithubApiRequestExecutor, details: GithubPullRequestDetailed) {
|
||||
object : Task.Backgroundable(project, "Rebasing and Merging Pull Request", true) {
|
||||
override fun run(indicator: ProgressIndicator) {
|
||||
requestExecutor.execute(indicator,
|
||||
GithubApiRequests.Repos.PullRequests.rebaseMerge(details, details.head.sha))
|
||||
}
|
||||
|
||||
override fun onSuccess() {
|
||||
GithubNotifications.showInfo(project, "Pull Request Rebased and Merged",
|
||||
"Successfully rebased and merged pull request #${details.number}")
|
||||
}
|
||||
|
||||
override fun onThrowable(error: Throwable) {
|
||||
GithubNotifications.showError(project, "Failed To Rebase and Merge Pull Request", error)
|
||||
}
|
||||
}.queue()
|
||||
}
|
||||
}
|
||||
+54
@@ -0,0 +1,54 @@
|
||||
// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
package org.jetbrains.plugins.github.pullrequest.action
|
||||
|
||||
import com.intellij.openapi.actionSystem.AnActionEvent
|
||||
import com.intellij.openapi.progress.ProgressIndicator
|
||||
import com.intellij.openapi.progress.Task
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.openapi.util.text.StringUtil
|
||||
import org.jetbrains.plugins.github.api.GithubApiRequestExecutor
|
||||
import org.jetbrains.plugins.github.api.GithubApiRequests
|
||||
import org.jetbrains.plugins.github.api.data.GithubPullRequestDetailed
|
||||
import org.jetbrains.plugins.github.pullrequest.action.merge.GithubPullRequestMergeActionBase
|
||||
import org.jetbrains.plugins.github.pullrequest.action.ui.GithubMergeCommitMessageDialog
|
||||
import org.jetbrains.plugins.github.util.GithubAsyncUtil
|
||||
import org.jetbrains.plugins.github.util.GithubNotifications
|
||||
|
||||
class GithubPullRequestSquashAction : GithubPullRequestMergeActionBase("Squash and Merge...") {
|
||||
override fun canMerge(details: GithubPullRequestDetailed) = details.mergeable
|
||||
|
||||
override fun mergePullRequest(e: AnActionEvent,
|
||||
project: Project, requestExecutor: GithubApiRequestExecutor, details: GithubPullRequestDetailed) {
|
||||
val commitsRequest = e.getRequiredData(GithubPullRequestKeys.SELECTED_PULL_REQUEST_DATA_PROVIDER).logCommitsRequest
|
||||
|
||||
GithubAsyncUtil.awaitFutureAndRunOnEdt(commitsRequest,
|
||||
project, "Loading Pull Request Commits", "Failed to Load Pull Request Commits") { commits ->
|
||||
val body = "* " + StringUtil.join(commits, { it.subject }, "\n\n* ")
|
||||
val dialog = GithubMergeCommitMessageDialog(project,
|
||||
"Merge Pull Request",
|
||||
"${details.title} (#${details.number})",
|
||||
body)
|
||||
if (!dialog.showAndGet()) return@awaitFutureAndRunOnEdt
|
||||
|
||||
val commitMessage = dialog.message
|
||||
|
||||
object : Task.Backgroundable(project, "Squashing and Merging Pull Request", true) {
|
||||
override fun run(indicator: ProgressIndicator) {
|
||||
requestExecutor.execute(indicator,
|
||||
GithubApiRequests.Repos.PullRequests.squashMerge(details,
|
||||
commitMessage.first, commitMessage.second,
|
||||
details.head.sha))
|
||||
}
|
||||
|
||||
override fun onSuccess() {
|
||||
GithubNotifications.showInfo(project, "Pull Request Squashed and Merged",
|
||||
"Successfully squashed amd merged pull request #${details.number}")
|
||||
}
|
||||
|
||||
override fun onThrowable(error: Throwable) {
|
||||
GithubNotifications.showError(project, "Failed To Squash and Merge Pull Request", error)
|
||||
}
|
||||
}.queue()
|
||||
}
|
||||
}
|
||||
}
|
||||
+56
@@ -0,0 +1,56 @@
|
||||
// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
package org.jetbrains.plugins.github.pullrequest.action.merge
|
||||
|
||||
import com.intellij.openapi.actionSystem.AnActionEvent
|
||||
import com.intellij.openapi.actionSystem.CommonDataKeys
|
||||
import com.intellij.openapi.project.DumbAwareAction
|
||||
import com.intellij.openapi.project.Project
|
||||
import org.jetbrains.plugins.github.api.GithubApiRequestExecutor
|
||||
import org.jetbrains.plugins.github.api.data.GithubIssueState
|
||||
import org.jetbrains.plugins.github.api.data.GithubPullRequestDetailed
|
||||
import org.jetbrains.plugins.github.pullrequest.action.GithubPullRequestKeys
|
||||
import org.jetbrains.plugins.github.util.GithubAsyncUtil
|
||||
import org.jetbrains.plugins.github.util.GithubNotifications
|
||||
|
||||
abstract class GithubPullRequestMergeActionBase(text: String) : DumbAwareAction(text) {
|
||||
override fun update(e: AnActionEvent) {
|
||||
//TODO: project-level switch
|
||||
val permissions = e.getRequiredData(GithubPullRequestKeys.REPO_DETAILS).permissions
|
||||
e.presentation.isEnabledAndVisible = (permissions.isPush || permissions.isAdmin) && isEnabled(e)
|
||||
}
|
||||
|
||||
private fun isEnabled(e: AnActionEvent): Boolean {
|
||||
val pullRequest = e.getData(GithubPullRequestKeys.SELECTED_PULL_REQUEST) ?: return false
|
||||
return pullRequest.state == GithubIssueState.open
|
||||
}
|
||||
|
||||
final override fun actionPerformed(e: AnActionEvent) {
|
||||
val project = e.getRequiredData(CommonDataKeys.PROJECT)
|
||||
val detailsRequest = e.getRequiredData(GithubPullRequestKeys.SELECTED_PULL_REQUEST_DATA_PROVIDER).detailsRequest
|
||||
|
||||
GithubAsyncUtil.awaitFutureAndRunOnEdt(detailsRequest,
|
||||
project, "Loading Pull Request Details", "Failed to Load Pull Request Details") { details ->
|
||||
if (!preCheck(project, details)) return@awaitFutureAndRunOnEdt
|
||||
mergePullRequest(e, project, e.getRequiredData(GithubPullRequestKeys.API_REQUEST_EXECUTOR), details)
|
||||
}
|
||||
}
|
||||
|
||||
private fun preCheck(project: Project, details: GithubPullRequestDetailed): Boolean {
|
||||
if (details.merged) {
|
||||
GithubNotifications.showError(project, "Failed to Merge Pull Request", "Pull request #${details.number} is already merged")
|
||||
return false
|
||||
}
|
||||
if (!canMerge(details)) {
|
||||
GithubNotifications.showError(project, "Failed to Merge Pull Request",
|
||||
"Cannot merge pull request #${details.number} due to conflicts")
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
protected abstract fun canMerge(details: GithubPullRequestDetailed): Boolean
|
||||
|
||||
protected abstract fun mergePullRequest(e: AnActionEvent,
|
||||
project: Project, requestExecutor: GithubApiRequestExecutor, details: GithubPullRequestDetailed)
|
||||
}
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
package org.jetbrains.plugins.github.pullrequest.action.merge
|
||||
|
||||
import com.intellij.openapi.actionSystem.AnActionEvent
|
||||
import com.intellij.openapi.actionSystem.DefaultActionGroup
|
||||
import com.intellij.openapi.project.DumbAware
|
||||
import org.jetbrains.plugins.github.pullrequest.action.GithubPullRequestKeys
|
||||
|
||||
class GithubPullRequestMergeActionGroup : DefaultActionGroup(), DumbAware {
|
||||
override fun update(e: AnActionEvent) {
|
||||
val permissions = e.getRequiredData(GithubPullRequestKeys.REPO_DETAILS).permissions
|
||||
e.presentation.isVisible = (permissions.isPush || permissions.isAdmin)
|
||||
}
|
||||
}
|
||||
+47
@@ -0,0 +1,47 @@
|
||||
// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
package org.jetbrains.plugins.github.pullrequest.action.ui
|
||||
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.openapi.ui.DialogWrapper
|
||||
import com.intellij.openapi.util.Disposer
|
||||
import com.intellij.openapi.vcs.ui.CommitMessage
|
||||
import com.intellij.util.ui.JBDimension
|
||||
import com.intellij.util.ui.JBUI
|
||||
import com.intellij.util.ui.UIUtil
|
||||
import javax.swing.JComponent
|
||||
import javax.swing.JLabel
|
||||
|
||||
class GithubMergeCommitMessageDialog(project: Project, title: String, subject: String, body: String) : DialogWrapper(project) {
|
||||
|
||||
private val commitMessage = CommitMessage(project, false, false, true).apply {
|
||||
setCommitMessage("$subject\n\n$body")
|
||||
preferredSize = JBDimension(500, 85)
|
||||
}
|
||||
|
||||
init {
|
||||
Disposer.register(disposable, commitMessage)
|
||||
|
||||
setTitle(title)
|
||||
setOKButtonText("Merge")
|
||||
init()
|
||||
}
|
||||
|
||||
override fun createCenterPanel(): JComponent? {
|
||||
return JBUI.Panels.simplePanel(0, UIUtil.DEFAULT_VGAP)
|
||||
.addToTop(JLabel("Merge commit message:"))
|
||||
.addToCenter(commitMessage)
|
||||
}
|
||||
|
||||
val message: Pair<String, String>
|
||||
get() {
|
||||
val text = commitMessage.comment
|
||||
|
||||
val idx = text.indexOf("\n\n")
|
||||
return if (idx < 0) "" to text
|
||||
else {
|
||||
val subject = text.substring(0, idx)
|
||||
if (subject.contains("\n")) "" to text
|
||||
else subject to text.substring(idx + 2)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,12 +2,53 @@
|
||||
package org.jetbrains.plugins.github.util
|
||||
|
||||
import com.intellij.openapi.application.runInEdt
|
||||
import com.intellij.openapi.progress.PerformInBackgroundOption
|
||||
import com.intellij.openapi.progress.ProcessCanceledException
|
||||
import com.intellij.openapi.progress.ProgressIndicator
|
||||
import com.intellij.openapi.progress.Task
|
||||
import com.intellij.openapi.project.Project
|
||||
import java.util.concurrent.*
|
||||
import java.util.function.BiFunction
|
||||
|
||||
object GithubAsyncUtil {
|
||||
|
||||
/**
|
||||
* Run [consumer] on EDT with the result of [future]
|
||||
* If future is cancelled, [consumer] will not be executed
|
||||
*
|
||||
* This is a naive implementation with timeout waiting
|
||||
*/
|
||||
@JvmStatic
|
||||
fun <R : Future<T>, T> awaitFutureAndRunOnEdt(future: R,
|
||||
project: Project, title: String, errorTitle: String,
|
||||
consumer: (T) -> Unit) {
|
||||
object : Task.Backgroundable(project, title, true, PerformInBackgroundOption.DEAF) {
|
||||
var result: T? = null
|
||||
|
||||
override fun run(indicator: ProgressIndicator) {
|
||||
while (true) {
|
||||
try {
|
||||
result = future.get(50, TimeUnit.MILLISECONDS)
|
||||
break
|
||||
}
|
||||
catch (e: TimeoutException) {
|
||||
indicator.checkCanceled()
|
||||
}
|
||||
}
|
||||
indicator.checkCanceled()
|
||||
}
|
||||
|
||||
override fun onSuccess() {
|
||||
result?.let(consumer)
|
||||
}
|
||||
|
||||
override fun onThrowable(error: Throwable) {
|
||||
if (isCancellation(error)) return
|
||||
GithubNotifications.showError(project, errorTitle, error)
|
||||
}
|
||||
}.queue()
|
||||
}
|
||||
|
||||
fun isCancellation(error: Throwable): Boolean {
|
||||
return error is ProcessCanceledException
|
||||
|| error is CancellationException
|
||||
|
||||
Reference in New Issue
Block a user