Commit Diff


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/<uid>/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<GotSettingsState> {
+
+    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"/>
         <vcsRootChecker implementation="dev.nezzontli.gotvcs.GotVcsRootChecker"/>
+        <applicationService serviceImplementation="dev.nezzontli.gotvcs.GotSettingsState"/>
+        <applicationConfigurable instance="dev.nezzontli.gotvcs.GotConfigurable" displayName="got" parentId="vcs"/>
     </extensions>
 </idea-plugin>