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
60 changes: 42 additions & 18 deletions opsm_ex/lib/opsm/runtime/manager.ex
Original file line number Diff line number Diff line change
Expand Up @@ -292,31 +292,55 @@ defmodule Opsm.Runtime.Manager do
nil ->
{:error, {:no_plugin, tool}}
path ->
case System.cmd("nickel", ["export", "--format", "json", path], stderr_to_stdout: true) do
{json_str, 0} ->
case Jason.decode(json_str) do
{:ok, plugin} ->
# Cache for next time
File.mkdir_p!(@plugins_dir)
File.write!(cached, json_str)
{:ok, plugin}
err -> {:error, {:json_decode, err}}
# System.cmd/3 raises ErlangError (:enoent) when the nickel binary
# is not installed — surface that as a structured error instead.
try do
case System.cmd("nickel", ["export", "--format", "json", path], stderr_to_stdout: true) do
{json_str, 0} ->
case Jason.decode(json_str) do
{:ok, plugin} ->
# Cache for next time
File.mkdir_p!(@plugins_dir)
File.write!(cached, json_str)
{:ok, plugin}
err -> {:error, {:json_decode, err}}
end
{err_str, _} ->
{:error, {:nickel_eval, err_str}}
end
rescue
e in ErlangError ->
case e do
%ErlangError{original: :enoent} -> {:error, {:nickel_not_installed, tool}}
_ -> reraise e, __STACKTRACE__
end
{err_str, _} ->
{:error, {:nickel_eval, err_str}}
end
end
end
end

@plugin_search_dirs [
# Relative to the OPSM install — resolved at runtime
Path.expand("../runtime/core", :code.priv_dir(:opsm)),
Path.expand("~/.opsm/plugins/core"),
]
# Computed at call time, NOT a module attribute: an attribute freezes
# :code.priv_dir/1 at compile time (pointing inside whatever _build the
# module was compiled in), and the repo-checkout fallbacks depend on cwd.
defp plugin_search_dirs do
priv_relative =
case :code.priv_dir(:opsm) do
{:error, _} -> []
priv -> [Path.expand("../runtime/core", priv)]
end

priv_relative ++
[
Path.expand("~/.opsm/plugins/core"),
# Repo checkout: cwd is opsm_ex/ under mix, the repo root as escript
Path.expand("../runtime/core", File.cwd!()),
Path.expand("runtime/core", File.cwd!())
]
end

defp find_plugin_ncl(tool) do
Enum.find_value(@plugin_search_dirs, fn dir ->
@doc false
def find_plugin_ncl(tool) do
Enum.find_value(plugin_search_dirs(), fn dir ->
path = Path.join(dir, "#{tool}.ncl")
if File.exists?(path), do: path
end)
Expand Down
6 changes: 0 additions & 6 deletions opsm_ex/test/opsm/runtime/integration_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,6 @@ defmodule Opsm.Runtime.IntegrationTest do
# ---------------------------------------------------------------------------

describe "Manager.install/2 — live download (zig 0.13.0)" do
@tag :external_api
@tag :live_download
test "installs zig 0.13.0 and creates expected directory layout" do
install_result = Manager.install("zig", @zig_stable)
Expand All @@ -158,7 +157,6 @@ defmodule Opsm.Runtime.IntegrationTest do
assert File.dir?(install_dir), "expected install_dir #{install_dir} to exist"
end

@tag :external_api
@tag :live_download
test "which/1 returns a path after successful install" do
Manager.install("zig", @zig_stable)
Expand All @@ -175,7 +173,6 @@ defmodule Opsm.Runtime.IntegrationTest do
end
end

@tag :external_api
@tag :live_download
test "current_version/1 returns the installed version after install" do
Manager.install("zig", @zig_stable)
Expand All @@ -185,7 +182,6 @@ defmodule Opsm.Runtime.IntegrationTest do
assert version == @zig_stable or version == "none"
end

@tag :external_api
@tag :live_download
test "remove/1 cleans up installed tool" do
Manager.install("zig", @zig_stable)
Expand All @@ -202,7 +198,6 @@ defmodule Opsm.Runtime.IntegrationTest do
# ---------------------------------------------------------------------------

describe "Manager.install/2 — idempotency" do
@tag :external_api
@tag :live_download
test "installing the same version twice returns :ok both times" do
on_exit(fn -> Manager.remove("zig") end)
Expand All @@ -229,7 +224,6 @@ defmodule Opsm.Runtime.IntegrationTest do
{:ok, dir: dir}
end

@tag :external_api
@tag :live_download
test "installs tools declared in [runtime] section", %{dir: dir} do
manifest = Path.join(dir, "opsm.toml")
Expand Down
18 changes: 18 additions & 0 deletions opsm_ex/test/opsm/runtime/manager_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,24 @@ defmodule Opsm.Runtime.ManagerTest do
end
end

# ---------------------------------------------------------------------------
# find_plugin_ncl/1 — plugin search path resolution
# ---------------------------------------------------------------------------

describe "find_plugin_ncl/1" do
test "resolves a core plugin from the repo checkout (runtime/core)" do
# Under mix, cwd is opsm_ex/ — the sibling runtime/core/ dir must be
# searched at call time (a compile-time priv_dir attribute cannot see it)
path = Manager.find_plugin_ncl("zig")
assert is_binary(path), "expected zig.ncl to resolve from the repo checkout"
assert String.ends_with?(path, "/zig.ncl")
end

test "returns nil for a tool with no plugin definition" do
assert Manager.find_plugin_ncl("definitely-not-a-real-tool-xyz") == nil
end
end

# ---------------------------------------------------------------------------
# list_installed/0 — filesystem scan
# ---------------------------------------------------------------------------
Expand Down
Loading