# `Macaw.Host.Release`
[🔗](https://github.com/dcoai/macaw_host/blob/v0.1.4/lib/macaw/host/release.ex#L1)

The release side of an app's `<app>-ctl`: entry points the script runs as
`bin/<app> eval` (in a throwaway container, so they work with the app
stopped) or `bin/<app> rpc` (asking the running app).

The protocol, the same for every app:
- **Arguments** arrive as a JSON array in `<PREFIX>_CTL_ARGS`, never on
  a command line `ps` can show; a password in `<PREFIX>_CTL_PASSWORD`.
- **Output** is one JSON object per line on stdout, for the script to
  read; a failure is one line on stderr and exit status 1.
- **Nothing typed becomes an atom.** Names are matched as strings
  (`String.to_existing_atom/1` fails under `eval` for atoms the release
  never loaded, though tests pass: the bug this module exists to rule out).

An app's release module delegates to these:

    defmodule MyApp.Release do
      def migrate, do: Macaw.Host.Release.migrate(:my_app)
      def config, do: Macaw.Host.Release.config(:my_app, MyApp.Settings.specs(), "MY_APP")
      def status, do: Macaw.Host.Release.status(MyApp.Health.report())
    end

# `config`

```elixir
@spec config(atom(), [Macaw.Host.Config.spec()], String.t()) :: :ok
```

`<app>-ctl config list | get NAME | set NAME VALUE | unset NAME`, over the
app's literal `config.exs` in its data directory (`:data_dir` in the
app's environment). Arguments from `<PREFIX>_CTL_ARGS`.

# `config_command`

```elixir
@spec config_command(
  Path.t(),
  [Macaw.Host.Config.spec()],
  String.t(),
  [String.t()],
  map()
) ::
  {:ok, [map()]} | {:error, String.t()}
```

The config commands, pure apart from the file and `env` (the environment,
`System.get_env/0` by default): `{:ok, objects}` or `{:error, message}`.
A file with faults is the error, every fault on one line.

`list` and `get` show each setting as the app would load it, through
`Macaw.Host.Config.resolve_setting/4`, the path `Macaw.Host.Config.load!/3`
takes: `%{name, value, source}`, the value typed and then shown
(`Macaw.Host.Config.mask/2`), or, when it doesn't fit its type, the value
as found with the load's `fault`.

# `ctl_args!`

```elixir
@spec ctl_args!(String.t()) :: [String.t()]
```

The arguments in `<PREFIX>_CTL_ARGS`, a JSON array of strings. Fails with a
message when it is missing or malformed.

# `ctl_password`

```elixir
@spec ctl_password(String.t()) :: String.t() | nil
```

The password in `<PREFIX>_CTL_PASSWORD`, or nil.

# `emit`

```elixir
@spec emit(map()) :: :ok
```

Prints one JSON object as a line.

# `fail`

```elixir
@spec fail(String.t()) :: no_return()
```

Prints a message on stderr and exits with status 1.

# `migrate`

```elixir
@spec migrate(atom()) :: :ok
```

Runs every repo's migrations for `app` (its `:ecto_repos`), starting
only what migrating needs. Requires `ecto_sql`.

# `status`

```elixir
@spec status(map()) :: :ok
```

Prints the running app's report (a health map, say) as one JSON line, for `rpc`.

---

*Consult [api-reference.md](api-reference.md) for complete listing*
