Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,11 @@

### Changed

- **All commands now use project Ruby SDK** — `bundle exec docscribe` (via `DocscribeRunner`
and `DocscribeDaemon.fallback()`) and `bundle exec docscribe --version` (via
`DocscribeDaemon.performGemCheck()`) now prepend the Ruby SDK's `bin/` directory to `PATH`
and set `BUNDLE_GEMFILE`. This ensures the SDK's Ruby and Bundler are used instead of system
defaults. Extracted shared `buildSdkEnvironment()` helper, also reused in server startup.
- **`com.intellij.modules.ruby` made optional** — changed from hard `<depends>` to
`<depends optional="true" config-file="withRubyPlugin.xml">`. Ruby-specific extensions
(`ExternalAnnotator`, `FoldingBuilder`, `DependencySupport`, `IntentionAction`) moved to a new
Expand Down
33 changes: 23 additions & 10 deletions src/main/kotlin/com/florexlabs/docscribe/runner/DocscribeDaemon.kt
Original file line number Diff line number Diff line change
Expand Up @@ -307,15 +307,7 @@ class DocscribeDaemon(

val pb = ProcessBuilder(ruby, "-e", script).directory(File(gemRoot))
val env = pb.environment()
val sdk = ProjectRootManager.getInstance(project).projectSdk
if (sdk?.homePath != null) {
val sdkBin = File(ruby).parentFile?.absolutePath
if (sdkBin != null) {
val currentPath = env["PATH"] ?: ""
env["PATH"] = "$sdkBin${File.pathSeparator}$currentPath"
}
env["BUNDLE_GEMFILE"] = File(gemRoot, "Gemfile").absolutePath
}
env.putAll(buildSdkEnvironment())

val localGemPath = System.getProperty("docscribe.local.gem.path")
if (localGemPath != null) {
Expand Down Expand Up @@ -395,6 +387,7 @@ class DocscribeDaemon(
val pb =
ProcessBuilder("bundle", "exec", "docscribe", "--version")
.directory(File(gemRoot))
pb.environment().putAll(buildSdkEnvironment())
pb.redirectErrorStream(true)
val proc = pb.start()
val exited = proc.waitFor(GEM_CHECK_TIMEOUT_SECONDS, TimeUnit.SECONDS)
Expand Down Expand Up @@ -504,6 +497,24 @@ class DocscribeDaemon(
return if (path != null && path.isNotBlank() && File(path).canExecute()) path else null
}

/**
* Build environment variables that point `bundle` and `ruby` to the project's Ruby SDK.
*
* Prepends the SDK's `bin/` directory to `PATH` and sets `BUNDLE_GEMFILE`.
* Returns an empty map when no SDK is configured or the SDK binary is not found.
*/
private fun buildSdkEnvironment(): Map<String, String> {
val ruby = rubyCommand() ?: return emptyMap()
val sdkBin = File(ruby).parentFile?.absolutePath ?: return emptyMap()
val currentPath = System.getenv("PATH") ?: ""
val env = mutableMapOf("PATH" to "$sdkBin${File.pathSeparator}$currentPath")
val gemRoot = DocscribeRunner.findProjectRoot(project.basePath ?: "")
if (gemRoot != null) {
env["BUNDLE_GEMFILE"] = File(gemRoot, "Gemfile").absolutePath
}
return env
}

/**
* Perform a single JSON-RPC 2.0 call over a Unix domain socket.
*
Expand Down Expand Up @@ -582,7 +593,9 @@ class DocscribeDaemon(
strategy = strategy,
formatJson = formatJson,
)
return DocscribeRunner.runDocscribe(options, DefaultCommandExecutor())
val sdkEnv = buildSdkEnvironment()
val executor = if (sdkEnv.isEmpty()) DefaultCommandExecutor() else DefaultCommandExecutor(sdkEnv)
return DocscribeRunner.runDocscribe(options, executor)
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,9 @@ interface CommandExecutor {
*
* Applies a 120-second timeout. Exit code 2 is treated as a failure (indistinguishable from timeout).
*/
class DefaultCommandExecutor : CommandExecutor {
class DefaultCommandExecutor(
private val environment: Map<String, String> = emptyMap(),
) : CommandExecutor {
override fun execute(
cmd: String,
args: List<String>,
Expand All @@ -93,6 +95,7 @@ class DefaultCommandExecutor : CommandExecutor {
GeneralCommandLine(cmd)
.withParameters(args)
.withWorkDirectory(cwd)
.withEnvironment(environment)
val handler = CapturingProcessHandler(commandLine)
val output =
try {
Expand Down
Loading