fix(jetbrains): harden cloud history git remote lookup

This commit is contained in:
kirillk
2026-05-13 16:01:07 -04:00
parent 4fc89dce67
commit 19988ccaae
3 changed files with 55 additions and 10 deletions
@@ -1,5 +1,9 @@
package ai.kilocode.client.session.history
import java.io.File
import java.util.concurrent.CompletableFuture
import java.util.concurrent.TimeUnit
/**
* Resolves the origin remote URL for a given directory by running
* `git remote get-url origin` as a subprocess.
@@ -11,9 +15,20 @@ package ai.kilocode.client.session.history
*/
internal fun resolveGitRemoteUrl(dir: String): String? = runCatching {
val proc = ProcessBuilder("git", "remote", "get-url", "origin")
.directory(java.io.File(dir))
.directory(File(dir))
.redirectErrorStream(true)
.start()
val out = proc.inputStream.bufferedReader().readText().trim()
val code = proc.waitFor()
val text = CompletableFuture.supplyAsync {
proc.inputStream.bufferedReader().use { it.readText() }
}
val done = proc.waitFor(5, TimeUnit.SECONDS)
if (!done) {
proc.destroyForcibly()
return@runCatching null
}
val out = text.get(1, TimeUnit.SECONDS).trim()
val code = proc.exitValue()
if (code == 0 && out.isNotEmpty()) out else null
}.getOrNull()
@@ -8,7 +8,11 @@ import ai.kilocode.rpc.dto.CloudSessionDto
import ai.kilocode.rpc.dto.SessionDto
import com.intellij.openapi.application.ApplicationManager
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock
import kotlinx.coroutines.withContext
class HistoryController(
private val sessions: KiloSessionService,
@@ -30,6 +34,10 @@ class HistoryController(
var gitUrl: String? = null
private set
@Volatile
private var resolved = false
private val lock = Mutex()
/** Whether to filter cloud history by the current repository. */
var repoOnly: Boolean = false
private set
@@ -151,14 +159,18 @@ class HistoryController(
* propagates state updates to EDT via [edt].
*/
private suspend fun resolveUrlIfNeeded(): String? {
if (gitUrl != null) return gitUrl
val url = kotlinx.coroutines.withContext(kotlinx.coroutines.Dispatchers.IO) {
gitUrlProvider()
if (resolved) return gitUrl
return lock.withLock {
if (resolved) return@withLock gitUrl
val url = withContext(Dispatchers.IO) {
gitUrlProvider()
}
// Write gitUrl directly (volatile) so it is visible before EDT callbacks fire.
gitUrl = url
resolved = true
if (url != null) updateRepoOnly(true)
url
}
// Write gitUrl directly (volatile) so it is visible before EDT callbacks fire.
gitUrl = url
if (url != null) updateRepoOnly(true)
return url
}
}
@@ -27,6 +27,7 @@ import java.awt.Cursor
import java.awt.event.KeyEvent
import java.time.Instant
import java.time.temporal.ChronoUnit
import java.util.concurrent.atomic.AtomicInteger
import javax.swing.JComponent
import javax.swing.KeyStroke
import javax.swing.event.ListDataEvent
@@ -573,6 +574,23 @@ class HistoryControllerTest : BasePlatformTestCase() {
assertNull(controller.gitUrl)
}
fun `test cloud git url resolves once across overlapping reloads`() {
rpc.cloud += cloud("cloud_1", "Cloud One")
val calls = AtomicInteger()
val controller = HistoryController(sessions, workspace, scope, gitUrlProvider = {
calls.incrementAndGet()
Thread.sleep(100)
"git@github.com:test/repo.git"
})
controller.reloadCloud()
controller.reloadCloud()
flush()
assertEquals(1, calls.get())
assertEquals(2, rpc.cloudCalls.size)
}
fun `test cloud load passes null when repo only disabled`() {
rpc.cloud += cloud("cloud_1", "Cloud One")
val url = "git@github.com:test/repo.git"