Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
7 changes: 5 additions & 2 deletions apps/expert/lib/expert/engine_node.ex
Original file line number Diff line number Diff line change
Expand Up @@ -171,8 +171,11 @@ defmodule Expert.EngineNode do
# Expert release, and we build it on the fly for the project elixir+opt
# versions if it was not built yet.
defp glob_paths(%Project{} = project) do
project_name = Project.name(project)
{:ok, elixir, env} = Expert.Port.elixir_executable(project)

Logger.info("Found elixir for #{project_name} at #{elixir}")

expert_priv = :code.priv_dir(:expert)
packaged_engine_source = Path.join([expert_priv, "engine_source", "apps", "engine"])

Expand Down Expand Up @@ -202,10 +205,10 @@ defmodule Expert.EngineNode do

GenLSP.info(
Expert.get_lsp(),
"Finding or building engine for project #{Project.name(project)}"
"Finding or building engine for project #{project_name}"
)

with_progress(project, "Building engine for #{Project.name(project)}", fn ->
with_progress(project, "Building engine for #{project_name}", fn ->
port =
Port.open(
{:spawn_executable, launcher},
Expand Down
114 changes: 12 additions & 102 deletions apps/expert/lib/expert/port.ex
Original file line number Diff line number Diff line change
Expand Up @@ -34,113 +34,23 @@ defmodule Expert.Port do
def elixir_executable(%Project{} = project) do
root_path = Project.root_path(project)

{path_result, env} =
with nil <- version_manager_path_and_env("asdf", root_path),
nil <- version_manager_path_and_env("mise", root_path),
nil <- version_manager_path_and_env("rtx", root_path) do
{File.cd!(root_path, fn -> System.find_executable("elixir") end), System.get_env()}
end
# We run a shell in interactive mode to populate the PATH with the right value
# at the project root. Otherwise, we either can't find an elixir executable,
# we use the wrong version if the user uses a version manager like asdf/mise,
# or we get an incomplete PATH not including erl or any other version manager
# managed programs.
shell = System.get_env("SHELL")

case path_result do
nil ->
{:error, :no_elixir}

executable when is_binary(executable) ->
{:ok, executable, env}
end
end

defp version_manager_path_and_env(manager, root_path) do
with true <- is_binary(System.find_executable(manager)),
env = reset_env(manager, root_path),
{path, 0} <- System.cmd(manager, ~w(which elixir), cd: root_path, env: env) do
{String.trim(path), env}
else
_ ->
nil
end
end

# We launch expert by asking the version managers to provide an environment,
# which contains path munging. This initial environment is present in the running
# VM, and needs to be undone so we can find the correct elixir executable in the project.
defp reset_env("asdf", root_path) do
{env, _} = System.cmd("asdf", ~w(env elixir), cd: root_path)
{path, 0} = System.cmd(shell, ["-i", "-l", "-c", "cd #{root_path} && echo $PATH"])
elixir = :os.find_executable(~c"elixir", to_charlist(path))

env =
env
|> String.trim()
|> String.split("\n")
|> Enum.map(fn key_and_value ->
[key, value] =
key_and_value
|> String.split("=", parts: 2)
|> Enum.map(&String.trim/1)

{key, value}
Enum.map(System.get_env(), fn
{"PATH", _path} -> {"PATH", path}
other -> other
end)
|> Enum.reject(&is_nil/1)

asdf_path =
case List.keyfind(env, "ASDF_INSTALL_PATH", 0) do
{_, path} -> Path.join(path, "../../../shims")
_ -> ""
end

Enum.map(System.get_env(), fn
{"PATH", path} -> {"PATH", "#{asdf_path}:#{path}"}
other -> other
end)
end

defp reset_env("rtx", root_path) do
{env, _} = System.cmd("rtx", ~w(env -s bash), cd: root_path)

env
|> String.trim()
|> String.split("\n")
|> Enum.map(fn
"export " <> key_and_value ->
[key, value] =
key_and_value
|> String.split("=", parts: 2)
|> Enum.map(&String.trim/1)
|> then(fn
["PATH", path] -> ["PATH", String.trim(path, "'")]
other -> other
end)

{key, value}

_ ->
nil
end)
|> Enum.reject(&is_nil/1)
end

defp reset_env("mise", root_path) do
{env, _} = System.cmd("mise", ~w(env -s bash), cd: root_path)

env
|> String.trim()
|> String.split("\n")
|> Enum.map(fn
"export " <> key_and_value ->
[key, value] =
key_and_value
|> String.split("=", parts: 2)
|> Enum.map(&String.trim/1)
|> then(fn
["PATH", path] -> ["PATH", String.trim(path, "'")]
other -> other
end)

{key, value}

_ ->
nil
end)
|> Enum.reject(&is_nil/1)
{:ok, elixir, env}
end

@doc """
Expand Down
Loading