diff --git a/plugins/github/resources/META-INF/plugin.xml b/plugins/github/resources/META-INF/plugin.xml
index a288610ff676..f19b5d26e38a 100644
--- a/plugins/github/resources/META-INF/plugin.xml
+++ b/plugins/github/resources/META-INF/plugin.xml
@@ -113,6 +113,8 @@
+
+
diff --git a/plugins/github/resources/graphql/query/getProtectionRules.graphql b/plugins/github/resources/graphql/query/getProtectionRules.graphql
new file mode 100644
index 000000000000..f945710522fb
--- /dev/null
+++ b/plugins/github/resources/graphql/query/getProtectionRules.graphql
@@ -0,0 +1,13 @@
+query($repoOwner: String!, $repoName: String!, $pageSize: Int = 100, $cursor: String) {
+ repository(owner: $repoOwner, name: $repoName) {
+ branchProtectionRules(first: $pageSize, after: $cursor) {
+ pageInfo {
+ ...pageInfo
+ }
+ nodes {
+ ...nodeInfo
+ pattern
+ }
+ }
+ }
+}
diff --git a/plugins/github/resources/messages/GithubBundle.properties b/plugins/github/resources/messages/GithubBundle.properties
index 3b1c041aec26..7213b0883735 100644
--- a/plugins/github/resources/messages/GithubBundle.properties
+++ b/plugins/github/resources/messages/GithubBundle.properties
@@ -391,3 +391,5 @@ pull.request.review.not.supported.non.linear=By-commit review is not supported f
issue.state.open=Open
issue.state.closed=Closed
+#protected branches
+progress.text.loading.protected.branches=Loading GitHub branch protection rules
diff --git a/plugins/github/src/org/jetbrains/plugins/github/api/GHGQLQueries.kt b/plugins/github/src/org/jetbrains/plugins/github/api/GHGQLQueries.kt
index 5e91bf174bbc..b6a2726efff2 100644
--- a/plugins/github/src/org/jetbrains/plugins/github/api/GHGQLQueries.kt
+++ b/plugins/github/src/org/jetbrains/plugins/github/api/GHGQLQueries.kt
@@ -4,6 +4,7 @@ package org.jetbrains.plugins.github.api
object GHGQLQueries {
const val findOrganizationTeams = "findOrganizationTeams"
const val findRepositoryPermission = "findRepositoryPermission"
+ const val getProtectionRules = "getProtectionRules"
const val commentBody = "commentBody"
const val updateIssueComment = "updateIssueComment"
const val deleteIssueComment = "deleteIssueComment"
@@ -25,4 +26,4 @@ object GHGQLQueries {
const val updateReviewComment = "updateReviewComment"
const val resolveReviewThread = "resolveReviewThread"
const val unresolveReviewThread = "unresolveReviewThread"
-}
\ No newline at end of file
+}
diff --git a/plugins/github/src/org/jetbrains/plugins/github/api/GHGQLRequests.kt b/plugins/github/src/org/jetbrains/plugins/github/api/GHGQLRequests.kt
index ddb23f9392b4..4892884f7505 100644
--- a/plugins/github/src/org/jetbrains/plugins/github/api/GHGQLRequests.kt
+++ b/plugins/github/src/org/jetbrains/plugins/github/api/GHGQLRequests.kt
@@ -50,6 +50,20 @@ object GHGQLRequests {
GHRepositoryPermission::class.java,
"repository")
}
+
+ fun getProtectionRules(repository: GHRepositoryCoordinates,
+ pagination: GHGQLRequestPagination? = null): GQLQuery> {
+ return GQLQuery.TraversedParsed(repository.serverPath.toGraphQLUrl(), GHGQLQueries.getProtectionRules,
+ mapOf("repoOwner" to repository.repositoryPath.owner,
+ "repoName" to repository.repositoryPath.repository,
+ "pageSize" to pagination?.pageSize,
+ "cursor" to pagination?.afterCursor),
+ ProtectedRulesConnection::class.java,
+ "repository", "branchProtectionRules")
+ }
+
+ private class ProtectedRulesConnection(pageInfo: GHGQLPageInfo, nodes: List)
+ : GHConnection(pageInfo, nodes)
}
object Comment {
@@ -274,4 +288,4 @@ object GHGQLRequests {
"unresolveReviewThread", "thread")
}
}
-}
\ No newline at end of file
+}
diff --git a/plugins/github/src/org/jetbrains/plugins/github/api/data/GHBranchProtectionRule.kt b/plugins/github/src/org/jetbrains/plugins/github/api/data/GHBranchProtectionRule.kt
new file mode 100644
index 000000000000..cc1b1023298d
--- /dev/null
+++ b/plugins/github/src/org/jetbrains/plugins/github/api/data/GHBranchProtectionRule.kt
@@ -0,0 +1,5 @@
+// Copyright 2000-2020 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
+
+class GHBranchProtectionRule(id: String,
+ val pattern: String): GHNode(id)
diff --git a/plugins/github/src/org/jetbrains/plugins/github/extensions/GHProtectedBranchRulesLoader.kt b/plugins/github/src/org/jetbrains/plugins/github/extensions/GHProtectedBranchRulesLoader.kt
new file mode 100644
index 000000000000..1a8eb33bd0c0
--- /dev/null
+++ b/plugins/github/src/org/jetbrains/plugins/github/extensions/GHProtectedBranchRulesLoader.kt
@@ -0,0 +1,83 @@
+// Copyright 2000-2020 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.extensions
+
+import com.intellij.concurrency.SensitiveProgressWrapper
+import com.intellij.openapi.application.runInEdt
+import com.intellij.openapi.components.service
+import com.intellij.openapi.diagnostic.logger
+import com.intellij.openapi.progress.ProcessCanceledException
+import com.intellij.openapi.progress.ProgressIndicator
+import com.intellij.openapi.project.Project
+import com.intellij.util.PatternUtil
+import git4idea.config.GitSharedSettings
+import git4idea.fetch.GitFetchHandler
+import git4idea.repo.GitRemote
+import git4idea.repo.GitRepository
+import org.jetbrains.plugins.github.api.GHGQLRequests
+import org.jetbrains.plugins.github.api.GithubApiRequestExecutorManager
+import org.jetbrains.plugins.github.api.util.SimpleGHGQLPagesLoader
+import org.jetbrains.plugins.github.authentication.GithubAuthenticationManager
+import org.jetbrains.plugins.github.i18n.GithubBundle
+import org.jetbrains.plugins.github.util.GHProjectRepositoriesManager
+import org.jetbrains.plugins.github.util.GithubSharedProjectSettings
+
+private val LOG = logger()
+
+internal class GHProtectedBranchRulesLoader : GitFetchHandler {
+
+ override fun doAfterSuccessfulFetch(project: Project, fetches: Map>, indicator: ProgressIndicator) {
+ try {
+ loadProtectionRules(indicator, fetches, project)
+ }
+ catch (e: Exception) {
+ if (e is ProcessCanceledException) {
+ throw e
+ }
+ LOG.info("Error occurred while trying to load branch protection rules", e)
+ }
+ }
+
+ private fun loadProtectionRules(indicator: ProgressIndicator,
+ fetches: Map>,
+ project: Project) {
+ val githubAuthenticationManager = GithubAuthenticationManager.getInstance()
+ if (!GitSharedSettings.getInstance(project).isSynchronizeBranchProtectionRules || !githubAuthenticationManager.hasAccounts()) {
+ runInEdt {
+ GithubSharedProjectSettings.getInstance(project).branchProtectionPatterns = arrayListOf()
+ }
+ return
+ }
+
+ indicator.text = GithubBundle.message("progress.text.loading.protected.branches")
+
+ val branchProtectionPatterns = mutableSetOf()
+ for ((repository, remotes) in fetches) {
+ indicator.checkCanceled()
+
+ for (remote in remotes) {
+ indicator.checkCanceled()
+
+ val account =
+ githubAuthenticationManager.getAccounts().find { it.server.matches(remote.firstUrl.orEmpty()) } ?: continue
+
+ val requestExecutor = GithubApiRequestExecutorManager.getInstance().getExecutor(account)
+
+ val githubRepositoryMapping =
+ project.service().findKnownRepositories(repository).find { it.gitRemote.remote == remote }
+ ?: continue
+
+ val repositoryCoordinates = githubRepositoryMapping.repository
+
+ SimpleGHGQLPagesLoader(requestExecutor, { GHGQLRequests.Repo.getProtectionRules(repositoryCoordinates) })
+ .loadAll(SensitiveProgressWrapper((indicator)))
+ .forEach { rule -> branchProtectionPatterns.add(PatternUtil.convertToRegex(rule.pattern)) }
+ }
+
+ }
+
+ runInEdt {
+ GithubSharedProjectSettings.getInstance(project).branchProtectionPatterns = branchProtectionPatterns.toMutableList()
+ }
+ }
+
+}
diff --git a/plugins/github/src/org/jetbrains/plugins/github/util/GithubSharedProjectSettings.kt b/plugins/github/src/org/jetbrains/plugins/github/util/GithubSharedProjectSettings.kt
index a7f06a6934d0..a47970e8f4cb 100644
--- a/plugins/github/src/org/jetbrains/plugins/github/util/GithubSharedProjectSettings.kt
+++ b/plugins/github/src/org/jetbrains/plugins/github/util/GithubSharedProjectSettings.kt
@@ -3,6 +3,8 @@ package org.jetbrains.plugins.github.util
import com.intellij.openapi.components.*
import com.intellij.openapi.project.Project
+import git4idea.config.GitProtectedBranchProvider
+import java.util.*
@State(name = "GithubSharedProjectSettings", storages = [Storage("vcs.xml")])
class GithubSharedProjectSettings : PersistentStateComponentWithModificationTracker {
@@ -10,6 +12,7 @@ class GithubSharedProjectSettings : PersistentStateComponentWithModificationTrac
class SettingsState : BaseState() {
var pullRequestMergeForbidden by property(false)
+ var branchProtectionPatterns by list()
}
var pullRequestMergeForbidden: Boolean
@@ -18,9 +21,15 @@ class GithubSharedProjectSettings : PersistentStateComponentWithModificationTrac
state.pullRequestMergeForbidden = value
}
+ var branchProtectionPatterns: MutableList
+ get() = Collections.unmodifiableList(state.branchProtectionPatterns)
+ set(value) {
+ state.branchProtectionPatterns = value
+ }
+
override fun getStateModificationCount() = state.modificationCount
override fun getState() = state
- override fun loadState(state: GithubSharedProjectSettings.SettingsState) {
+ override fun loadState(state: SettingsState) {
this.state = state
}
@@ -29,3 +38,10 @@ class GithubSharedProjectSettings : PersistentStateComponentWithModificationTrac
fun getInstance(project: Project) = project.service()
}
}
+
+internal class GithubProtectedBranchProvider : GitProtectedBranchProvider {
+
+ override fun doGetProtectedBranchPatterns(project: Project): List {
+ return GithubSharedProjectSettings.getInstance(project).branchProtectionPatterns
+ }
+}