mirror of
https://github.com/livebook-dev/livebook.git
synced 2025-09-08 14:04:31 +08:00
Handles partial failure when deploying multiple apps via CLI
This commit is contained in:
parent
e6d615432d
commit
20b8a26256
2 changed files with 130 additions and 36 deletions
|
@ -132,34 +132,45 @@ defmodule LivebookCLI.Deploy do
|
||||||
|
|
||||||
log_info("Deploying notebooks:")
|
log_info("Deploying notebooks:")
|
||||||
|
|
||||||
for path <- config.paths do
|
deploy_results =
|
||||||
log_info(" * Preparing to deploy notebook #{Path.basename(path)}")
|
for path <- config.paths do
|
||||||
files_dir = Livebook.FileSystem.File.local(path)
|
log_info(" * Preparing to deploy notebook #{Path.basename(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} ->
|
{:ok, url} ->
|
||||||
log_info([:green, " * #{app_deployment.title} deployed successfully. (#{url})"])
|
log_info([:green, " * #{app_deployment.title} deployed successfully. (#{url})"])
|
||||||
|
:ok
|
||||||
|
|
||||||
{:error, errors} ->
|
{:error, errors} ->
|
||||||
log_error(" * #{app_deployment.title} failed to deploy.")
|
log_error(" * #{app_deployment.title} failed to deploy.")
|
||||||
errors = normalize_errors(errors)
|
|
||||||
|
|
||||||
raise LivebookCLI.Error, """
|
error_message =
|
||||||
#{format_errors(errors, " * ")}
|
errors
|
||||||
|
|> normalize_errors
|
||||||
|
|> format_errors(" * ")
|
||||||
|
|
||||||
#{Teams.Requests.error_message()}\
|
log_error(error_message)
|
||||||
"""
|
|
||||||
|
|
||||||
{:transport_error, reason} ->
|
:error
|
||||||
log_error(" * #{app_deployment.title} failed to deploy.")
|
|
||||||
raise LivebookCLI.Error, reason
|
{:transport_error, reason} ->
|
||||||
|
log_error(
|
||||||
|
" * #{app_deployment.title} failed to deploy. Transport error: #{reason}"
|
||||||
|
)
|
||||||
|
|
||||||
|
:error
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
|
||||||
|
|
||||||
:ok
|
if Enum.any?(deploy_results, fn result -> result != :ok end) do
|
||||||
|
raise LivebookCLI.Error, "Some app deployments failed."
|
||||||
|
else
|
||||||
|
:ok
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
defp prepare_app_deployment(path, content, files_dir) do
|
defp prepare_app_deployment(path, content, files_dir) do
|
||||||
|
@ -168,13 +179,19 @@ defmodule LivebookCLI.Deploy do
|
||||||
{:ok, app_deployment}
|
{:ok, app_deployment}
|
||||||
|
|
||||||
{:warning, warnings} ->
|
{:warning, warnings} ->
|
||||||
raise LivebookCLI.Error, """
|
error_message = """
|
||||||
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, " * ")}
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
log_error(error_message)
|
||||||
|
|
||||||
|
:error
|
||||||
|
|
||||||
{:error, reason} ->
|
{:error, reason} ->
|
||||||
raise LivebookCLI.Error, "Failed to handle I/O operations: #{reason}"
|
log_error(" * Failed to handle I/O operations: #{reason}")
|
||||||
|
|
||||||
|
:error
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -199,16 +199,19 @@ defmodule LivebookCLI.Integration.DeployTest do
|
||||||
```
|
```
|
||||||
""")
|
""")
|
||||||
|
|
||||||
assert_raise LivebookCLI.Error, ~r/Deployment Group does not exist/s, fn ->
|
output =
|
||||||
ExUnit.CaptureIO.capture_io(fn ->
|
ExUnit.CaptureIO.capture_io(fn ->
|
||||||
deploy(
|
assert_raise LivebookCLI.Error, "Some app deployments failed.", fn ->
|
||||||
key,
|
deploy(
|
||||||
team.teams_key,
|
key,
|
||||||
Utils.random_short_id(),
|
team.teams_key,
|
||||||
app_path
|
Utils.random_short_id(),
|
||||||
)
|
app_path
|
||||||
|
)
|
||||||
|
end
|
||||||
end)
|
end)
|
||||||
end
|
|
||||||
|
assert output =~ ~r/Deployment Group does not exist/
|
||||||
|
|
||||||
refute_receive {:app_deployment_started,
|
refute_receive {:app_deployment_started,
|
||||||
%{
|
%{
|
||||||
|
@ -247,14 +250,88 @@ defmodule LivebookCLI.Integration.DeployTest do
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "handles partial failure when deploying multiple notebooks",
|
||||||
|
%{team: team, node: node, org: org, tmp_dir: tmp_dir} do
|
||||||
|
{deploy_key, _} = TeamsRPC.create_deploy_key(node, org: org)
|
||||||
|
deployment_group = TeamsRPC.create_deployment_group(node, org: org, url: @url)
|
||||||
|
hub_id = team.id
|
||||||
|
|
||||||
|
# Notebook without app settings (deploymeny should fail)
|
||||||
|
invalid_title = "Invalid App"
|
||||||
|
invalid_slug = "invalid-#{Utils.random_short_id()}"
|
||||||
|
invalid_app_path = Path.join(tmp_dir, "#{invalid_slug}.livemd")
|
||||||
|
|
||||||
|
File.write!(invalid_app_path, """
|
||||||
|
<!-- livebook:{"hub_id":"#{hub_id}"} -->
|
||||||
|
|
||||||
|
# #{invalid_title}
|
||||||
|
|
||||||
|
```elixir
|
||||||
|
1 + 1
|
||||||
|
```
|
||||||
|
""")
|
||||||
|
|
||||||
|
# Second notebook should succeed
|
||||||
|
valid_title = "Valid App"
|
||||||
|
valid_slug = "valid-#{Utils.random_short_id()}"
|
||||||
|
valid_app_path = Path.join(tmp_dir, "#{valid_slug}.livemd")
|
||||||
|
|
||||||
|
stamp_notebook(valid_app_path, """
|
||||||
|
<!-- livebook:{"app_settings":{"access_type":"public","slug":"#{valid_slug}"},"hub_id":"#{hub_id}"} -->
|
||||||
|
|
||||||
|
# #{valid_title}
|
||||||
|
|
||||||
|
```elixir
|
||||||
|
1 + 1
|
||||||
|
```
|
||||||
|
""")
|
||||||
|
|
||||||
|
output =
|
||||||
|
ExUnit.CaptureIO.capture_io(fn ->
|
||||||
|
assert_raise(LivebookCLI.Error, "Some app deployments failed.", fn ->
|
||||||
|
deploy(
|
||||||
|
deploy_key,
|
||||||
|
team.teams_key,
|
||||||
|
deployment_group.name,
|
||||||
|
[invalid_app_path, valid_app_path]
|
||||||
|
)
|
||||||
|
end)
|
||||||
|
end)
|
||||||
|
|
||||||
|
# The valid notebook should have been deployed successfully
|
||||||
|
assert output =~
|
||||||
|
"#{valid_title} deployed successfully. (#{@url}/apps/#{valid_slug})"
|
||||||
|
|
||||||
|
deployment_group_id = to_string(deployment_group.id)
|
||||||
|
|
||||||
|
assert_receive {:app_deployment_started,
|
||||||
|
%{
|
||||||
|
title: ^valid_title,
|
||||||
|
slug: ^valid_slug,
|
||||||
|
deployment_group_id: ^deployment_group_id,
|
||||||
|
hub_id: ^hub_id,
|
||||||
|
deployed_by: "CLI"
|
||||||
|
}}
|
||||||
|
|
||||||
|
# And deployment of the invalide notebook shows an error message
|
||||||
|
invalid_app_filename = Path.basename(invalid_app_path)
|
||||||
|
|
||||||
|
assert output =~
|
||||||
|
"Deployment for notebook #{invalid_app_filename} failed"
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
defp deploy(deploy_key, teams_key, deployment_group_name, path) do
|
defp deploy(deploy_key, teams_key, deployment_group_name, path) do
|
||||||
paths =
|
paths =
|
||||||
case Path.wildcard(path) do
|
if is_list(path) do
|
||||||
[] -> [path]
|
path
|
||||||
[path] -> [path]
|
else
|
||||||
paths -> paths
|
case Path.wildcard(path) do
|
||||||
|
[] -> [path]
|
||||||
|
[path] -> [path]
|
||||||
|
paths -> paths
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
LivebookCLI.Deploy.call(
|
LivebookCLI.Deploy.call(
|
||||||
|
|
Loading…
Add table
Reference in a new issue