mirror of
				https://github.com/livebook-dev/livebook.git
				synced 2025-11-01 00:06:04 +08:00 
			
		
		
		
	
		
			
				
	
	
		
			129 lines
		
	
	
	
		
			3.4 KiB
		
	
	
	
		
			Elixir
		
	
	
	
	
	
			
		
		
	
	
			129 lines
		
	
	
	
		
			3.4 KiB
		
	
	
	
		
			Elixir
		
	
	
	
	
	
| defmodule LivebookCLI do
 | |
|   def usage() do
 | |
|     """
 | |
|     Usage: livebook [command] [options]
 | |
| 
 | |
|     Available commands:
 | |
| 
 | |
|       livebook server    Starts the Livebook web application
 | |
| 
 | |
|     The --help and --version options can be given instead of a command for usage and versioning information.
 | |
|     """
 | |
|   end
 | |
| 
 | |
|   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
 | |
| 
 | |
|     call(args)
 | |
|   end
 | |
| 
 | |
|   defp unix?(), do: match?({:unix, _}, :os.type())
 | |
| 
 | |
|   defp call([arg]) when arg in ["--help", "-h"], do: display_help()
 | |
|   defp call([arg]) when arg in ["--version", "-v"], do: display_version()
 | |
| 
 | |
|   defp call([task_name | args]) do
 | |
|     case find_task(task_name) do
 | |
|       nil ->
 | |
|         IO.ANSI.format([:red, "Unknown command #{task_name}\n"]) |> IO.puts()
 | |
|         IO.write(usage())
 | |
| 
 | |
|       task ->
 | |
|         call_task(task, args)
 | |
|     end
 | |
|   end
 | |
| 
 | |
|   defp call(_args), do: IO.write(usage())
 | |
| 
 | |
|   defp find_task("server"), do: LivebookCLI.Server
 | |
|   defp find_task(_), do: nil
 | |
| 
 | |
|   defp call_task(task, [arg]) when arg in ["--help", "-h"] do
 | |
|     IO.write(task.usage())
 | |
|   end
 | |
| 
 | |
|   defp call_task(task, args) do
 | |
|     try do
 | |
|       task.call(args)
 | |
|     rescue
 | |
|       error in OptionParser.ParseError ->
 | |
|         IO.ANSI.format([
 | |
|           :red,
 | |
|           Exception.message(error),
 | |
|           "\n\nFor more information try --help"
 | |
|         ])
 | |
|         |> IO.puts()
 | |
| 
 | |
|       error ->
 | |
|         IO.ANSI.format([:red, Exception.format(:error, error, __STACKTRACE__), "\n"]) |> IO.puts()
 | |
|     end
 | |
|   end
 | |
| 
 | |
|   defp display_help() do
 | |
|     IO.puts("Livebook is an interactive notebook system for Elixir\n")
 | |
|     IO.write(usage())
 | |
|   end
 | |
| 
 | |
|   defp display_version() do
 | |
|     IO.puts(:erlang.system_info(:system_version))
 | |
|     IO.puts("Elixir " <> System.build_info()[:build])
 | |
| 
 | |
|     version = Livebook.Config.app_version()
 | |
|     IO.puts("\nLivebook #{version}")
 | |
|   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
 | |
| 
 | |
|       case :zip.extract(archive, cwd: String.to_charlist(archive_dir), file_filter: file_filter) do
 | |
|         {:ok, _} ->
 | |
|           :ok
 | |
| 
 | |
|         {:error, error} ->
 | |
|           print_error_and_exit(
 | |
|             "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
 | |
| 
 | |
|   @spec print_error_and_exit(String.t()) :: no_return()
 | |
|   defp print_error_and_exit(message) do
 | |
|     IO.ANSI.format([:red, message]) |> IO.puts()
 | |
|     System.halt(1)
 | |
|   end
 | |
| end
 |