Fly.io platform operations for ExAtlas.
This is the public facade for everything Fly-related in atlas. It deliberately
covers platform operations (deploys, logs, token lifecycle) — for GPU
compute, see ExAtlas.spawn_compute/2 and the provider behaviour.
Quick start
# In config/config.exs (optional — defaults are sensible)
config :ex_atlas, :fly,
enabled: true,
dispatcher: :registry,
storage_path: "priv/ex_atlas_fly"Discover apps from fly.toml
ExAtlas.Fly.discover_apps("/path/to/project")
# => [{"my-api", "/path/to/project"}, {"my-web", "/path/to/project/web"}]Tail logs
ExAtlas.Fly.subscribe_logs("my-api", "/path/to/project")
# Then in your GenServer / LiveView:
def handle_info({:ex_atlas_fly_logs, "my-api", entries}, state) do
# entries is a list of ExAtlas.Fly.Logs.LogEntry
...
endDeploy with streaming output
ExAtlas.Fly.subscribe_deploy(ticket_id)
ExAtlas.Fly.stream_deploy(project_path, "web", ticket_id)
def handle_info({:ex_atlas_fly_deploy, ^ticket_id, line}, state) do
...
end
Summary
Functions
Subscribe the calling pid to streamed deploy output for ticket_id.
Subscribe the calling pid to log events for app_name, starting a streamer
if none is running.
Unsubscribe the calling pid from streamed deploy output for ticket_id.
Unsubscribe the calling pid from log events for app_name.
Functions
See ExAtlas.Fly.Logs.StreamerSupervisor.streamer_running?/1.
Subscribe the calling pid to streamed deploy output for ticket_id.
Subscribe the calling pid to log events for app_name, starting a streamer
if none is running.
Returns {:error, :no_streamer} if the streamer supervisor tree is not
running (e.g. the Fly sub-tree is disabled via
config :ex_atlas, :fly, enabled: false).
project_dir is optional — it is carried in the Streamer's state for
introspection but not used in the log-fetching code path.
Teardown
When the Streamer for app_name stops (all subscribers gone, or
stop_streamer/1), it sends a final
{:ex_atlas_fly_logs_stopped, app_name} message on the same topic.
Subscribers should match on that and call unsubscribe_logs/1 to clean
up — atlas cannot unsubscribe them from a framework-agnostic dispatcher.
@spec unsubscribe_deploy(String.t()) :: :ok
Unsubscribe the calling pid from streamed deploy output for ticket_id.
@spec unsubscribe_logs(String.t()) :: :ok
Unsubscribe the calling pid from log events for app_name.