commit e508740b86451a34024db9d279843f7168c3df94 from: ale date: Thu Jul 23 21:34:21 2026 UTC Fase 6: panel de configuración (Settings > Version Control > got) GotSettingsState (PersistentStateComponent, gotvcs.xml) persiste dos rutas: binario got y SSH_AUTH_SOCK. GotConfigurable expone un panel con un file chooser para el binario y un campo de texto para el socket, registrado como applicationConfigurable bajo el grupo 'vcs'. GotCommandLineWrapper.binaryPath()/sshAuthSock() ahora consultan estos settings primero; si están vacíos (default), caen a la misma detección automática de antes (/run/current-system/sw/bin/got o PATH; System.getenv o el socket fijo de gpg-agent). Esto reemplaza los hardcodes de ruta que quedaron marcados como deuda desde la Fase 1, y da una forma real de resolver a mano el problema de SSH_AUTH_SOCK=null encontrado hoy si vuelve a pasar en otra sesión de IntelliJ. commit - 513258b76f2bcdaf8deeca03c0f1745660bd13b4 commit + e508740b86451a34024db9d279843f7168c3df94 blob - 51c15a60b3fe0cd0d8c4f69086f6ebdada80525d blob + 8c7b07b8439c7003f455c3e811f58ce6df93e266 --- src/main/kotlin/dev/nezzontli/gotvcs/GotCommandLineWrapper.kt +++ src/main/kotlin/dev/nezzontli/gotvcs/GotCommandLineWrapper.kt @@ -14,15 +14,15 @@ data class GotLogEntry(val commitId: String, val autho data class GotUpdateEntry(val code: Char, val path: String) /** - * Centraliza todas las invocaciones al binario `got`. La ruta de abajo es la - * del store de Nix resuelta al momento de escribir este código - * (readlink -f /etc/profiles/per-user/ale/bin/idea muestra el store path del - * IDE; el de got se resolvió igual desde /run/current-system/sw/bin/got). - * Cambia con cada rebuild del sistema -> deuda para GotConfigurable (Fase 6). + * Centraliza todas las invocaciones al binario `got`. Ruta al binario y + * SSH_AUTH_SOCK son configurables en Settings > Version Control > got + * (GotConfigurable/GotSettingsState); si no se configuran, caen a detección + * automática -- ver binaryPath()/sshAuthSock(). */ class GotCommandLineWrapper { private fun binaryPath(): String { + GotSettingsState.getInstance().gotBinaryPath.takeIf { it.isNotBlank() }?.let { return it } val nixPath = File("/run/current-system/sw/bin/got") return if (nixPath.canExecute()) nixPath.path else "got" } @@ -32,11 +32,12 @@ class GotCommandLineWrapper { * IntelliJ mismo (System.getenv, no solo el snapshot cacheado de * GeneralCommandLine) según cómo Hyprland lo haya lanzado esa sesión -- * no es algo que este plugin pueda forzar si el valor real no existe. - * Fallback a la ruta fija del socket ssh de gpg-agent en este sistema - * (uid 1000, único usuario). Igual que la ruta de `got`, debería ser - * configurable en la Fase 6 en vez de estar fija. + * Fallback: override manual en Settings, y si tampoco está seteado, la + * ruta fija del socket ssh de gpg-agent en este sistema (uid 1000, + * único usuario). */ private fun sshAuthSock(): String? { + GotSettingsState.getInstance().sshAuthSock.takeIf { it.isNotBlank() }?.let { return it } System.getenv("SSH_AUTH_SOCK")?.let { return it } val fallback = File("/run/user/1000/gnupg/S.gpg-agent.ssh") return if (fallback.exists()) fallback.path else null @@ -47,18 +48,14 @@ class GotCommandLineWrapper { .withWorkDirectory(workDir) .withCharset(StandardCharsets.UTF_8) .withParentEnvironmentType(GeneralCommandLine.ParentEnvironmentType.SYSTEM) - val sshAuthSock = sshAuthSock() - sshAuthSock?.let { commandLine.environment["SSH_AUTH_SOCK"] = it } + sshAuthSock()?.let { commandLine.environment["SSH_AUTH_SOCK"] = it } val output = try { ExecUtil.execAndGetOutput(commandLine) } catch (e: ExecutionException) { throw VcsException("No se pudo ejecutar got ${args.joinToString(" ")}: ${e.message}", e) } if (output.exitCode != 0) { - throw VcsException( - "got ${args.joinToString(" ")} salió con código ${output.exitCode}: " + - "${output.stderr.trim()} [SSH_AUTH_SOCK=$sshAuthSock]" - ) + throw VcsException("got ${args.joinToString(" ")} salió con código ${output.exitCode}: ${output.stderr.trim()}") } return output.stdout } blob - /dev/null blob + 01e778b4cc412bea174886428b1f9c07e63e7ae4 (mode 644) --- /dev/null +++ src/main/kotlin/dev/nezzontli/gotvcs/GotConfigurable.kt @@ -0,0 +1,69 @@ +package dev.nezzontli.gotvcs + +import com.intellij.openapi.options.Configurable +import com.intellij.openapi.ui.TextFieldWithBrowseButton +import com.intellij.openapi.fileChooser.FileChooserDescriptorFactory +import com.intellij.util.ui.FormBuilder +import javax.swing.JComponent +import javax.swing.JPanel +import javax.swing.JTextField + +class GotConfigurable : Configurable { + + private var panel: JPanel? = null + private var gotBinaryField: TextFieldWithBrowseButton? = null + private var sshAuthSockField: JTextField? = null + + override fun getDisplayName(): String = "got" + + override fun createComponent(): JComponent { + val binaryField = TextFieldWithBrowseButton().apply { + addBrowseFolderListener( + "Ruta al binario got", + "Vacío para detectarlo automáticamente (/run/current-system/sw/bin/got o PATH)", + null, + FileChooserDescriptorFactory.createSingleFileDescriptor(), + ) + } + val sockField = JTextField() + + gotBinaryField = binaryField + sshAuthSockField = sockField + + val built = FormBuilder.createFormBuilder() + .addLabeledComponent("Binario got:", binaryField) + .addTooltip("Vacío para detectarlo automáticamente (/run/current-system/sw/bin/got o PATH)") + .addLabeledComponent("SSH_AUTH_SOCK:", sockField) + .addTooltip("Vacío para usar el del proceso de IntelliJ, con fallback a /run/user//gnupg/S.gpg-agent.ssh") + .addComponentFillVertically(JPanel(), 0) + .panel + + panel = built + reset() + return built + } + + override fun isModified(): Boolean { + val settings = GotSettingsState.getInstance() + return gotBinaryField?.text.orEmpty() != settings.gotBinaryPath || + sshAuthSockField?.text.orEmpty() != settings.sshAuthSock + } + + override fun apply() { + val settings = GotSettingsState.getInstance() + settings.gotBinaryPath = gotBinaryField?.text.orEmpty() + settings.sshAuthSock = sshAuthSockField?.text.orEmpty() + } + + override fun reset() { + val settings = GotSettingsState.getInstance() + gotBinaryField?.text = settings.gotBinaryPath + sshAuthSockField?.text = settings.sshAuthSock + } + + override fun disposeUIResources() { + panel = null + gotBinaryField = null + sshAuthSockField = null + } +} blob - /dev/null blob + 59f56f981c62253964510e70eb413f50399c20c0 (mode 644) --- /dev/null +++ src/main/kotlin/dev/nezzontli/gotvcs/GotSettingsState.kt @@ -0,0 +1,32 @@ +package dev.nezzontli.gotvcs + +import com.intellij.openapi.application.ApplicationManager +import com.intellij.openapi.components.PersistentStateComponent +import com.intellij.openapi.components.State +import com.intellij.openapi.components.Storage +import com.intellij.util.xmlb.XmlSerializerUtil + +/** + * Rutas configurables desde Settings > Version Control > got. Vacío ("") + * significa "usar la detección automática" (ver GotCommandLineWrapper): + * binario got en /run/current-system/sw/bin/got o PATH, y SSH_AUTH_SOCK de + * System.getenv() o el socket fijo de gpg-agent. + */ +@State(name = "GotVcsSettings", storages = [Storage("gotvcs.xml")]) +class GotSettingsState : PersistentStateComponent { + + var gotBinaryPath: String = "" + var sshAuthSock: String = "" + + override fun getState(): GotSettingsState = this + + override fun loadState(state: GotSettingsState) { + XmlSerializerUtil.copyBean(state, this) + } + + companion object { + @JvmStatic + fun getInstance(): GotSettingsState = + ApplicationManager.getApplication().getService(GotSettingsState::class.java) + } +} blob - 7691b318ffa7f5dfcd4c30985ff5418261cd2591 blob + 43cc025a25cf34f1f1fa2383094d5b56a5a9b582 --- src/main/resources/META-INF/plugin.xml +++ src/main/resources/META-INF/plugin.xml @@ -11,5 +11,7 @@ displayName="got" administrativeAreaName=".got"/> + +