diff --git a/lib/livebook/application.ex b/lib/livebook/application.ex index 0d44835b7..52cce2e55 100644 --- a/lib/livebook/application.ex +++ b/lib/livebook/application.ex @@ -57,6 +57,7 @@ defmodule Livebook.Application do {:ok, _} = result -> Livebook.Migration.migrate() load_lb_env_vars() + create_offline_hub() clear_env_vars() display_startup_info() Livebook.Hubs.connect_hubs() @@ -189,7 +190,7 @@ defmodule Livebook.Application do end end - defp load_lb_env_vars do + defp load_lb_env_vars() do secrets = for {"LB_" <> name = var, value} <- System.get_env() do System.delete_env(var) @@ -204,6 +205,22 @@ defmodule Livebook.Application do Livebook.Secrets.set_startup_secrets(secrets) end + def create_offline_hub() do + name = System.get_env("LIVEBOOK_TEAMS_NAME") + teams_key = System.get_env("LIVEBOOK_TEAMS_KEY") + public_key = System.get_env("LIVEBOOK_TEAMS_OFFLINE_KEY") + + if name && teams_key && public_key do + Livebook.Hubs.set_offline_hub(%Livebook.Hubs.Team{ + id: "team-#{name}", + hub_name: name, + hub_emoji: "💡", + teams_key: teams_key, + org_public_key: public_key + }) + end + end + defp config_env_var?("LIVEBOOK_" <> _), do: true defp config_env_var?("RELEASE_" <> _), do: true defp config_env_var?("MIX_ENV"), do: true diff --git a/lib/livebook/hubs.ex b/lib/livebook/hubs.ex index 051ab5905..8066e64f7 100644 --- a/lib/livebook/hubs.ex +++ b/lib/livebook/hubs.ex @@ -272,4 +272,25 @@ defmodule Livebook.Hubs do def capability?(hub, capabilities) do capabilities -- Provider.capabilities(hub) == [] end + + @offline_hub_key :livebook_offline_hub + + @doc """ + Get the offline hub. + """ + @spec get_offline_hub(String.t()) :: Provider.t() | nil + def get_offline_hub(id) do + case :persistent_term.get(@offline_hub_key, nil) do + %{id: ^id} = hub -> hub + _ -> nil + end + end + + @doc """ + Sets a offline hub that will be kept only in memory. + """ + @spec set_offline_hub(Provider.t()) :: :ok + def set_offline_hub(hub) do + :persistent_term.put(@offline_hub_key, hub) + end end diff --git a/lib/livebook/hubs/provider.ex b/lib/livebook/hubs/provider.ex index ac15d5622..c5f1b58ee 100644 --- a/lib/livebook/hubs/provider.ex +++ b/lib/livebook/hubs/provider.ex @@ -100,7 +100,8 @@ defprotocol Livebook.Hubs.Provider do See `t:notebook_stamp/0` for more details. """ - @spec notebook_stamp(t(), iodata(), map()) :: {:ok, notebook_stamp()} | :skip | :error + @spec notebook_stamp(t(), iodata(), map()) :: + {:ok, notebook_stamp()} | :skip | {:error, String.t()} def notebook_stamp(hub, notebook_source, metadata) @doc """ diff --git a/lib/livebook/hubs/team.ex b/lib/livebook/hubs/team.ex index 5f32c725c..8fb10feb8 100644 --- a/lib/livebook/hubs/team.ex +++ b/lib/livebook/hubs/team.ex @@ -67,18 +67,7 @@ defimpl Livebook.Hubs.Provider, for: Livebook.Hubs.Team do alias Livebook.Teams def load(team, fields) do - %{ - team - | id: fields.id, - session_token: fields.session_token, - teams_key: fields.teams_key, - org_public_key: fields.org_public_key, - org_id: fields.org_id, - user_id: fields.user_id, - org_key_id: fields.org_key_id, - hub_name: fields.hub_name, - hub_emoji: fields.hub_emoji - } + struct(team, fields) end def to_metadata(team) do diff --git a/lib/livebook/live_markdown/import.ex b/lib/livebook/live_markdown/import.ex index bda72b4cf..d0cf23d44 100644 --- a/lib/livebook/live_markdown/import.ex +++ b/lib/livebook/live_markdown/import.ex @@ -1,4 +1,5 @@ defmodule Livebook.LiveMarkdown.Import do + alias Livebook.Hubs alias Livebook.Notebook alias Livebook.LiveMarkdown.MarkdownHelpers @@ -9,9 +10,11 @@ defmodule Livebook.LiveMarkdown.Import do {ast, rewrite_messages} = rewrite_ast(ast) elements = group_elements(ast) {stamp_data, elements} = take_stamp_data(elements) - {notebook, build_messages} = build_notebook(elements) + {notebook, stamp_hub_id, build_messages} = build_notebook(elements) {notebook, postprocess_messages} = postprocess_notebook(notebook) - {notebook, metadata_messages} = postprocess_stamp(notebook, markdown, stamp_data) + + {notebook, metadata_messages} = + postprocess_stamp(notebook, markdown, stamp_data, stamp_hub_id) messages = earmark_messages ++ @@ -326,7 +329,7 @@ defmodule Livebook.LiveMarkdown.Import do ] end - {attrs, metadata_messages} = notebook_metadata_to_attrs(metadata) + {attrs, stamp_hub_id, metadata_messages} = notebook_metadata_to_attrs(metadata) messages = messages ++ metadata_messages # We identify a single leading cell as the setup cell, in any @@ -349,7 +352,7 @@ defmodule Livebook.LiveMarkdown.Import do |> maybe_put_setup_cell(setup_cell) |> Map.merge(attrs) - {notebook, messages} + {notebook, stamp_hub_id, messages} end defp maybe_put_name(notebook, nil), do: notebook @@ -377,36 +380,36 @@ defmodule Livebook.LiveMarkdown.Import do defp grab_leading_comments(elems), do: {[], elems} defp notebook_metadata_to_attrs(metadata) do - Enum.reduce(metadata, {%{}, []}, fn - {"persist_outputs", persist_outputs}, {attrs, messages} -> - {Map.put(attrs, :persist_outputs, persist_outputs), messages} + Enum.reduce(metadata, {%{}, Livebook.Hubs.Personal.id(), []}, fn + {"persist_outputs", persist_outputs}, {attrs, id, messages} -> + {Map.put(attrs, :persist_outputs, persist_outputs), id, messages} - {"autosave_interval_s", autosave_interval_s}, {attrs, messages} -> - {Map.put(attrs, :autosave_interval_s, autosave_interval_s), messages} + {"autosave_interval_s", autosave_interval_s}, {attrs, id, messages} -> + {Map.put(attrs, :autosave_interval_s, autosave_interval_s), id, messages} - {"default_language", default_language}, {attrs, messages} + {"default_language", default_language}, {attrs, id, messages} when default_language in ["elixir", "erlang"] -> default_language = String.to_atom(default_language) - {Map.put(attrs, :default_language, default_language), messages} + {Map.put(attrs, :default_language, default_language), id, messages} - {"hub_id", hub_id}, {attrs, messages} -> - if Livebook.Hubs.hub_exists?(hub_id) do - {Map.put(attrs, :hub_id, hub_id), messages} - else - {attrs, messages ++ ["ignoring notebook Hub with unknown id"]} + {"hub_id", hub_id}, {attrs, id, messages} -> + cond do + Hubs.hub_exists?(hub_id) -> {Map.put(attrs, :hub_id, hub_id), hub_id, messages} + Hubs.get_offline_hub(hub_id) -> {attrs, hub_id, messages} + true -> {attrs, id, messages ++ ["ignoring notebook Hub with unknown id"]} end - {"app_settings", app_settings_metadata}, {attrs, messages} -> + {"app_settings", app_settings_metadata}, {attrs, id, messages} -> app_settings = Map.merge( Notebook.AppSettings.new(), app_settings_metadata_to_attrs(app_settings_metadata) ) - {Map.put(attrs, :app_settings, app_settings), messages} + {Map.put(attrs, :app_settings, app_settings), id, messages} - _entry, {attrs, messages} -> - {attrs, messages} + _entry, {attrs, id, messages} -> + {attrs, id, messages} end) end @@ -521,10 +524,10 @@ defmodule Livebook.LiveMarkdown.Import do defp take_stamp_data([{:stamp, data} | elements]), do: {data, elements} defp take_stamp_data(elements), do: {nil, elements} - defp postprocess_stamp(notebook, _notebook_source, nil), do: {notebook, []} + defp postprocess_stamp(notebook, _notebook_source, nil, _), do: {notebook, []} - defp postprocess_stamp(notebook, notebook_source, stamp_data) do - hub = Livebook.Hubs.fetch_hub!(notebook.hub_id) + defp postprocess_stamp(notebook, notebook_source, stamp_data, stamp_hub_id) do + hub = Hubs.get_offline_hub(stamp_hub_id) || Hubs.fetch_hub!(stamp_hub_id) with %{"offset" => offset, "stamp" => stamp} <- stamp_data, {:ok, notebook_source} <- safe_binary_slice(notebook_source, 0, offset), diff --git a/lib/livebook/secrets/secret.ex b/lib/livebook/secrets/secret.ex index 9fd526341..3c1275b41 100644 --- a/lib/livebook/secrets/secret.ex +++ b/lib/livebook/secrets/secret.ex @@ -4,8 +4,8 @@ defmodule Livebook.Secrets.Secret do import Ecto.Changeset @type t :: %__MODULE__{ - name: String.t() | nil, - value: String.t() | nil, + name: String.t(), + value: String.t(), hub_id: String.t() | nil } diff --git a/lib/livebook/session/data.ex b/lib/livebook/session/data.ex index 0205af12b..83079a60c 100644 --- a/lib/livebook/session/data.ex +++ b/lib/livebook/session/data.ex @@ -884,8 +884,6 @@ defmodule Livebook.Session.Data do |> update_notebook_hub_secret_names() |> set_dirty() |> wrap_ok() - else - _ -> :error end end diff --git a/lib/livebook_web/live/hub/edit/team_component.ex b/lib/livebook_web/live/hub/edit/team_component.ex index 9487e1334..d5f12c415 100644 --- a/lib/livebook_web/live/hub/edit/team_component.ex +++ b/lib/livebook_web/live/hub/edit/team_component.ex @@ -47,6 +47,7 @@ defmodule LivebookWeb.Hub.Edit.TeamComponent do <.remix_icon icon="key-2-fill" class="text-xl sm:hidden" />