Skip to main content

lib/marea/plugins/run.ex

defmodule Marea.Plugins.Run do
  @moduledoc """
  Execution subcommands: `marea run local`, `run release`, `run docker`.

  Each command returns `{:cmd, shell_command}` so the parent shell takes
  over for an interactive session (IEx, release start, docker run).
  See the [Run Plugin](run.html) guide.
  """

  use Malla.Plugin, plugin_deps: [Marea.Plugins.Base]

  alias Marea.{Lib, Config}

  ## ===================================================================
  ## Args
  ## ===================================================================

  defcb marea_config_args(args) do
    alias Marea.Config.Args

    subcommands = [
      run: [
        name: "run",
        about: "Execute related operations",
        subcommands: [
          local: [
            name: "local",
            about: "Executes system with dev mix environment",
            options: [
              Args.cookie_option(),
              Args.pos_option()
            ]
          ],
          release: [
            name: "release",
            about: "Executes release",
            options: [
              Args.deploy_option(),
              Args.release_option(),
              Args.cookie_option(),
              Args.pos_option()
            ]
          ],
          docker: [
            name: "docker",
            about: "Executes release",
            options: [
              Args.deploy_option(),
              Args.release_option(),
              Args.cookie_option(),
              Args.pos_option()
            ]
          ]
        ]
      ]
    ]

    {:cont, [Args.add_subcommands(args, subcommands)]}
  end

  ## ===================================================================
  ## Callbacks
  ## ===================================================================

  defcb marea_cmd([:run | rest], %Config{} = config), do: run_cmd(rest, config)
  defcb marea_cmd(_cmds, _config), do: :cont

  ## ===================================================================
  ## Commands
  ## ===================================================================

  @doc """
  Public so `Marea.Plugins.Build` can chain into it when `--run` is set
  on `build release`. Returns the same `{:cmd, ...}` shape as the
  command callback.
  """
  @spec run_cmd([atom()], Config.t()) ::
          {:cmd, String.t()} | {:cmd, String.t(), Config.t()} | :usage
  def run_cmd([:local], config) do
    cookie = config.options.cookie
    pos = config.options.pos
    node_name = if pos == 0, do: "marea", else: "marea_#{pos}"

    cmd =
      "ENV=dev TARGET=local " <>
        "iex --name #{node_name}@127.0.0.1 --cookie \"#{cookie}\" -S mix run"

    {:cmd, cmd}
  end

  def run_cmd([:release], config) do
    {deploy, _deploy_data, release, _release_data} = Lib.get_release!(config)
    pos = config.options.pos

    node_name = if pos == 0, do: release, else: "#{release}_#{pos}"
    node_host = "127.0.0.1"
    httpd_port = to_string(3001 + pos)

    vars = [
      "NODE_HOST=#{node_host}",
      "NODE_NAME=#{node_name}",
      "RELEASE_COOKIE=#{config.options.cookie}",
      "MAREA_RELEASE=#{release}",
      "MAREA_DEPLOY=#{deploy}",
      "MAREA_SYSTEM_PORT=#{httpd_port}"
    ]

    cmd = "#{Enum.join(vars, " ")} _build/prod/rel/#{release}/bin/#{release} start_iex"

    {:cmd, cmd, config}
  end

  def run_cmd([:docker], config) do
    {deploy, _deploy_data, release, _release_data} = Lib.get_release!(config)
    pos = config.options.pos
    node_name = if pos == 0, do: release, else: "#{release}_#{pos}"

    image_tag =
      case config.values[:git_vsn] do
        nil -> "latest"
        vsn -> vsn
      end

    [
      ["deploy", deploy],
      ["release", release],
      ["pos", pos],
      ["node", node_name],
      ["image_tag", image_tag]
    ]
    |> Lib.print_table("DOCKER RUN")

    parts = [
      "docker run",
      "-ti",
      "--rm",
      "-e MAREA_RELEASE=#{release}",
      "-e MAREA_DEPLOY=#{deploy}",
      "-e NK_SYSTEM_PORT=0",
      "--hostname #{node_name}",
      "--name #{node_name}",
      "--platform arm64",
      "#{release}:#{image_tag}",
      case config.options do
        %{console: true} -> "/opt/#{release}/bin/#{release} start_iex"
        _ -> ""
      end
    ]

    {:cmd, Enum.join(parts, " ")}
  end

  def run_cmd(_, _config), do: :usage
end