mirror of
https://github.com/livebook-dev/livebook.git
synced 2025-10-04 18:54:33 +08:00
Pass notebook files when deploying apps (#2045)
This commit is contained in:
parent
f4365e44d2
commit
d3f7cfa665
5 changed files with 93 additions and 13 deletions
|
@ -58,13 +58,17 @@ defmodule Livebook.App do
|
|||
|
||||
* `:warnings` - a list of warnings to show for the initial deployment
|
||||
|
||||
* `:files_source` - a location to fetch notebook files from, see
|
||||
`Livebook.Session.start_link/1` for more details
|
||||
|
||||
"""
|
||||
@spec start_link(keyword()) :: {:ok, pid} | {:error, any()}
|
||||
def start_link(opts) do
|
||||
notebook = Keyword.fetch!(opts, :notebook)
|
||||
warnings = Keyword.get(opts, :warnings, [])
|
||||
files_source = Keyword.get(opts, :files_source)
|
||||
|
||||
GenServer.start_link(__MODULE__, {notebook, warnings})
|
||||
GenServer.start_link(__MODULE__, {notebook, warnings, files_source})
|
||||
end
|
||||
|
||||
@doc """
|
||||
|
@ -116,7 +120,8 @@ defmodule Livebook.App do
|
|||
@spec deploy(pid(), Livebook.Notebook.t(), keyword()) :: :ok
|
||||
def deploy(pid, notebook, opts \\ []) do
|
||||
warnings = Keyword.get(opts, :warnings, [])
|
||||
GenServer.cast(pid, {:deploy, notebook, warnings})
|
||||
files_source = Keyword.get(opts, :files_source)
|
||||
GenServer.cast(pid, {:deploy, notebook, warnings, files_source})
|
||||
end
|
||||
|
||||
@doc """
|
||||
|
@ -150,11 +155,12 @@ defmodule Livebook.App do
|
|||
end
|
||||
|
||||
@impl true
|
||||
def init({notebook, warnings}) do
|
||||
def init({notebook, warnings, files_source}) do
|
||||
{:ok,
|
||||
%{
|
||||
version: 1,
|
||||
notebook: notebook,
|
||||
files_source: files_source,
|
||||
warnings: warnings,
|
||||
sessions: [],
|
||||
users: %{}
|
||||
|
@ -191,11 +197,17 @@ defmodule Livebook.App do
|
|||
end
|
||||
|
||||
@impl true
|
||||
def handle_cast({:deploy, notebook, warnings}, state) do
|
||||
def handle_cast({:deploy, notebook, warnings, files_source}, state) do
|
||||
true = notebook.app_settings.slug == state.notebook.app_settings.slug
|
||||
|
||||
{:noreply,
|
||||
%{state | notebook: notebook, version: state.version + 1, warnings: warnings}
|
||||
%{
|
||||
state
|
||||
| notebook: notebook,
|
||||
version: state.version + 1,
|
||||
warnings: warnings,
|
||||
files_source: files_source
|
||||
}
|
||||
|> start_eagerly()
|
||||
|> shutdown_old_versions()
|
||||
|> notify_update()}
|
||||
|
@ -265,6 +277,7 @@ defmodule Livebook.App do
|
|||
|
||||
opts = [
|
||||
notebook: state.notebook,
|
||||
files_source: state.files_source,
|
||||
mode: :app,
|
||||
app_pid: self(),
|
||||
auto_shutdown_ms: state.notebook.app_settings.auto_shutdown_ms,
|
||||
|
|
|
@ -21,10 +21,13 @@ defmodule Livebook.Apps do
|
|||
|
||||
* `:warnings` - a list of warnings to show for the new deployment
|
||||
|
||||
* `:files_source` - a location to fetch notebook files from, see
|
||||
`Livebook.Session.start_link/1` for more details
|
||||
|
||||
"""
|
||||
@spec deploy(Livebook.Notebook.t(), keyword()) :: {:ok, pid()} | {:error, term()}
|
||||
def deploy(notebook, opts \\ []) do
|
||||
opts = Keyword.validate!(opts, warnings: [])
|
||||
opts = Keyword.validate!(opts, warnings: [], files_source: nil)
|
||||
|
||||
slug = notebook.app_settings.slug
|
||||
name = name(slug)
|
||||
|
@ -34,25 +37,29 @@ defmodule Livebook.Apps do
|
|||
:global.trans({{:app_registration, name}, node()}, fn ->
|
||||
case :global.whereis_name(name) do
|
||||
:undefined ->
|
||||
with {:ok, pid} <- start_app(notebook, opts[:warnings]) do
|
||||
with {:ok, pid} <- start_app(notebook, opts[:warnings], opts[:files_source]) do
|
||||
:yes = :global.register_name(name, pid)
|
||||
{:ok, pid}
|
||||
end
|
||||
|
||||
pid ->
|
||||
App.deploy(pid, notebook, warnings: opts[:warnings])
|
||||
App.deploy(pid, notebook,
|
||||
warnings: opts[:warnings],
|
||||
files_source: opts[:files_source]
|
||||
)
|
||||
|
||||
{:ok, pid}
|
||||
end
|
||||
end)
|
||||
|
||||
pid ->
|
||||
App.deploy(pid, notebook, warnings: opts[:warnings])
|
||||
App.deploy(pid, notebook, warnings: opts[:warnings], files_source: opts[:files_source])
|
||||
{:ok, pid}
|
||||
end
|
||||
end
|
||||
|
||||
defp start_app(notebook, warnings) do
|
||||
opts = [notebook: notebook, warnings: warnings]
|
||||
defp start_app(notebook, warnings, files_source) do
|
||||
opts = [notebook: notebook, warnings: warnings, files_source: files_source]
|
||||
|
||||
case DynamicSupervisor.start_child(Livebook.AppSupervisor, {App, opts}) do
|
||||
{:ok, pid} ->
|
||||
|
@ -191,7 +198,9 @@ defmodule Livebook.Apps do
|
|||
apps_path_hub_id = Livebook.Config.apps_path_hub_id()
|
||||
|
||||
if apps_path_hub_id == nil or apps_path_hub_id == verified_hub_id do
|
||||
deploy(notebook, warnings: warnings)
|
||||
notebook_file = Livebook.FileSystem.File.local(path)
|
||||
files_dir = Livebook.Session.files_dir_for_notebook(notebook_file)
|
||||
deploy(notebook, warnings: warnings, files_source: {:dir, files_dir})
|
||||
else
|
||||
Logger.warning(
|
||||
"Skipping app deployment at #{path}. The notebook is not verified to come from hub #{apps_path_hub_id}"
|
||||
|
|
|
@ -1288,7 +1288,8 @@ defmodule Livebook.Session do
|
|||
# In the initial state app settings are empty, hence not valid,
|
||||
# so we double-check that we can actually deploy
|
||||
if Notebook.AppSettings.valid?(state.data.notebook.app_settings) do
|
||||
{:ok, pid} = Livebook.Apps.deploy(state.data.notebook)
|
||||
files_dir = files_dir_from_state(state)
|
||||
{:ok, pid} = Livebook.Apps.deploy(state.data.notebook, files_source: {:dir, files_dir})
|
||||
|
||||
if ref = state.deployed_app_monitor_ref do
|
||||
Process.demonitor(ref, [:flush])
|
||||
|
|
|
@ -160,5 +160,34 @@ defmodule Livebook.AppsTest do
|
|||
|
||||
Livebook.App.close(app.pid)
|
||||
end
|
||||
|
||||
@tag :tmp_dir
|
||||
test "deploys notebook with attachment files", %{tmp_dir: tmp_dir} do
|
||||
app_path = Path.join(tmp_dir, "app.livemd")
|
||||
files_path = Path.join(tmp_dir, "files")
|
||||
File.mkdir_p!(files_path)
|
||||
image_path = Path.join(files_path, "image.jpg")
|
||||
File.write!(image_path, "content")
|
||||
|
||||
File.write!(app_path, """
|
||||
<!-- livebook:{"app_settings":{"slug":"app"},"file_entries":[{"name":"image.jpg","type":"attachment"}]} -->
|
||||
|
||||
# App
|
||||
""")
|
||||
|
||||
Livebook.Apps.subscribe()
|
||||
|
||||
Livebook.Apps.deploy_apps_in_dir(tmp_dir)
|
||||
|
||||
assert_receive {:app_created, %{slug: "app"} = app}
|
||||
|
||||
session_id = Livebook.App.get_session_id(app.pid)
|
||||
{:ok, session} = Livebook.Sessions.fetch_session(session_id)
|
||||
|
||||
assert Livebook.FileSystem.File.resolve(session.files_dir, "image.jpg")
|
||||
|> Livebook.FileSystem.File.read() == {:ok, "content"}
|
||||
|
||||
Livebook.App.close(app.pid)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1277,6 +1277,34 @@ defmodule Livebook.SessionTest do
|
|||
|
||||
assert_receive {:operation, {:set_deployed_app_slug, _client_id, nil}}
|
||||
end
|
||||
|
||||
test "deploys notebook with attachment files" do
|
||||
session = start_session()
|
||||
|
||||
%{files_dir: files_dir} = session
|
||||
image_file = FileSystem.File.resolve(files_dir, "image.jpg")
|
||||
:ok = FileSystem.File.write(image_file, "content")
|
||||
Session.add_file_entries(session.pid, [%{type: :attachment, name: "image.jpg"}])
|
||||
|
||||
Session.subscribe(session.id)
|
||||
|
||||
slug = Utils.random_short_id()
|
||||
app_settings = %{Notebook.AppSettings.new() | slug: slug}
|
||||
Session.set_app_settings(session.pid, app_settings)
|
||||
|
||||
Apps.subscribe()
|
||||
|
||||
Session.deploy_app(session.pid)
|
||||
assert_receive {:app_created, %{slug: ^slug, pid: app_pid}}
|
||||
|
||||
session_id = App.get_session_id(app_pid)
|
||||
{:ok, session} = Livebook.Sessions.fetch_session(session_id)
|
||||
|
||||
assert FileSystem.File.resolve(session.files_dir, "image.jpg")
|
||||
|> FileSystem.File.read() == {:ok, "content"}
|
||||
|
||||
App.close(app_pid)
|
||||
end
|
||||
end
|
||||
|
||||
describe "apps" do
|
||||
|
|
Loading…
Add table
Reference in a new issue