mirror of
https://gitflic.ru/project/openide/openide.git
synced 2025-12-16 14:23:28 +07:00
[gitlab] try and scan remote servers for a gitlab instance
IDEA-320465 Fixed GitOrigin-RevId: 843b3b005475ff3ef1c25c09e6d2f3557447f6bc
This commit is contained in:
committed by
intellij-monorepo-bot
parent
8cd1016e97
commit
1ca8b7cc81
@@ -1,9 +1,13 @@
|
||||
// Copyright 2000-2022 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package git4idea.remote.hosting
|
||||
|
||||
import com.intellij.collaboration.api.ServerPath
|
||||
import com.intellij.openapi.diagnostic.Logger
|
||||
import com.intellij.openapi.util.NlsSafe
|
||||
import com.intellij.util.UriUtil
|
||||
import com.intellij.util.io.URLUtil
|
||||
import git4idea.remote.GitRemoteUrlCoordinates
|
||||
import org.jetbrains.annotations.ApiStatus
|
||||
import java.net.URI
|
||||
import java.net.URISyntaxException
|
||||
|
||||
@@ -45,7 +49,7 @@ object GitHostingUrlUtil {
|
||||
fun match(serverUri: URI, gitRemoteUrl: String): Boolean {
|
||||
val remoteUri = getUriFromRemoteUrl(gitRemoteUrl) ?: return false
|
||||
|
||||
if(!serverUri.host.equals(remoteUri.host, true)) return false
|
||||
if (!serverUri.host.equals(remoteUri.host, true)) return false
|
||||
|
||||
if (serverUri.path != null && serverUri.path != "/") {
|
||||
val remoteUriPath = remoteUri.path ?: return false
|
||||
@@ -53,4 +57,34 @@ object GitHostingUrlUtil {
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
@ApiStatus.Internal
|
||||
suspend fun <S : ServerPath> findServerAt(log: Logger, coordinates: GitRemoteUrlCoordinates, serverCheck: suspend (URI) -> S?): S? {
|
||||
val uri = getUriFromRemoteUrl(coordinates.url)
|
||||
log.debug("Extracted URI $uri from remote ${coordinates.url}")
|
||||
if (uri == null) return null
|
||||
|
||||
val host = uri.host ?: return null
|
||||
val path = uri.path ?: return null
|
||||
val pathParts = path.removePrefix("/").split('/').takeIf { it.size >= 2 } ?: return null
|
||||
val serverSuffix = if (pathParts.size == 2) null else pathParts.subList(0, pathParts.size - 2).joinToString("/")
|
||||
|
||||
for (serverUri in listOf(
|
||||
URI(URLUtil.HTTPS_PROTOCOL, host, serverSuffix, null),
|
||||
URI(URLUtil.HTTP_PROTOCOL, host, serverSuffix, null),
|
||||
URI(URLUtil.HTTP_PROTOCOL, null, host, 8080, serverSuffix, null, null)
|
||||
)) {
|
||||
log.debug("Looking for server at $serverUri")
|
||||
try {
|
||||
val server = serverCheck(serverUri)
|
||||
if (server != null) {
|
||||
log.debug("Found server at $serverUri")
|
||||
return server
|
||||
}
|
||||
}
|
||||
catch (ignored: Throwable) {
|
||||
}
|
||||
}
|
||||
return null
|
||||
}
|
||||
}
|
||||
@@ -4,8 +4,10 @@ package git4idea.remote.hosting
|
||||
import com.intellij.collaboration.api.ServerPath
|
||||
import com.intellij.dvcs.repo.VcsRepositoryManager
|
||||
import com.intellij.dvcs.repo.VcsRepositoryMappingListener
|
||||
import com.intellij.openapi.diagnostic.Logger
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.openapi.util.Disposer
|
||||
import com.intellij.util.io.URLUtil
|
||||
import git4idea.remote.GitRemoteUrlCoordinates
|
||||
import git4idea.repo.GitRepository
|
||||
import git4idea.repo.GitRepositoryChangeListener
|
||||
@@ -14,6 +16,8 @@ import kotlinx.coroutines.async
|
||||
import kotlinx.coroutines.awaitAll
|
||||
import kotlinx.coroutines.channels.awaitClose
|
||||
import kotlinx.coroutines.flow.*
|
||||
import org.jetbrains.annotations.ApiStatus
|
||||
import java.net.URI
|
||||
|
||||
fun gitRemotesFlow(project: Project): Flow<Set<GitRemoteUrlCoordinates>> =
|
||||
callbackFlow {
|
||||
@@ -69,7 +73,12 @@ fun <S : ServerPath> GitRemotesFlow.discoverServers(knownServersFlow: Flow<Set<S
|
||||
remotes.chunked(parallelism).forEach { remotesChunk ->
|
||||
remotesChunk.map { remote ->
|
||||
async {
|
||||
val server = checkForDedicatedServer(remote)
|
||||
val server = try {
|
||||
checkForDedicatedServer(remote)
|
||||
}
|
||||
catch (e: Exception) {
|
||||
null
|
||||
}
|
||||
if (server != null) send(server)
|
||||
}
|
||||
}.awaitAll()
|
||||
|
||||
@@ -26,8 +26,12 @@ class GHHostedRepositoriesManager(project: Project, cs: CoroutineScope) : Hosted
|
||||
mutableSetOf(GithubServerPath.DEFAULT_SERVER) + accounts.map { it.server }
|
||||
}.distinctUntilChanged()
|
||||
|
||||
val discoveredServersFlow = gitRemotesFlow.discoverServers(accountsServersFlow) {
|
||||
checkForDedicatedServer(it)
|
||||
val discoveredServersFlow = gitRemotesFlow.discoverServers(accountsServersFlow) { remote ->
|
||||
GitHostingUrlUtil.findServerAt(LOG, remote) {
|
||||
val server = GithubServerPath.from(it.toString())
|
||||
val metadata = service<GHEnterpriseServerMetadataLoader>().loadMetadata(server).await()
|
||||
if (metadata != null) server else null
|
||||
}
|
||||
}.runningFold(emptySet<GithubServerPath>()) { accumulator, value ->
|
||||
accumulator + value
|
||||
}.distinctUntilChanged()
|
||||
@@ -46,33 +50,6 @@ class GHHostedRepositoriesManager(project: Project, cs: CoroutineScope) : Hosted
|
||||
override val knownRepositoriesState: StateFlow<Set<GHGitRepositoryMapping>> =
|
||||
knownRepositoriesFlow.stateIn(cs, getStateSharingStartConfig(), emptySet())
|
||||
|
||||
private suspend fun checkForDedicatedServer(remote: GitRemoteUrlCoordinates): GithubServerPath? {
|
||||
val uri = GitHostingUrlUtil.getUriFromRemoteUrl(remote.url)
|
||||
LOG.debug("Extracted URI $uri from remote ${remote.url}")
|
||||
if (uri == null) return null
|
||||
|
||||
val host = uri.host ?: return null
|
||||
val path = uri.path ?: return null
|
||||
val pathParts = path.removePrefix("/").split('/').takeIf { it.size >= 2 } ?: return null
|
||||
val serverSuffix = if (pathParts.size == 2) null else pathParts.subList(0, pathParts.size - 2).joinToString("/", "/")
|
||||
|
||||
for (server in listOf(
|
||||
GithubServerPath(false, host, null, serverSuffix),
|
||||
GithubServerPath(true, host, null, serverSuffix),
|
||||
GithubServerPath(true, host, 8080, serverSuffix)
|
||||
)) {
|
||||
LOG.debug("Looking for GHE server at $server")
|
||||
try {
|
||||
service<GHEnterpriseServerMetadataLoader>().loadMetadata(server).await()
|
||||
LOG.debug("Found GHE server at $server")
|
||||
return server
|
||||
}
|
||||
catch (ignored: Throwable) {
|
||||
}
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val LOG = logger<GHHostedRepositoriesManager>()
|
||||
|
||||
|
||||
@@ -22,6 +22,8 @@
|
||||
<applicationService serviceImplementation="org.jetbrains.plugins.gitlab.authentication.accounts.GitLabPersistentAccounts"/>
|
||||
<applicationService serviceInterface="org.jetbrains.plugins.gitlab.authentication.accounts.GitLabAccountManager"
|
||||
serviceImplementation="org.jetbrains.plugins.gitlab.authentication.accounts.PersistentGitLabAccountManager"/>
|
||||
<applicationService serviceInterface="org.jetbrains.plugins.gitlab.GitLabServersManager"
|
||||
serviceImplementation="org.jetbrains.plugins.gitlab.CachingGitLabServersManager"/>
|
||||
|
||||
<projectService serviceInterface="org.jetbrains.plugins.gitlab.GitLabProjectsManager"
|
||||
serviceImplementation="org.jetbrains.plugins.gitlab.GitLabProjectsManagerImpl"/>
|
||||
|
||||
@@ -4,9 +4,7 @@ package org.jetbrains.plugins.gitlab
|
||||
import com.intellij.openapi.components.service
|
||||
import com.intellij.openapi.diagnostic.logger
|
||||
import com.intellij.openapi.project.Project
|
||||
import git4idea.remote.hosting.HostedGitRepositoriesManager
|
||||
import git4idea.remote.hosting.gitRemotesFlow
|
||||
import git4idea.remote.hosting.mapToServers
|
||||
import git4idea.remote.hosting.*
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.flow.*
|
||||
import org.jetbrains.plugins.gitlab.api.GitLabServerPath
|
||||
@@ -24,7 +22,21 @@ internal class GitLabProjectsManagerImpl(project: Project, cs: CoroutineScope) :
|
||||
mutableSetOf(GitLabServerPath.DEFAULT_SERVER) + accounts.map { it.server }
|
||||
}.distinctUntilChanged()
|
||||
|
||||
val knownRepositoriesFlow = gitRemotesFlow.mapToServers(accountsServersFlow) { server, remote ->
|
||||
val discoveredServersFlow = gitRemotesFlow.discoverServers(accountsServersFlow) { remote ->
|
||||
GitHostingUrlUtil.findServerAt(LOG, remote) {
|
||||
val server = GitLabServerPath(it.toString())
|
||||
val isGitLabServer = service<GitLabServersManager>().checkIsGitLabServer(server)
|
||||
if (isGitLabServer) server else null
|
||||
}
|
||||
}.runningFold(emptySet<GitLabServerPath>()) { accumulator, value ->
|
||||
accumulator + value
|
||||
}.distinctUntilChanged()
|
||||
|
||||
val serversFlow = accountsServersFlow.combine(discoveredServersFlow) { servers1, servers2 ->
|
||||
servers1 + servers2
|
||||
}
|
||||
|
||||
val knownRepositoriesFlow = gitRemotesFlow.mapToServers(serversFlow) { server, remote ->
|
||||
GitLabProjectMapping.create(server, remote)
|
||||
}.onEach {
|
||||
LOG.debug("New list of known repos: $it")
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
// Copyright 2000-2022 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package org.jetbrains.plugins.gitlab
|
||||
|
||||
import kotlinx.coroutines.*
|
||||
import org.jetbrains.plugins.gitlab.api.GitLabApiImpl
|
||||
import org.jetbrains.plugins.gitlab.api.GitLabServerPath
|
||||
import org.jetbrains.plugins.gitlab.api.request.checkIsGitLabServer
|
||||
import java.util.concurrent.ConcurrentHashMap
|
||||
|
||||
interface GitLabServersManager {
|
||||
suspend fun checkIsGitLabServer(server: GitLabServerPath): Boolean
|
||||
}
|
||||
|
||||
internal class CachingGitLabServersManager(private val cs: CoroutineScope) : GitLabServersManager {
|
||||
|
||||
private val cache = ConcurrentHashMap<GitLabServerPath, Deferred<Boolean>>()
|
||||
|
||||
override suspend fun checkIsGitLabServer(server: GitLabServerPath): Boolean =
|
||||
cache.getOrPut(server) {
|
||||
cs.async(Dispatchers.IO + CoroutineName("GitLab Server metadata loader")) {
|
||||
GitLabApiImpl().checkIsGitLabServer(server)
|
||||
}
|
||||
}.await()
|
||||
}
|
||||
@@ -31,6 +31,8 @@ class GitLabApiImpl private constructor(httpHelper: HttpApiHelper)
|
||||
|
||||
constructor(tokenSupplier: () -> String) : this(httpHelper(tokenSupplier))
|
||||
|
||||
constructor() : this(httpHelper())
|
||||
|
||||
companion object {
|
||||
private fun httpHelper(tokenSupplier: () -> String): HttpApiHelper {
|
||||
val authConfigurer = object : AuthorizationConfigurer() {
|
||||
@@ -43,6 +45,13 @@ class GitLabApiImpl private constructor(httpHelper: HttpApiHelper)
|
||||
return HttpApiHelper(logger = logger<GitLabApi>(),
|
||||
requestConfigurer = requestConfigurer)
|
||||
}
|
||||
|
||||
private fun httpHelper(): HttpApiHelper {
|
||||
val requestConfigurer = CompoundRequestConfigurer(RequestTimeoutConfigurer(),
|
||||
CommonHeadersConfigurer())
|
||||
return HttpApiHelper(logger = logger<GitLabApi>(),
|
||||
requestConfigurer = requestConfigurer)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
// Copyright 2000-2022 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package org.jetbrains.plugins.gitlab.api.request
|
||||
|
||||
import com.intellij.collaboration.util.resolveRelative
|
||||
import org.jetbrains.plugins.gitlab.api.GitLabApi
|
||||
import org.jetbrains.plugins.gitlab.api.GitLabServerPath
|
||||
import java.net.http.HttpResponse
|
||||
|
||||
suspend fun GitLabApi.checkIsGitLabServer(server: GitLabServerPath): Boolean {
|
||||
val uri = server.restApiUri.resolveRelative("metadata")
|
||||
val request = request(uri).GET().build()
|
||||
val response = sendAndAwaitCancellable(request, HttpResponse.BodyHandlers.discarding())
|
||||
return response.headers().map().keys.any {
|
||||
it.contains("gitlab", true)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user