defmodule LivebookWeb.SessionLive.AppDockerComponent do use LivebookWeb, :live_component import Ecto.Changeset alias Livebook.Hubs alias Livebook.FileSystem alias LivebookWeb.AppHelpers @impl true def update(assigns, socket) do socket = assign(socket, assigns) {:ok, socket |> assign(settings_valid?: Livebook.Notebook.AppSettings.valid?(socket.assigns.settings)) |> assign( hub_secrets: Hubs.get_secrets(assigns.hub), hub_file_systems: Hubs.get_file_systems(assigns.hub, hub_only: true) ) |> assign_new(:changeset, fn -> Hubs.Dockerfile.config_changeset() end) |> assign_new(:save_result, fn -> nil end) |> update_dockerfile()} end @impl true def render(assigns) do ~H"""

App deployment

<.content file={@file} settings_valid?={@settings_valid?} hub={@hub} changeset={@changeset} session={@session} dockerfile={@dockerfile} warnings={@warnings} save_result={@save_result} myself={@myself} />
""" end defp content(%{file: nil} = assigns) do ~H"""

To deploy this app, make sure to save the notebook first.

<.link class="text-blue-600 font-medium" patch={~p"/sessions/#{@session.id}/settings/file"}> Save <.remix_icon icon="arrow-right-line" />
""" end defp content(%{settings_valid?: false} = assigns) do ~H"""

To deploy this app, make sure to specify valid settings.

<.link class="text-blue-600 font-medium" patch={~p"/sessions/#{@session.id}/settings/app"}> Configure <.remix_icon icon="arrow-right-line" />
""" end defp content(assigns) do ~H"""

You can deploy this app in the cloud using Docker. To do that, configure the deployment and then use the generated Dockerfile.

<.label>Hub <%= @hub.hub_emoji %> <%= @hub.hub_name %>

<.message_box :for={warning <- @warnings} kind={:warning}> <%= raw(warning) %>
<.form :let={f} for={@changeset} as={:data} phx-change="validate" phx-target={@myself}> <.save_result :if={@save_result} save_result={@save_result} /> <:dockerfile_actions>
""" end defp save_result(%{save_result: {:ok, file}}) do assigns = %{path: file.path} ~H""" <.message_box kind={:info} message={"File saved at #{@path}"} /> """ end defp save_result(%{save_result: {:error, message}}) do assigns = %{message: message} ~H""" <.message_box kind={:error} message={@message} /> """ end @impl true def handle_event("validate", %{"data" => data}, socket) do changeset = data |> Hubs.Dockerfile.config_changeset() |> Map.replace!(:action, :validate) {:noreply, assign(socket, changeset: changeset) |> update_dockerfile()} end def handle_event("save_dockerfile", %{}, socket) do dockerfile_file = FileSystem.File.resolve(socket.assigns.file, "./Dockerfile") save_result = case FileSystem.File.write(dockerfile_file, socket.assigns.dockerfile) do :ok -> {:ok, dockerfile_file} {:error, message} -> {:error, message} end {:noreply, assign(socket, save_result: save_result)} end defp update_dockerfile(socket) when socket.assigns.file == nil do assign(socket, dockerfile: nil, warnings: []) end defp update_dockerfile(socket) do config = apply_changes(socket.assigns.changeset) %{ hub: hub, hub_secrets: hub_secrets, hub_file_systems: hub_file_systems, file: file, file_entries: file_entries, secrets: secrets, app_settings: app_settings } = socket.assigns dockerfile = Hubs.Dockerfile.build_dockerfile( config, hub, hub_secrets, hub_file_systems, file, file_entries, secrets ) warnings = Hubs.Dockerfile.warnings( config, hub, hub_secrets, hub_file_systems, app_settings, file_entries, secrets ) assign(socket, dockerfile: dockerfile, warnings: warnings) end end