mirror of
https://github.com/livebook-dev/livebook.git
synced 2025-11-11 14:42:39 +08:00
Apply review comments
This commit is contained in:
parent
f14d436888
commit
01e5c71131
9 changed files with 228 additions and 184 deletions
|
|
@ -24,7 +24,6 @@ config :mime, :types, %{
|
||||||
|
|
||||||
config :livebook,
|
config :livebook,
|
||||||
agent_name: "default",
|
agent_name: "default",
|
||||||
mode: :app,
|
|
||||||
allowed_uri_schemes: [],
|
allowed_uri_schemes: [],
|
||||||
app_service_name: nil,
|
app_service_name: nil,
|
||||||
app_service_url: nil,
|
app_service_url: nil,
|
||||||
|
|
|
||||||
|
|
@ -326,7 +326,7 @@ defmodule Livebook.Application do
|
||||||
Application.put_env(:livebook, :apps_path_hub_id, hub_id)
|
Application.put_env(:livebook, :apps_path_hub_id, hub_id)
|
||||||
fun
|
fun
|
||||||
|
|
||||||
Application.get_env(:livebook, :mode) == :app and (teams_key || auth) ->
|
teams_key || auth ->
|
||||||
Livebook.Config.abort!(
|
Livebook.Config.abort!(
|
||||||
"You must specify both LIVEBOOK_TEAMS_KEY and LIVEBOOK_TEAMS_AUTH."
|
"You must specify both LIVEBOOK_TEAMS_KEY and LIVEBOOK_TEAMS_AUTH."
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -344,7 +344,12 @@ defmodule Livebook.Teams.Requests do
|
||||||
defp transform_response({request, response}) do
|
defp transform_response({request, response}) do
|
||||||
case {request, response} do
|
case {request, response} do
|
||||||
{request, %{status: 404}} when request.private.cli and request.private.deploy ->
|
{request, %{status: 404}} when request.private.cli and request.private.deploy ->
|
||||||
{request, %{response | status: 422, body: %{"errors" => %{"name" => ["does not exist"]}}}}
|
{request,
|
||||||
|
%{
|
||||||
|
response
|
||||||
|
| status: 422,
|
||||||
|
body: %{"errors" => %{"deployment_group" => ["does not exist"]}}
|
||||||
|
}}
|
||||||
|
|
||||||
{request, %{status: 400, body: %{"errors" => %{"detail" => error}}}}
|
{request, %{status: 400, body: %{"errors" => %{"detail" => error}}}}
|
||||||
when request.private.deploy ->
|
when request.private.deploy ->
|
||||||
|
|
|
||||||
|
|
@ -1,32 +1,11 @@
|
||||||
defmodule LivebookCLI do
|
defmodule LivebookCLI do
|
||||||
alias LivebookCLI.{Task, Utils}
|
alias LivebookCLI.{Task, Utils}
|
||||||
|
|
||||||
@switches [
|
@help_args ["--help", "-h"]
|
||||||
help: :boolean,
|
@version_args ["--version", "-v"]
|
||||||
version: :boolean
|
|
||||||
]
|
|
||||||
|
|
||||||
@aliases [
|
|
||||||
h: :help,
|
|
||||||
v: :version
|
|
||||||
]
|
|
||||||
|
|
||||||
def main(args) do
|
|
||||||
Utils.setup()
|
|
||||||
|
|
||||||
case Utils.option_parse(args, strict: @switches, aliases: @aliases) do
|
|
||||||
{parsed, [], _} when parsed.help -> display_help()
|
|
||||||
{parsed, [name], _} when parsed.help -> Task.usage(name)
|
|
||||||
{parsed, _, _} when parsed.version -> display_version()
|
|
||||||
# We want to keep the switches for the task
|
|
||||||
{_, [name | _], _} -> Task.call(name, List.delete(args, name))
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
defp display_help() do
|
|
||||||
Utils.print_text("""
|
|
||||||
Livebook is an interactive notebook system for Elixir
|
|
||||||
|
|
||||||
|
def usage,
|
||||||
|
do: """
|
||||||
Usage: livebook [command] [options]
|
Usage: livebook [command] [options]
|
||||||
|
|
||||||
Available commands:
|
Available commands:
|
||||||
|
|
@ -35,6 +14,34 @@ defmodule LivebookCLI do
|
||||||
livebook deploy Deploys a notebook to Livebook Teams
|
livebook deploy Deploys a notebook to Livebook Teams
|
||||||
|
|
||||||
The --help and --version options can be given instead of a command for usage and versioning information.\
|
The --help and --version options can be given instead of a command for usage and versioning information.\
|
||||||
|
"""
|
||||||
|
|
||||||
|
def main(args) do
|
||||||
|
{:ok, _} = Application.ensure_all_started(:elixir)
|
||||||
|
extract_priv!()
|
||||||
|
|
||||||
|
:ok = Application.load(:livebook)
|
||||||
|
|
||||||
|
if unix?() do
|
||||||
|
Application.put_env(:elixir, :ansi_enabled, true)
|
||||||
|
end
|
||||||
|
|
||||||
|
case args do
|
||||||
|
[arg] when arg in @help_args -> display_help()
|
||||||
|
[arg] when arg in @version_args -> display_version()
|
||||||
|
[name | [arg]] when arg in @help_args -> Task.usage(name)
|
||||||
|
[name | args] -> Task.call(name, List.delete(args, name))
|
||||||
|
_args -> Utils.print_text(usage())
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
defp unix?(), do: match?({:unix, _}, :os.type())
|
||||||
|
|
||||||
|
defp display_help() do
|
||||||
|
Utils.print_text("""
|
||||||
|
Livebook is an interactive notebook system for Elixir
|
||||||
|
|
||||||
|
#{usage()}\
|
||||||
""")
|
""")
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
@ -42,7 +49,45 @@ defmodule LivebookCLI do
|
||||||
Utils.print_text("""
|
Utils.print_text("""
|
||||||
#{:erlang.system_info(:system_version)}
|
#{:erlang.system_info(:system_version)}
|
||||||
Elixir #{System.build_info()[:build]}
|
Elixir #{System.build_info()[:build]}
|
||||||
|
|
||||||
Livebook #{Livebook.Config.app_version()}\
|
Livebook #{Livebook.Config.app_version()}\
|
||||||
""")
|
""")
|
||||||
end
|
end
|
||||||
|
|
||||||
|
import Record
|
||||||
|
defrecord(:zip_file, extract(:zip_file, from_lib: "stdlib/include/zip.hrl"))
|
||||||
|
|
||||||
|
defp extract_priv!() do
|
||||||
|
archive_dir = Path.join(Livebook.Config.tmp_path(), "escript")
|
||||||
|
extracted_path = Path.join(archive_dir, "extracted")
|
||||||
|
in_archive_priv_path = ~c"livebook/priv"
|
||||||
|
|
||||||
|
# In dev we want to extract fresh directory on every boot
|
||||||
|
if Livebook.Config.app_version() =~ "-dev" do
|
||||||
|
File.rm_rf!(archive_dir)
|
||||||
|
end
|
||||||
|
|
||||||
|
# When temporary directory is cleaned by the OS, the directories
|
||||||
|
# may be left in place, so we use a regular file (extracted) to
|
||||||
|
# check if the extracted archive is already available
|
||||||
|
if not File.exists?(extracted_path) do
|
||||||
|
{:ok, sections} = :escript.extract(:escript.script_name(), [])
|
||||||
|
archive = Keyword.fetch!(sections, :archive)
|
||||||
|
|
||||||
|
file_filter = fn zip_file(name: name) ->
|
||||||
|
List.starts_with?(name, in_archive_priv_path)
|
||||||
|
end
|
||||||
|
|
||||||
|
opts = [cwd: String.to_charlist(archive_dir), file_filter: file_filter]
|
||||||
|
|
||||||
|
with {:error, error} <- :zip.extract(archive, opts) do
|
||||||
|
raise "Livebook failed to extract archive files, reason: #{inspect(error)}"
|
||||||
|
end
|
||||||
|
|
||||||
|
File.touch!(extracted_path)
|
||||||
|
end
|
||||||
|
|
||||||
|
priv_dir = Path.join(archive_dir, in_archive_priv_path)
|
||||||
|
Application.put_env(:livebook, :priv_dir, priv_dir, persistent: true)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -40,8 +40,6 @@ defmodule LivebookCLI.Deploy do
|
||||||
|
|
||||||
@impl true
|
@impl true
|
||||||
def call(args) do
|
def call(args) do
|
||||||
Application.put_env(:livebook, :mode, :cli)
|
|
||||||
|
|
||||||
{:ok, _} = Application.ensure_all_started(:livebook)
|
{:ok, _} = Application.ensure_all_started(:livebook)
|
||||||
config = config_from_args(args)
|
config = config_from_args(args)
|
||||||
ensure_config!(config)
|
ensure_config!(config)
|
||||||
|
|
@ -67,35 +65,26 @@ defmodule LivebookCLI.Deploy do
|
||||||
|
|
||||||
errors =
|
errors =
|
||||||
Enum.reduce(config, %{}, fn
|
Enum.reduce(config, %{}, fn
|
||||||
{:session_token, value}, acc when value in ["", nil] ->
|
{key, value}, acc when value in ["", nil] ->
|
||||||
add_error(acc, "Deploy Key", "can't be blank")
|
add_error(acc, normalize_key(key), "can't be blank")
|
||||||
|
|
||||||
{:session_token, value}, acc ->
|
{:session_token, value}, acc ->
|
||||||
if not String.starts_with?(value, @deploy_key_prefix) do
|
if not String.starts_with?(value, @deploy_key_prefix) do
|
||||||
add_error(acc, "Deploy Key", "must be a Livebook Teams Deploy Key")
|
add_error(acc, normalize_key(:session_token), "must be a Livebook Teams Deploy Key")
|
||||||
else
|
else
|
||||||
acc
|
acc
|
||||||
end
|
end
|
||||||
|
|
||||||
{:teams_key, value}, acc when value in ["", nil] ->
|
|
||||||
add_error(acc, "Teams Key", "can't be blank")
|
|
||||||
|
|
||||||
{:teams_key, value}, acc ->
|
{:teams_key, value}, acc ->
|
||||||
if not String.starts_with?(value, @teams_key_prefix) do
|
if not String.starts_with?(value, @teams_key_prefix) do
|
||||||
add_error(acc, "Teams Key", "must be a Livebook Teams Key")
|
add_error(acc, normalize_key(:teams_key), "must be a Livebook Teams Key")
|
||||||
else
|
else
|
||||||
acc
|
acc
|
||||||
end
|
end
|
||||||
|
|
||||||
{:deployment_group, value}, acc when value in ["", nil] ->
|
|
||||||
add_error(acc, "Deployment Group", "can't be blank")
|
|
||||||
|
|
||||||
{:path, value}, acc when value in ["", nil] ->
|
|
||||||
add_error(acc, "Path", "can't be blank")
|
|
||||||
|
|
||||||
{:path, value}, acc ->
|
{:path, value}, acc ->
|
||||||
if not File.exists?(value) do
|
if not File.exists?(value) do
|
||||||
add_error(acc, "Path", "must be a valid path")
|
add_error(acc, normalize_key(:path), "must be a valid path")
|
||||||
else
|
else
|
||||||
acc
|
acc
|
||||||
end
|
end
|
||||||
|
|
@ -110,7 +99,7 @@ defmodule LivebookCLI.Deploy do
|
||||||
raise """
|
raise """
|
||||||
You configuration is invalid, make sure you are using the correct options for this task.
|
You configuration is invalid, make sure you are using the correct options for this task.
|
||||||
|
|
||||||
#{format_errors(errors)}\
|
#{format_errors(errors, " * ")}\
|
||||||
"""
|
"""
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
@ -126,16 +115,32 @@ defmodule LivebookCLI.Deploy do
|
||||||
end
|
end
|
||||||
|
|
||||||
defp deploy_to_teams(team, config) do
|
defp deploy_to_teams(team, config) do
|
||||||
for path <- list_notebooks!(config.path) do
|
notebook_paths = list_notebooks!(config.path)
|
||||||
log_debug("Deploying notebook: #{path}")
|
log_info("Deploying notebooks:")
|
||||||
|
|
||||||
|
for path <- notebook_paths do
|
||||||
|
log_info(" * Preparing to deploy notebook #{Path.basename(path)}")
|
||||||
files_dir = Livebook.FileSystem.File.local(path)
|
files_dir = Livebook.FileSystem.File.local(path)
|
||||||
|
|
||||||
with {:ok, content} <- File.read(path),
|
with {:ok, content} <- File.read(path),
|
||||||
{:ok, app_deployment} <- prepare_app_deployment(path, content, files_dir) do
|
{:ok, app_deployment} <- prepare_app_deployment(path, content, files_dir) do
|
||||||
case Livebook.Teams.deploy_app_from_cli(team, app_deployment, config.deployment_group) do
|
case Livebook.Teams.deploy_app_from_cli(team, app_deployment, config.deployment_group) do
|
||||||
{:ok, url} -> print_deployment(app_deployment, url)
|
{:ok, url} ->
|
||||||
{:error, errors} -> raise format_errors(errors)
|
print_text([:green, " * #{app_deployment.title} deployed successfully. (#{url})"])
|
||||||
{:transport_error, reason} -> raise reason
|
|
||||||
|
{:error, errors} ->
|
||||||
|
print_text([:red, " * #{app_deployment.title} failed to deployed."])
|
||||||
|
errors = normalize_errors(errors)
|
||||||
|
|
||||||
|
raise """
|
||||||
|
#{format_errors(errors, " * ")}
|
||||||
|
|
||||||
|
#{Teams.Requests.error_message()}\
|
||||||
|
"""
|
||||||
|
|
||||||
|
{:transport_error, reason} ->
|
||||||
|
print_text([:red, " * #{app_deployment.title} failed to deployed."])
|
||||||
|
raise reason
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
@ -170,25 +175,6 @@ defmodule LivebookCLI.Deploy do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
defp add_error(errors, key, message) do
|
|
||||||
Map.update(errors, key, [message], &[message | &1])
|
|
||||||
end
|
|
||||||
|
|
||||||
defp format_errors(%{} = errors_map) do
|
|
||||||
errors_map
|
|
||||||
|> Enum.map(fn {key, errors} ->
|
|
||||||
"""
|
|
||||||
* #{key}
|
|
||||||
#{format_list(errors)}\
|
|
||||||
"""
|
|
||||||
end)
|
|
||||||
|> Enum.join("\n")
|
|
||||||
end
|
|
||||||
|
|
||||||
defp format_list(errors) when is_list(errors) do
|
|
||||||
errors |> Enum.map(&" * #{&1}") |> Enum.join("\n")
|
|
||||||
end
|
|
||||||
|
|
||||||
defp prepare_app_deployment(path, content, files_dir) do
|
defp prepare_app_deployment(path, content, files_dir) do
|
||||||
case Livebook.Teams.AppDeployment.new(content, files_dir) do
|
case Livebook.Teams.AppDeployment.new(content, files_dir) do
|
||||||
{:ok, app_deployment} ->
|
{:ok, app_deployment} ->
|
||||||
|
|
@ -197,7 +183,7 @@ defmodule LivebookCLI.Deploy do
|
||||||
{:warning, warnings} ->
|
{:warning, warnings} ->
|
||||||
raise """
|
raise """
|
||||||
Deployment for notebook #{Path.basename(path)} failed because the notebook has some warnings:
|
Deployment for notebook #{Path.basename(path)} failed because the notebook has some warnings:
|
||||||
#{format_list(warnings)}
|
#{format_list(warnings, " * ")}
|
||||||
"""
|
"""
|
||||||
|
|
||||||
{:error, reason} ->
|
{:error, reason} ->
|
||||||
|
|
@ -205,22 +191,31 @@ defmodule LivebookCLI.Deploy do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
defp print_deployment(app_deployment, url) do
|
defp add_error(errors, key, message) do
|
||||||
print_text([
|
Map.update(errors, key, [message], &[message | &1])
|
||||||
:green,
|
end
|
||||||
"App deployment created successfully.\n\n",
|
|
||||||
:magenta,
|
def normalize_errors(%{} = errors) do
|
||||||
:bright,
|
for {key, values} <- errors, into: %{} do
|
||||||
"Slug: ",
|
{normalize_key(key), values}
|
||||||
:reset,
|
end
|
||||||
:white,
|
end
|
||||||
"#{app_deployment.slug} (#{url})\n",
|
|
||||||
:magenta,
|
defp normalize_key(key) when is_atom(key), do: to_string(key) |> normalize_key()
|
||||||
:bright,
|
defp normalize_key("session_token"), do: "Deploy Key"
|
||||||
"Title: ",
|
defp normalize_key("teams_key"), do: "Teams Key"
|
||||||
:reset,
|
defp normalize_key("deployment_group"), do: "Deployment Group"
|
||||||
:white,
|
defp normalize_key("path"), do: "Path"
|
||||||
app_deployment.title
|
|
||||||
])
|
defp format_errors(errors, prefix) do
|
||||||
|
errors
|
||||||
|
|> Enum.map(fn {key, values} ->
|
||||||
|
values |> Enum.map(&"#{prefix}#{key} #{&1}") |> Enum.join("\n")
|
||||||
|
end)
|
||||||
|
|> Enum.join("\n")
|
||||||
|
end
|
||||||
|
|
||||||
|
defp format_list(errors, prefix) do
|
||||||
|
errors |> Enum.map(&"#{prefix}#{&1}") |> Enum.join("\n")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -18,8 +18,6 @@ defmodule LivebookCLI.Task do
|
||||||
def call(name, args) do
|
def call(name, args) do
|
||||||
task = fetch_task!(name)
|
task = fetch_task!(name)
|
||||||
task.call(args)
|
task.call(args)
|
||||||
|
|
||||||
:ok
|
|
||||||
rescue
|
rescue
|
||||||
exception -> log_exception(exception, name, __STACKTRACE__)
|
exception -> log_exception(exception, name, __STACKTRACE__)
|
||||||
end
|
end
|
||||||
|
|
@ -31,14 +29,50 @@ defmodule LivebookCLI.Task do
|
||||||
def usage(name) do
|
def usage(name) do
|
||||||
task = fetch_task!(name)
|
task = fetch_task!(name)
|
||||||
print_text(task.usage())
|
print_text(task.usage())
|
||||||
|
|
||||||
:ok
|
|
||||||
rescue
|
rescue
|
||||||
exception -> log_exception(exception, name, __STACKTRACE__)
|
exception -> log_exception(exception, name, __STACKTRACE__)
|
||||||
end
|
end
|
||||||
|
|
||||||
@spec fetch_task!(String.t()) :: module() | no_return()
|
|
||||||
defp fetch_task!("server"), do: LivebookCLI.Server
|
defp fetch_task!("server"), do: LivebookCLI.Server
|
||||||
defp fetch_task!("deploy"), do: LivebookCLI.Deploy
|
defp fetch_task!("deploy"), do: LivebookCLI.Deploy
|
||||||
defp fetch_task!(name), do: raise("Unknown command #{name}")
|
|
||||||
|
defp fetch_task!(name) do
|
||||||
|
log_error("Unknown command #{name}")
|
||||||
|
print_text(LivebookCLI.usage())
|
||||||
|
|
||||||
|
System.halt(1)
|
||||||
|
end
|
||||||
|
|
||||||
|
defp log_error(message) do
|
||||||
|
[:red, message]
|
||||||
|
|> IO.ANSI.format()
|
||||||
|
|> IO.puts()
|
||||||
|
end
|
||||||
|
|
||||||
|
@spec log_exception(Exception.t(), String.t(), Exception.stacktrace()) :: no_return()
|
||||||
|
defp log_exception(exception, command_name, stacktrace) when is_exception(exception) do
|
||||||
|
[:red, format_exception(exception, command_name, stacktrace)]
|
||||||
|
|> IO.ANSI.format()
|
||||||
|
|> IO.puts()
|
||||||
|
|
||||||
|
System.halt(1)
|
||||||
|
end
|
||||||
|
|
||||||
|
defp format_exception(%OptionParser.ParseError{} = exception, command_name, _) do
|
||||||
|
"""
|
||||||
|
#{Exception.message(exception)}
|
||||||
|
|
||||||
|
For more information try:
|
||||||
|
|
||||||
|
livebook #{command_name} --help
|
||||||
|
"""
|
||||||
|
end
|
||||||
|
|
||||||
|
defp format_exception(%RuntimeError{} = exception, _, _) do
|
||||||
|
Exception.message(exception)
|
||||||
|
end
|
||||||
|
|
||||||
|
defp format_exception(exception, _, stacktrace) do
|
||||||
|
Exception.format(:error, exception, stacktrace)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -1,18 +1,4 @@
|
||||||
defmodule LivebookCLI.Utils do
|
defmodule LivebookCLI.Utils do
|
||||||
def setup do
|
|
||||||
{:ok, _} = Application.ensure_all_started(:elixir)
|
|
||||||
|
|
||||||
extract_priv!()
|
|
||||||
|
|
||||||
:ok = Application.load(:livebook)
|
|
||||||
|
|
||||||
if unix?() do
|
|
||||||
Application.put_env(:elixir, :ansi_enabled, true)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
defp unix?(), do: match?({:unix, _}, :os.type())
|
|
||||||
|
|
||||||
def option_parse(argv, opts \\ []) do
|
def option_parse(argv, opts \\ []) do
|
||||||
{parsed, argv, errors} = OptionParser.parse(argv, opts)
|
{parsed, argv, errors} = OptionParser.parse(argv, opts)
|
||||||
{Enum.into(parsed, %{}), argv, errors}
|
{Enum.into(parsed, %{}), argv, errors}
|
||||||
|
|
@ -37,7 +23,7 @@ defmodule LivebookCLI.Utils do
|
||||||
def log_warning(message) do
|
def log_warning(message) do
|
||||||
[:yellow, message]
|
[:yellow, message]
|
||||||
|> IO.ANSI.format()
|
|> IO.ANSI.format()
|
||||||
|> IO.warn()
|
|> IO.puts()
|
||||||
end
|
end
|
||||||
|
|
||||||
def print_text(message) do
|
def print_text(message) do
|
||||||
|
|
@ -45,68 +31,4 @@ defmodule LivebookCLI.Utils do
|
||||||
|> IO.ANSI.format()
|
|> IO.ANSI.format()
|
||||||
|> IO.puts()
|
|> IO.puts()
|
||||||
end
|
end
|
||||||
|
|
||||||
@spec log_exception(Exception.t(), String.t(), Exception.stacktrace()) :: no_return()
|
|
||||||
def log_exception(exception, command_name, stacktrace) when is_exception(exception) do
|
|
||||||
[:red, format_exception(exception, command_name, stacktrace)]
|
|
||||||
|> IO.ANSI.format()
|
|
||||||
|> IO.puts()
|
|
||||||
|
|
||||||
System.halt(1)
|
|
||||||
end
|
|
||||||
|
|
||||||
defp format_exception(%OptionParser.ParseError{} = exception, command_name, _) do
|
|
||||||
"""
|
|
||||||
#{Exception.message(exception)}
|
|
||||||
|
|
||||||
For more information try:
|
|
||||||
|
|
||||||
livebook #{command_name} --help
|
|
||||||
"""
|
|
||||||
end
|
|
||||||
|
|
||||||
defp format_exception(%RuntimeError{} = exception, _, _) do
|
|
||||||
Exception.message(exception)
|
|
||||||
end
|
|
||||||
|
|
||||||
defp format_exception(exception, _, stacktrace) do
|
|
||||||
Exception.format(:error, exception, stacktrace)
|
|
||||||
end
|
|
||||||
|
|
||||||
import Record
|
|
||||||
defrecord(:zip_file, extract(:zip_file, from_lib: "stdlib/include/zip.hrl"))
|
|
||||||
|
|
||||||
defp extract_priv!() do
|
|
||||||
archive_dir = Path.join(Livebook.Config.tmp_path(), "escript")
|
|
||||||
extracted_path = Path.join(archive_dir, "extracted")
|
|
||||||
in_archive_priv_path = ~c"livebook/priv"
|
|
||||||
|
|
||||||
# In dev we want to extract fresh directory on every boot
|
|
||||||
if Livebook.Config.app_version() =~ "-dev" do
|
|
||||||
File.rm_rf!(archive_dir)
|
|
||||||
end
|
|
||||||
|
|
||||||
# When temporary directory is cleaned by the OS, the directories
|
|
||||||
# may be left in place, so we use a regular file (extracted) to
|
|
||||||
# check if the extracted archive is already available
|
|
||||||
if not File.exists?(extracted_path) do
|
|
||||||
{:ok, sections} = :escript.extract(:escript.script_name(), [])
|
|
||||||
archive = Keyword.fetch!(sections, :archive)
|
|
||||||
|
|
||||||
file_filter = fn zip_file(name: name) ->
|
|
||||||
List.starts_with?(name, in_archive_priv_path)
|
|
||||||
end
|
|
||||||
|
|
||||||
opts = [cwd: String.to_charlist(archive_dir), file_filter: file_filter]
|
|
||||||
|
|
||||||
with {:error, error} <- :zip.extract(archive, opts) do
|
|
||||||
raise "Livebook failed to extract archive files, reason: #{inspect(error)}"
|
|
||||||
end
|
|
||||||
|
|
||||||
File.touch!(extracted_path)
|
|
||||||
end
|
|
||||||
|
|
||||||
priv_dir = Path.join(archive_dir, in_archive_priv_path)
|
|
||||||
Application.put_env(:livebook, :priv_dir, priv_dir, persistent: true)
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -53,9 +53,8 @@ defmodule LivebookCLI.Integration.DeployTest do
|
||||||
]) == :ok
|
]) == :ok
|
||||||
end)
|
end)
|
||||||
|
|
||||||
assert output =~ "App deployment created successfully."
|
assert output =~ "* Preparing to deploy notebook #{slug}.livemd"
|
||||||
assert output =~ "#{slug} (#{@url}/apps/#{slug})"
|
assert output =~ " * #{title} deployed successfully. (#{@url}/apps/#{slug})"
|
||||||
assert output =~ title
|
|
||||||
|
|
||||||
assert_receive {:app_deployment_started,
|
assert_receive {:app_deployment_started,
|
||||||
%{
|
%{
|
||||||
|
|
@ -78,7 +77,7 @@ defmodule LivebookCLI.Integration.DeployTest do
|
||||||
for i <- 1..3 do
|
for i <- 1..3 do
|
||||||
title = "Test App #{i}"
|
title = "Test App #{i}"
|
||||||
slug = "app-#{i}-#{Utils.random_short_id()}"
|
slug = "app-#{i}-#{Utils.random_short_id()}"
|
||||||
app_path = Path.join(tmp_dir, "app_#{i}.livemd")
|
app_path = Path.join(tmp_dir, "#{slug}.livemd")
|
||||||
|
|
||||||
stamp_notebook(app_path, """
|
stamp_notebook(app_path, """
|
||||||
<!-- livebook:{"app_settings":{"access_type":"public","slug":"#{slug}"},"hub_id":"#{hub_id}"} -->
|
<!-- livebook:{"app_settings":{"access_type":"public","slug":"#{slug}"},"hub_id":"#{hub_id}"} -->
|
||||||
|
|
@ -107,9 +106,8 @@ defmodule LivebookCLI.Integration.DeployTest do
|
||||||
end)
|
end)
|
||||||
|
|
||||||
for {slug, title} <- apps do
|
for {slug, title} <- apps do
|
||||||
assert output =~ "App deployment created successfully."
|
assert output =~ "* Preparing to deploy notebook #{slug}.livemd"
|
||||||
assert output =~ "#{slug} (#{@url}/apps/#{slug})"
|
assert output =~ " * #{title} deployed successfully. (#{@url}/apps/#{slug})"
|
||||||
assert output =~ title
|
|
||||||
|
|
||||||
assert_receive {:app_deployment_started,
|
assert_receive {:app_deployment_started,
|
||||||
%{
|
%{
|
||||||
|
|
@ -133,7 +131,7 @@ defmodule LivebookCLI.Integration.DeployTest do
|
||||||
# Test App
|
# Test App
|
||||||
""")
|
""")
|
||||||
|
|
||||||
assert_raise RuntimeError, ~r/Deploy Key.*must be a Livebook Teams Deploy Key/s, fn ->
|
assert_raise RuntimeError, ~r/Deploy Key must be a Livebook Teams Deploy Key/s, fn ->
|
||||||
Deploy.call([
|
Deploy.call([
|
||||||
"--deploy-key",
|
"--deploy-key",
|
||||||
"invalid_key",
|
"invalid_key",
|
||||||
|
|
@ -158,7 +156,7 @@ defmodule LivebookCLI.Integration.DeployTest do
|
||||||
# Test App
|
# Test App
|
||||||
""")
|
""")
|
||||||
|
|
||||||
assert_raise RuntimeError, ~r/Teams Key.*must be a Livebook Teams Key/s, fn ->
|
assert_raise RuntimeError, ~r/Teams Key must be a Livebook Teams Key/s, fn ->
|
||||||
Deploy.call([
|
Deploy.call([
|
||||||
"--deploy-key",
|
"--deploy-key",
|
||||||
key,
|
key,
|
||||||
|
|
@ -183,7 +181,7 @@ defmodule LivebookCLI.Integration.DeployTest do
|
||||||
# Test App
|
# Test App
|
||||||
""")
|
""")
|
||||||
|
|
||||||
assert_raise RuntimeError, ~r/Deployment Group.*can't be blank/s, fn ->
|
assert_raise RuntimeError, ~r/Deployment Group can't be blank/s, fn ->
|
||||||
Deploy.call([
|
Deploy.call([
|
||||||
"--deploy-key",
|
"--deploy-key",
|
||||||
key,
|
key,
|
||||||
|
|
@ -194,11 +192,57 @@ defmodule LivebookCLI.Integration.DeployTest do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "fails with invalid deployment group",
|
||||||
|
%{team: team, node: node, org: org, tmp_dir: tmp_dir} do
|
||||||
|
title = "Test CLI Deploy App"
|
||||||
|
slug = Utils.random_short_id()
|
||||||
|
app_path = Path.join(tmp_dir, "#{slug}.livemd")
|
||||||
|
{key, _} = TeamsRPC.create_deploy_key(node, org: org)
|
||||||
|
deployment_group = TeamsRPC.create_deployment_group(node, org: org, url: @url)
|
||||||
|
hub_id = team.id
|
||||||
|
deployment_group_id = to_string(deployment_group.id)
|
||||||
|
|
||||||
|
stamp_notebook(app_path, """
|
||||||
|
<!-- livebook:{"app_settings":{"access_type":"public","slug":"#{slug}"},"hub_id":"#{hub_id}"} -->
|
||||||
|
|
||||||
|
# #{title}
|
||||||
|
|
||||||
|
## Test Section
|
||||||
|
|
||||||
|
```elixir
|
||||||
|
IO.puts("Hello from CLI deployed app!")
|
||||||
|
```
|
||||||
|
""")
|
||||||
|
|
||||||
|
assert_raise RuntimeError, ~r/Deployment Group does not exist/s, fn ->
|
||||||
|
ExUnit.CaptureIO.capture_io(fn ->
|
||||||
|
Deploy.call([
|
||||||
|
"--deploy-key",
|
||||||
|
key,
|
||||||
|
"--teams-key",
|
||||||
|
team.teams_key,
|
||||||
|
"--deployment-group",
|
||||||
|
Utils.random_short_id(),
|
||||||
|
app_path
|
||||||
|
])
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
|
||||||
|
refute_receive {:app_deployment_started,
|
||||||
|
%{
|
||||||
|
title: ^title,
|
||||||
|
slug: ^slug,
|
||||||
|
deployment_group_id: ^deployment_group_id,
|
||||||
|
hub_id: ^hub_id,
|
||||||
|
deployed_by: "CLI"
|
||||||
|
}}
|
||||||
|
end
|
||||||
|
|
||||||
test "fails with non-existent file", %{team: team, node: node, org: org, tmp_dir: tmp_dir} do
|
test "fails with non-existent file", %{team: team, node: node, org: org, tmp_dir: tmp_dir} do
|
||||||
{key, _} = TeamsRPC.create_deploy_key(node, org: org)
|
{key, _} = TeamsRPC.create_deploy_key(node, org: org)
|
||||||
deployment_group = TeamsRPC.create_deployment_group(node, org: org, url: @url)
|
deployment_group = TeamsRPC.create_deployment_group(node, org: org, url: @url)
|
||||||
|
|
||||||
assert_raise RuntimeError, ~r/Path.*must be a valid path/s, fn ->
|
assert_raise RuntimeError, ~r/Path must be a valid path/s, fn ->
|
||||||
Deploy.call([
|
Deploy.call([
|
||||||
"--deploy-key",
|
"--deploy-key",
|
||||||
key,
|
key,
|
||||||
|
|
|
||||||
|
|
@ -355,7 +355,7 @@ defmodule Livebook.TeamsTest do
|
||||||
} = app_deployment2}
|
} = app_deployment2}
|
||||||
|
|
||||||
assert Teams.deploy_app_from_cli(team, app_deployment, "foo") ==
|
assert Teams.deploy_app_from_cli(team, app_deployment, "foo") ==
|
||||||
{:error, %{"name" => ["does not exist"]}}
|
{:error, %{"deployment_group" => ["does not exist"]}}
|
||||||
|
|
||||||
assert Teams.deploy_app_from_cli(team, %{app_deployment | slug: "@abc"}, name) ==
|
assert Teams.deploy_app_from_cli(team, %{app_deployment | slug: "@abc"}, name) ==
|
||||||
{:error, %{"slug" => ["should only contain alphanumeric characters and dashes"]}}
|
{:error, %{"slug" => ["should only contain alphanumeric characters and dashes"]}}
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue