defmodule LivebookWeb.SessionLive.AppTeamsComponent do use LivebookWeb, :live_component @impl true def update(assigns, socket) do socket = assign(socket, assigns) deployment_groups = Livebook.Teams.get_deployment_groups(assigns.hub) app_deployments = Livebook.Teams.get_app_deployments(assigns.hub) deployment_group = if assigns.deployment_group_id do Enum.find(deployment_groups, &(&1.id == assigns.deployment_group_id)) end app_deployment = if deployment_group do Enum.find( app_deployments, &(&1.slug == assigns.app_settings.slug and &1.deployment_group_id == deployment_group.id) ) end socket = socket |> assign( settings_valid?: Livebook.Notebook.AppSettings.valid?(socket.assigns.app_settings), app_deployment: app_deployment, deployment_groups: deployment_groups, deployment_group: deployment_group, deployment_group_form: %{"deployment_group_id" => assigns.deployment_group_id}, deployment_group_id: assigns.deployment_group_id ) |> assign_new(:messages, fn -> [] end) {:ok, socket} end @impl true def render(assigns) do ~H"""

App deployment with Livebook Teams

<.content file={@file} hub={@hub} app_deployment={@app_deployment} deployment_group={@deployment_group} deployment_groups={@deployment_groups} deployment_group_form={@deployment_group_form} deployment_group_id={@deployment_group_id} session={@session} messages={@messages} myself={@myself} />
""" 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?context=app-teams"} > Configure <.remix_icon icon="arrow-right-line" />
""" end defp content(assigns) do ~H"""

You can deploy this app in the to your own cloud using Livebook Teams. To do that, select which deployment group you want to send this app and then click the button to Deploy.

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

<%= if @deployment_groups do %> <%= if @deployment_groups != [] do %> <.form for={@deployment_group_form} phx-change="select_deployment_group" phx-target={@myself} id="select_deployment_group_form" > <.select_field help={deployment_group_help()} field={@deployment_group_form[:deployment_group_id]} options={deployment_group_options(@deployment_groups)} label="Deployment Group" name="deployment_group_id" value={@deployment_group_id} /> <% else %>

<.label help={deployment_group_help()}> Deployment Group None configured

<% end %> <% end %>
<.message_box :for={{kind, message} <- @messages} kind={kind}> <%= raw(message) %>

Current deployed version:

<.button id="deploy-livebook-agent-button" color="blue" phx-click="deploy_app" phx-target={@myself} > <.remix_icon icon="rocket-line" /> Deploy to Livebook Agent
""" end @impl true def handle_event("select_deployment_group", %{"deployment_group_id" => id}, socket) do id = if(id != "", do: id) Livebook.Session.set_notebook_deployment_group(socket.assigns.session.pid, id) {:noreply, socket} end def handle_event("deploy_app", _, socket) do notebook = Livebook.Session.get_notebook(socket.assigns.session.pid) with {:ok, app_deployment} <- pack_app(socket, notebook), :ok <- deploy_app(socket, app_deployment, notebook.app_settings) do message = "App deployment for #{app_deployment.slug} with title #{app_deployment.title} created successfully" {:noreply, assign(socket, messages: [{:info, message}])} end end defp pack_app(socket, notebook) do files_dir = socket.assigns.session.files_dir case Livebook.Teams.AppDeployment.new(notebook, files_dir) do {:ok, app_deployment} -> {:ok, app_deployment} {:warning, warnings} -> messages = Enum.map(warnings, &{:error, &1}) {:noreply, assign(socket, messages: messages)} {:error, error} -> error = "Failed to pack files: #{error}" {:noreply, assign(socket, messages: [{:error, error}])} end end defp deploy_app(socket, app_deployment, app_settings) do case Livebook.Teams.deploy_app(socket.assigns.hub, app_deployment, app_settings) do :ok -> :ok {:error, %{errors: errors}} -> errors = Enum.map(errors, fn {key, error} -> "#{key}: #{normalize_error(error)}" end) {:noreply, assign(socket, messages: errors)} {:transport_error, error} -> {:noreply, assign(socket, messages: [{:error, error}])} end end defp normalize_error({msg, opts}) do Enum.reduce(opts, msg, fn {key, value}, acc -> String.replace(acc, "%{#{key}}", to_string(value)) end) end defp deployment_group_options(deployment_groups) do for deployment_group <- [%{name: "none", id: nil}] ++ deployment_groups, do: {deployment_group.name, deployment_group.id} end defp deployment_group_help() do "Share deployment credentials, secrets, and configuration with deployment groups." end end