From 64ae82e304daa8445ba105ed0981bc6b2e2969a7 Mon Sep 17 00:00:00 2001 From: Camilowser Date: Fri, 26 Apr 2024 21:18:11 -0400 Subject: [PATCH] added new example --- README.md | 15 +++--- pod_babashka_sqlite3/.formatter.exs | 4 ++ pod_babashka_sqlite3/.gitignore | 26 ++++++++++ pod_babashka_sqlite3/README.md | 49 ++++++++++++++++++ pod_babashka_sqlite3/artifacts/pod.edn | 4 ++ pod_babashka_sqlite3/lib/manifest.ex | 45 +++++++++++++++++ pod_babashka_sqlite3/lib/mix/tasks/check.ex | 15 ++++++ pod_babashka_sqlite3/lib/mix/tasks/install.ex | 35 +++++++++++++ pod_babashka_sqlite3/lib/pod.ex | 50 +++++++++++++++++++ pod_babashka_sqlite3/mix.exs | 29 +++++++++++ .../test/pod_babashka_hsqldb_test.exs | 8 +++ pod_babashka_sqlite3/test/test_helper.exs | 1 + pod_lispyclouds_sqlite/README.md | 24 +++------ pod_lispyclouds_sqlite/lib/manifest.ex | 6 ++- pods_core/README.md | 23 ++------- pods_core/lib/pods.ex | 2 + pods_example_project/.gitignore | 2 + pods_example_project/README.md | 24 +++------ pods_example_project/lib/decoder.ex | 10 ++-- pods_example_project/lib/encoder.ex | 5 +- pods_example_project/lib/handler.ex | 3 +- .../lib/pods_example_project/application.ex | 19 ++++++- pods_example_project/mix.exs | 4 +- pods_example_project/mix.lock | 3 ++ pods_process_manager/README.md | 22 ++------ pods_process_manager/lib/manager.ex | 35 +++++++++---- 26 files changed, 361 insertions(+), 102 deletions(-) create mode 100644 pod_babashka_sqlite3/.formatter.exs create mode 100644 pod_babashka_sqlite3/.gitignore create mode 100644 pod_babashka_sqlite3/README.md create mode 100644 pod_babashka_sqlite3/artifacts/pod.edn create mode 100644 pod_babashka_sqlite3/lib/manifest.ex create mode 100644 pod_babashka_sqlite3/lib/mix/tasks/check.ex create mode 100644 pod_babashka_sqlite3/lib/mix/tasks/install.ex create mode 100644 pod_babashka_sqlite3/lib/pod.ex create mode 100644 pod_babashka_sqlite3/mix.exs create mode 100644 pod_babashka_sqlite3/test/pod_babashka_hsqldb_test.exs create mode 100644 pod_babashka_sqlite3/test/test_helper.exs diff --git a/README.md b/README.md index 26b817c..57d83ca 100644 --- a/README.md +++ b/README.md @@ -82,15 +82,7 @@ for technologies that: 2. Are not available as _NIF_. 3. Other reasons for fun and profit?. -#### Why not Erlang Ports? - -The problem with _Erlang Ports_ is zombie processes and that not every -technology has a proper _CLI_. With this approach you can implement -a simple communication interface using standard tools. - -More details about problems with Erlang Ports in the awesome lib - -- https://github.com/fhunleth/muontrap +### Starting Pods The example process manager uses https://github.com/saleyn/erlexec/ but you can implement the pod services using `System.cmd` or `Erlang Ports` @@ -151,6 +143,11 @@ defp deps do end ``` +##### Examples + +- [Simple Pod](pod_lispyclouds_sqlite): Just a simple python script +- [Advanced Pod](pod_babashka_sqlite3): A program that requires installation pipeline + ## Installation ```bash diff --git a/pod_babashka_sqlite3/.formatter.exs b/pod_babashka_sqlite3/.formatter.exs new file mode 100644 index 0000000..d2cda26 --- /dev/null +++ b/pod_babashka_sqlite3/.formatter.exs @@ -0,0 +1,4 @@ +# Used by "mix format" +[ + inputs: ["{mix,.formatter}.exs", "{config,lib,test}/**/*.{ex,exs}"] +] diff --git a/pod_babashka_sqlite3/.gitignore b/pod_babashka_sqlite3/.gitignore new file mode 100644 index 0000000..4067aea --- /dev/null +++ b/pod_babashka_sqlite3/.gitignore @@ -0,0 +1,26 @@ +# The directory Mix will write compiled artifacts to. +/_build/ + +# If you run "mix test --cover", coverage assets end up here. +/cover/ + +# The directory Mix downloads your dependencies sources to. +/deps/ + +# Where third-party dependencies like ExDoc output generated docs. +/doc/ + +# Ignore .fetch files in case you like to edit your project deps locally. +/.fetch + +# If the VM crashes, it generates a dump, let's ignore it too. +erl_crash.dump + +# Also ignore archive artifacts (built via "mix archive.build"). +*.ez + +# Ignore package tarball (built via "mix hex.build"). +pod_babashka_hsqldb-*.tar + +# Temporary files, for example, from tests. +/tmp/ diff --git a/pod_babashka_sqlite3/README.md b/pod_babashka_sqlite3/README.md new file mode 100644 index 0000000..4ab3b50 --- /dev/null +++ b/pod_babashka_sqlite3/README.md @@ -0,0 +1,49 @@ +# Pod Babashka SQLite3 + +This is an example pod that uses an external installation +pipeline for the artifacts. + +## Installation + +Requires `babashka` (`bb`) executable to install the pod. +It will install the artifacts inside the `pods` directory +on the project. + +- Add to `mix.exs` + +```elixir +{:pod_babashka_sqlite3, path: "../pod_babashka_sqlite3"} +``` + +```elixir +mix compile +``` + +- Install the artifacts + +```elixir +mix pod.babashka.sqlite3.check # Checks if babashka runtime is on the $PATH + +mix pod.babashka.sqlite3.install # Installs the sqlite3 pod in pods directory +``` + +## Note on Babashka Pods + +Some pods are specially created for clojure language (defines macros and other stuff), so it won't work well with elixir. + +_Elixir Pods_ is meant to fill a gap between a CLI and a RPC, +so if you stick to following the bencode + json approach it will work well. + +Example Incompatible Pod + +- https://github.com/babashka/pod-babashka-filewatcher/blob/master/src/main.rs#L50 + +Since it requires clojure code to be executed in the client. + +## Is this Working? + +At least the `describe` command works. Other command does not work +due to parser issues. + +Is best to create some pods that can be properly tested with Elixir Pods, +but at least this serves as an example of a more advanced pod configuration and installation procedure. diff --git a/pod_babashka_sqlite3/artifacts/pod.edn b/pod_babashka_sqlite3/artifacts/pod.edn new file mode 100644 index 0000000..66a30a1 --- /dev/null +++ b/pod_babashka_sqlite3/artifacts/pod.edn @@ -0,0 +1,4 @@ +; We will use babashka pods to install the artifacts +; we only pin the desired version +(require '[babashka.pods :as pods]) +(pods/load-pod 'org.babashka/go-sqlite3 "0.1.0") diff --git a/pod_babashka_sqlite3/lib/manifest.ex b/pod_babashka_sqlite3/lib/manifest.ex new file mode 100644 index 0000000..51c494f --- /dev/null +++ b/pod_babashka_sqlite3/lib/manifest.ex @@ -0,0 +1,45 @@ +defmodule Pod.Babashka.SQLite3.Manifest do + @moduledoc """ + Stores the information for the pod + """ + + def version, do: "0.1.0" + + @doc """ + Returns the namespace that will be used when invoking the commands + Example: pod.babashka.go-sqlite3/execute! + """ + def namespace, do: "pod.babashka.go-sqlite3" + + @doc """ + Which format will the payload be encoded/decoded: + - json + - transit+json + """ + def format, do: "transit+json" + + @doc """ + Which programming language this pod is implemented + """ + def language, do: "golang" + + defp directory(), do: Path.join([ + "pods", + "repository", + "org.babashka", + "go-sqlite3", + version(), + ]) + + @doc """ + The executable artifact that will be returned + depending on the requested os and arch params. + values are from :os.type() and :erlang.system_info(:system_architecture) + You can add other os and archs executables if needed. + """ + def executable(%{type: _type, os: :darwin, arch: "x86_64" <> _arch}), + do: + Path.expand( + Path.join([directory(), "mac_os_x", "x86_64", "pod-babashka-go-sqlite3"]) + ) +end diff --git a/pod_babashka_sqlite3/lib/mix/tasks/check.ex b/pod_babashka_sqlite3/lib/mix/tasks/check.ex new file mode 100644 index 0000000..8d2cde5 --- /dev/null +++ b/pod_babashka_sqlite3/lib/mix/tasks/check.ex @@ -0,0 +1,15 @@ +defmodule Mix.Tasks.Pod.Babashka.Sqlite3.Check do + @moduledoc """ + Provides helper commands to check SQLite3 pod + """ + + use Mix.Task + + @shortdoc """ + Checks if babashka runtime is on the $PATH + """ + def run(_) do + Pod.Babashka.SQLite3.babashka() + |> IO.inspect() + end +end diff --git a/pod_babashka_sqlite3/lib/mix/tasks/install.ex b/pod_babashka_sqlite3/lib/mix/tasks/install.ex new file mode 100644 index 0000000..386afed --- /dev/null +++ b/pod_babashka_sqlite3/lib/mix/tasks/install.ex @@ -0,0 +1,35 @@ +defmodule Mix.Tasks.Pod.Babashka.Sqlite3.Install do + @moduledoc """ + Provides helper commands to install Sqlite3 pod + """ + + use Mix.Task + + @shortdoc """ + Installs the sqlite3 pod in pods directory + """ + def run(_) do + {:ok, {exe, _}} = Pod.Babashka.SQLite3.babashka() + artifact = Path.expand( + Path.join([ + Path.dirname(__ENV__.file), + "..", + "..", + "..", + "artifacts", + "pod.edn" + ]) + ) + + project_pods_directory = Path.expand(Path.join(["pods"])) + File.mkdir_p!(project_pods_directory) + + # Check https://github.com/babashka/pods/ + # Will install for the current system only + # If you want other artifacts set env + # Other env BABASHKA_PODS_OS_NAME=Linux + # BASHKA_PODS_OS_ARCH=aarch64 + + System.cmd(exe, [artifact], env: [{"BABASHKA_PODS_DIR", project_pods_directory}]) + end +end diff --git a/pod_babashka_sqlite3/lib/pod.ex b/pod_babashka_sqlite3/lib/pod.ex new file mode 100644 index 0000000..9c9349c --- /dev/null +++ b/pod_babashka_sqlite3/lib/pod.ex @@ -0,0 +1,50 @@ +defmodule Pod.Babashka.SQLite3 do + + alias __MODULE__.Manifest + + def db(name \\ "testdb"), do: "#{name}.db" + + def babashka() do + {type, _} = :os.type() + exe = case type do + :unix -> {"which", ["bb"]} + _ -> {"where", ["bb.exe"]} + end + + {command, args} = exe + {bin, _} = System.cmd(command, args) + {version, _} = System.cmd(String.trim(bin), ["--version"]) + + case version do + "babashka v" <> number -> {:ok, {String.trim(bin), String.trim(number)}} + _ -> {:error, "babashka runtime not found"} + end + end + + def manifest, do: Manifest + + # options when loading the pod with the process manager + def opts, do: [] + + def setup(), + do: Pods.Core.setup(__MODULE__, Manifest) + + def describe(pods) do + Pods.Core.describe(pods, __MODULE__) + pods + end + + def invoke(pods, command, args \\ []) do + Pods.Core.invoke(pods, __MODULE__, command, args) + pods + end + + def execute!(pods, db, args \\ []) do + invoke(pods, "execute!", [db, args]) + end + + def query(pods, db, args \\ []) do + invoke(pods, "query", [db, args]) + end + +end diff --git a/pod_babashka_sqlite3/mix.exs b/pod_babashka_sqlite3/mix.exs new file mode 100644 index 0000000..a3928a9 --- /dev/null +++ b/pod_babashka_sqlite3/mix.exs @@ -0,0 +1,29 @@ +defmodule Pod.Babashka.SQLite3.MixProject do + use Mix.Project + + def project do + [ + app: :pod_babashka_sqlite3, + version: "0.1.0", + elixir: "~> 1.12", + start_permanent: Mix.env() == :prod, + deps: deps() + ] + end + + # Run "mix help compile.app" to learn about applications. + def application do + [ + extra_applications: [:logger] + ] + end + + # Run "mix help deps" to learn about dependencies. + defp deps do + [ + {:pods_core, path: "../pods_core"} + # {:dep_from_hexpm, "~> 0.3.0"}, + # {:dep_from_git, git: "https://github.com/elixir-lang/my_dep.git", tag: "0.1.0"} + ] + end +end diff --git a/pod_babashka_sqlite3/test/pod_babashka_hsqldb_test.exs b/pod_babashka_sqlite3/test/pod_babashka_hsqldb_test.exs new file mode 100644 index 0000000..70ae2bf --- /dev/null +++ b/pod_babashka_sqlite3/test/pod_babashka_hsqldb_test.exs @@ -0,0 +1,8 @@ +defmodule PodBabashkaHsqldbTest do + use ExUnit.Case + doctest PodBabashkaHsqldb + + test "greets the world" do + assert PodBabashkaHsqldb.hello() == :world + end +end diff --git a/pod_babashka_sqlite3/test/test_helper.exs b/pod_babashka_sqlite3/test/test_helper.exs new file mode 100644 index 0000000..869559e --- /dev/null +++ b/pod_babashka_sqlite3/test/test_helper.exs @@ -0,0 +1 @@ +ExUnit.start() diff --git a/pod_lispyclouds_sqlite/README.md b/pod_lispyclouds_sqlite/README.md index 287fe70..b7eadc0 100644 --- a/pod_lispyclouds_sqlite/README.md +++ b/pod_lispyclouds_sqlite/README.md @@ -1,21 +1,11 @@ -# PodLispycloudsSqlite +# Pod Lispyclouds Sqlite -**TODO: Add description** +This is an example pod for a simple python script. +Does not require a heavy pipeline to install. -## Installation +Remember to check that `main.py` has execution permissions +(655). -If [available in Hex](https://hex.pm/docs/publish), the package can be installed -by adding `pod_lispyclouds_sqlite` to your list of dependencies in `mix.exs`: - -```elixir -def deps do - [ - {:pod_lispyclouds_sqlite, "~> 0.1.0"} - ] -end +```bash +chmod +x main.py ``` - -Documentation can be generated with [ExDoc](https://github.com/elixir-lang/ex_doc) -and published on [HexDocs](https://hexdocs.pm). Once published, the docs can -be found at . - diff --git a/pod_lispyclouds_sqlite/lib/manifest.ex b/pod_lispyclouds_sqlite/lib/manifest.ex index 2de6e90..67049ff 100644 --- a/pod_lispyclouds_sqlite/lib/manifest.ex +++ b/pod_lispyclouds_sqlite/lib/manifest.ex @@ -3,6 +3,8 @@ defmodule Pod.LispyClouds.SQLite.Manifest do Stores the information for the pod """ + def version, do: "1.0.0" + @doc """ Returns the namespace that will be used when invoking the commands Example: pod.lispyclouds.sqlite/execute! @@ -24,7 +26,9 @@ defmodule Pod.LispyClouds.SQLite.Manifest do @doc """ The executable artifact that will be returned depending on the requested os and arch params. - values are from :os.type() and :erlang.system_info(:system_architecture) + values are from :os.type() and :erlang.system_info(:system_architecture). + This do not need to be installed in pods directory, since is just a python script. + and is not handled by babashka pods. """ def executable(%{type: _type, os: _os, arch: _arch}), do: diff --git a/pods_core/README.md b/pods_core/README.md index 74acb4b..4cb20b7 100644 --- a/pods_core/README.md +++ b/pods_core/README.md @@ -1,21 +1,6 @@ -# PodsCore +# Pods Core -**TODO: Add description** - -## Installation - -If [available in Hex](https://hex.pm/docs/publish), the package can be installed -by adding `pods_core` to your list of dependencies in `mix.exs`: - -```elixir -def deps do - [ - {:pods_core, "~> 0.1.0"} - ] -end -``` - -Documentation can be generated with [ExDoc](https://github.com/elixir-lang/ex_doc) -and published on [HexDocs](https://hexdocs.pm). Once published, the docs can -be found at . +This handles the main communication and decoding +from stdout. +And provides helper functions to pod creation. diff --git a/pods_core/lib/pods.ex b/pods_core/lib/pods.ex index 9eef8c5..342a8f9 100644 --- a/pods_core/lib/pods.ex +++ b/pods_core/lib/pods.ex @@ -46,6 +46,7 @@ defmodule Pods.Core do pod = module.setup() decode_out = fn %{origin: origin, pid: pid, response: response} = raw -> + decoded_response = decoder.decode!(response, :bencode) result = @@ -100,6 +101,7 @@ defmodule Pods.Core do pid: manager.load( module.manifest().executable(os_type()), + decoder, decode_out, decode_error, module.opts() diff --git a/pods_example_project/.gitignore b/pods_example_project/.gitignore index bdf8de9..4739b55 100644 --- a/pods_example_project/.gitignore +++ b/pods_example_project/.gitignore @@ -24,3 +24,5 @@ pods_example_project-*.tar # Temporary files, for example, from tests. /tmp/ + +pods/ diff --git a/pods_example_project/README.md b/pods_example_project/README.md index d1faaad..bd14e90 100644 --- a/pods_example_project/README.md +++ b/pods_example_project/README.md @@ -1,21 +1,11 @@ # PodsExampleProject -**TODO: Add description** +This is an example project that calls the Pods +and implements the: -## Installation - -If [available in Hex](https://hex.pm/docs/publish), the package can be installed -by adding `pods_example_project` to your list of dependencies in `mix.exs`: - -```elixir -def deps do - [ - {:pods_example_project, "~> 0.1.0"} - ] -end -``` - -Documentation can be generated with [ExDoc](https://github.com/elixir-lang/ex_doc) -and published on [HexDocs](https://hexdocs.pm). Once published, the docs can -be found at . +- stdin handler +- encoder +- decoders +This is an example on how the pods can be used +within your project. diff --git a/pods_example_project/lib/decoder.ex b/pods_example_project/lib/decoder.ex index bbce9d5..d241521 100644 --- a/pods_example_project/lib/decoder.ex +++ b/pods_example_project/lib/decoder.ex @@ -1,4 +1,9 @@ defmodule PodsExampleProject.Decoder do + + def decode(content, :bencode) do + Bento.decode(content) + end + def decode!(content, :bencode) do Bento.decode!(content) end @@ -7,8 +12,7 @@ defmodule PodsExampleProject.Decoder do Jason.decode!(content) end - def decode(_content, "transit+json") do - # not implemented yet - :noop + def decode!(content, "transit+json") do + :transit.read(content, %{format: :json_verbose}) end end diff --git a/pods_example_project/lib/encoder.ex b/pods_example_project/lib/encoder.ex index f18b15c..103998b 100644 --- a/pods_example_project/lib/encoder.ex +++ b/pods_example_project/lib/encoder.ex @@ -7,8 +7,7 @@ defmodule PodsExampleProject.Encoder do Jason.encode!(content) end - def encode(_content, "transit+json") do - # not implemented yet - :noop + def encode!(content, "transit+json") do + :transit.write(content, %{format: :json_verbose}) end end diff --git a/pods_example_project/lib/handler.ex b/pods_example_project/lib/handler.ex index b3c9835..6de488b 100644 --- a/pods_example_project/lib/handler.ex +++ b/pods_example_project/lib/handler.ex @@ -3,8 +3,7 @@ defmodule PodsExampleProject.Handler do IO.inspect([pod, message], label: :on_pod_ready) end - def on_before_call(_registry, pod, message, op) do - IO.inspect(pod.pid, label: op) + def on_before_call(_registry, _pod, message, _op) do IO.inspect(message, label: :on_before_call) end diff --git a/pods_example_project/lib/pods_example_project/application.ex b/pods_example_project/lib/pods_example_project/application.ex index d31e965..7fc6fe2 100644 --- a/pods_example_project/lib/pods_example_project/application.ex +++ b/pods_example_project/lib/pods_example_project/application.ex @@ -12,9 +12,10 @@ defmodule PodsExampleProject.Application do # {PodsExampleProject.Worker, arg} ] - Pods.Core.start( + pods = Pods.Core.start( # Available Pods List - [Pod.LispyClouds.SQLite], + [ Pod.LispyClouds.SQLite, + Pod.Babashka.SQLite3], # Pod Manager Pods.ProcessManager, # Message Encoder @@ -24,11 +25,25 @@ defmodule PodsExampleProject.Application do # stdout and stderr handler PodsExampleProject.Handler ) + + pods |> Pod.LispyClouds.SQLite.execute!("create table if not exists foo ( int foo )") |> Pod.LispyClouds.SQLite.execute!("delete from foo") |> Pod.LispyClouds.SQLite.execute!("insert into foo values (1), (2)") |> Pod.LispyClouds.SQLite.execute!("select * from foo") + + # This pod uses bencode features not supported by the parser + # so at least the describe command works. + # serves as an example of a more complex pod + # db = Pod.Babashka.SQLite3.db() + # pods + # |> Pod.Babashka.SQLite3.execute!(db, "create table if not exists foo ( int foo )") + # |> Pod.Babashka.SQLite3.execute!(db, "delete from foo") + # |> Pod.Babashka.SQLite3.execute!(db, "insert into foo values (1), (2)") + # |> Pod.Babashka.SQLite3.query(db, "select * from foo") + + # See https://hexdocs.pm/elixir/Supervisor.html # for other strategies and supported options opts = [strategy: :one_for_one, name: PodsExampleProject.Supervisor] diff --git a/pods_example_project/mix.exs b/pods_example_project/mix.exs index c1216e7..50217ff 100644 --- a/pods_example_project/mix.exs +++ b/pods_example_project/mix.exs @@ -26,11 +26,13 @@ defmodule PodsExampleProject.MixProject do {:bento, "~> 1.0"}, # json {:jason, "~> 1.4"}, + {:transit, git: "https://github.com/ElixirCL/transit-erlang.git"}, # {:dep_from_hexpm, "~> 0.3.0"}, # {:dep_from_git, git: "https://github.com/elixir-lang/my_dep.git", tag: "0.1.0"} {:pods_core, path: "../pods_core"}, {:pods_process_manager, path: "../pods_process_manager"}, - {:pod_lispyclouds_sqlite, path: "../pod_lispyclouds_sqlite"} + {:pod_lispyclouds_sqlite, path: "../pod_lispyclouds_sqlite"}, + {:pod_babashka_sqlite3, path: "../pod_babashka_sqlite3"} ] end end diff --git a/pods_example_project/mix.lock b/pods_example_project/mix.lock index ef6d748..056ae5a 100644 --- a/pods_example_project/mix.lock +++ b/pods_example_project/mix.lock @@ -5,7 +5,10 @@ "ecto": {:hex, :ecto, "3.11.2", "e1d26be989db350a633667c5cda9c3d115ae779b66da567c68c80cfb26a8c9ee", [:mix], [{:decimal, "~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "3c38bca2c6f8d8023f2145326cc8a80100c3ffe4dcbd9842ff867f7fc6156c65"}, "erlexec": {:hex, :erlexec, "2.0.6", "b7443121cfb8add8bc25e3db9c1fd79d14613bbc406984264a0bbc62f121f377", [:rebar3], [], "hexpm", "8c4ebc02449f838648d9854d1c4dc7257e57d4ea2098a7e6386348372085ab21"}, "jason": {:hex, :jason, "1.4.1", "af1504e35f629ddcdd6addb3513c3853991f694921b1b9368b0bd32beb9f1b63", [:mix], [{:decimal, "~> 1.0 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "fbb01ecdfd565b56261302f7e1fcc27c4fb8f32d56eab74db621fc154604a7a1"}, + "jsx": {:git, "https://github.com/talentdeficit/jsx.git", "1d3407aa9752430ec0a06111ac1b046ffafdca22", [ref: "main"]}, + "msgpack": {:git, "https://github.com/msgpack/msgpack-erlang.git", "6b544de60723839244866b109d6a79c7caca6bc9", [ref: "master"]}, "rustler_precompiled": {:hex, :rustler_precompiled, "0.7.1", "ecadf02cc59a0eccbaed6c1937303a5827fbcf60010c541595e6d3747d3d0f9f", [:mix], [{:castore, "~> 0.1 or ~> 1.0", [hex: :castore, repo: "hexpm", optional: false]}, {:rustler, "~> 0.23", [hex: :rustler, repo: "hexpm", optional: true]}], "hexpm", "b9e4657b99a1483ea31502e1d58c464bedebe9028808eda45c3a429af4550c66"}, "telemetry": {:hex, :telemetry, "1.2.1", "68fdfe8d8f05a8428483a97d7aab2f268aaff24b49e0f599faa091f1d4e7f61c", [:rebar3], [], "hexpm", "dad9ce9d8effc621708f99eac538ef1cbe05d6a874dd741de2e689c47feafed5"}, + "transit": {:git, "https://github.com/ElixirCL/transit-erlang.git", "90f84b00a6e1429b74218e664465f05bd4bd18c8", []}, "uuidv7": {:hex, :uuidv7, "0.2.1", "2062e2ad63d3376ee1aa00089013cf538e24fcab989e444a1d043c6d6c76930e", [:mix], [{:ecto, "~> 3.10", [hex: :ecto, repo: "hexpm", optional: false]}, {:rustler, "~> 0.30.0", [hex: :rustler, repo: "hexpm", optional: true]}, {:rustler_precompiled, "~> 0.7", [hex: :rustler_precompiled, repo: "hexpm", optional: false]}], "hexpm", "a7ec15522c7796399469ad3e8a00190d5fe4a02839944bea61dfbde35fea12e5"}, } diff --git a/pods_process_manager/README.md b/pods_process_manager/README.md index abed85b..7c28016 100644 --- a/pods_process_manager/README.md +++ b/pods_process_manager/README.md @@ -1,21 +1,5 @@ # PodsProcessManager -**TODO: Add description** - -## Installation - -If [available in Hex](https://hex.pm/docs/publish), the package can be installed -by adding `pods_process_manager` to your list of dependencies in `mix.exs`: - -```elixir -def deps do - [ - {:pods_process_manager, "~> 0.1.0"} - ] -end -``` - -Documentation can be generated with [ExDoc](https://github.com/elixir-lang/ex_doc) -and published on [HexDocs](https://hexdocs.pm). Once published, the docs can -be found at . - +This is the process manager. +Implements how the pods will be started on the operating system +and provides the main `stdio` interface for pod communication. diff --git a/pods_process_manager/lib/manager.ex b/pods_process_manager/lib/manager.ex index d6b9359..a7cbe7a 100644 --- a/pods_process_manager/lib/manager.ex +++ b/pods_process_manager/lib/manager.ex @@ -2,21 +2,38 @@ defmodule Pods.ProcessManager do def start(), do: :exec.start() def send(pid, message), do: :exec.send(pid, message) - def load(executable, stdout_handler, stderr_handler, opts \\ []) do + def load(executable, decoder, stdout_handler, stderr_handler, opts \\ []) do + + temp_dir = System.tmp_dir!() + temp_file_stdout = Path.join([temp_dir, Path.basename(executable) <> ".stdout"]) + + # Some processes may write a lot of text, so we try to decode a cache + # until it works and then send the data to the handler + stdout_watcher = fn origin, pid, response -> + File.write!(temp_file_stdout, response, [:append]) + case decoder.decode(String.trim(File.read!(temp_file_stdout)), :bencode) do + {:ok, data} -> + stdout_handler.(%{origin: origin, pid: pid, response: response, data: data}) + File.rm_rf!(temp_file_stdout) + _ -> nil + end + end + + # errors may not be in bencode, so we send the raw response + stderr_watcher = fn origin, pid, response -> + stderr_handler.(%{origin: origin, pid: pid, response: response}) + end + {:ok, _, pid} = :exec.run_link( executable, [ :stdin, + {:stdout, stdout_watcher}, + {:stderr, stderr_watcher}, :monitor, - {:stderr, - fn origin, pid, response -> - stderr_handler.(%{origin: origin, pid: pid, response: response}) - end}, - {:stdout, - fn origin, pid, response -> - stdout_handler.(%{origin: origin, pid: pid, response: response}) - end} + # https://github.com/babashka/pods/?tab=readme-ov-file#environment + {:env, [{"BABASHKA_POD", "1"}]} ] ++ opts )