Packages
livebook
0.19.7
0.19.8
0.19.7
0.19.6
0.19.5
0.19.4
0.19.3
0.19.2
0.19.1
0.19.0
0.18.6
0.18.5
0.18.4
0.18.3
0.18.2
0.18.1
0.18.0
0.17.3
0.17.2
0.17.1
0.17.0
0.16.4
0.16.3
0.16.2
0.16.1
0.16.0
0.15.5
0.15.4
0.15.3
0.15.2
0.15.1
0.15.0
0.14.7
0.14.6
0.14.5
0.14.4
0.14.3
0.14.2
0.14.1
0.14.0
0.14.0-rc.1
0.14.0-rc.0
0.13.3
0.13.2
0.13.1
0.13.0
0.12.1
0.12.0
0.11.4
0.11.3
0.11.2
0.11.1
0.11.0
0.10.0
0.9.3
0.9.2
0.9.1
0.9.0
0.8.2
0.8.1
0.8.0
0.7.2
0.7.1
0.7.0
0.6.3
0.6.2
0.6.1
0.6.0
0.5.2
0.5.1
0.5.0
0.4.1
0.4.0
0.3.2
0.3.1
0.3.0
0.2.3
0.2.2
0.2.1
0.2.0
0.1.2
0.1.1
0.1.0
Automate code & data workflows with interactive notebooks
Current section
Files
Jump to
Current section
Files
lib/livebook/file_system/git.ex
defmodule Livebook.FileSystem.Git do
use Ecto.Schema
import Ecto.Changeset
alias Livebook.FileSystem
# File system backed by an Git repository.
@type t :: %__MODULE__{
id: String.t(),
repo_url: String.t(),
branch: String.t(),
key: String.t(),
external_id: String.t() | nil,
hub_id: String.t()
}
embedded_schema do
field :repo_url, :string
field :branch, :string
field :key, :string, redact: true
field :external_id, :string
field :hub_id, :string
end
@doc """
Returns an `%Ecto.Changeset{}` for tracking file system changes.
"""
@spec change_file_system(t(), map()) :: Ecto.Changeset.t()
def change_file_system(git, attrs \\ %{}) do
changeset(git, attrs)
end
defp changeset(git, attrs) do
git
|> cast(attrs, [:repo_url, :branch, :key, :external_id, :hub_id])
|> validate_format(:repo_url, ~r/^git@[\w\.\-]+:(?:v\d+\/)?[\w\.\-\/]+\.git$/,
message: "must be a valid repo URL"
)
|> validate_required([:repo_url, :branch, :key, :hub_id])
|> put_id()
end
defp put_id(changeset) do
hub_id = get_field(changeset, :hub_id)
repo_url = get_field(changeset, :repo_url)
if get_field(changeset, :id) do
changeset
else
put_change(changeset, :id, FileSystem.Utils.id("git", hub_id, repo_url))
end
end
@doc false
def git_dir(%__MODULE__{id: id}), do: Path.join(Livebook.Config.tmp_path(), id)
@doc false
def key_path(%__MODULE__{id: id}), do: Path.join(Livebook.Config.tmp_path(), "#{id}_key")
end
defimpl Livebook.FileSystem, for: Livebook.FileSystem.Git do
alias Livebook.FileSystem
alias Livebook.FileSystem.Git
def type(_file_system) do
:global
end
def default_path(_file_system) do
"/"
end
def list(file_system, path, _recursive) do
with {:ok, []} <- Git.Client.list_files(file_system, path) do
FileSystem.Utils.posix_error(:enoent)
end
end
def read(file_system, path) do
Git.Client.read_file(file_system, path)
end
def write(_file_system, _path, _content), do: raise("not implemented")
def access(_file_system, _path) do
{:ok, :read}
end
def create_dir(_file_system, _path), do: raise("not implemented")
def remove(_file_system, _path), do: raise("not implemented")
def copy(_file_system, _source_path, _destination_path), do: raise("not implemented")
def rename(_file_system, _source_path, _destination_path), do: raise("not implemented")
def etag_for(file_system, path) do
FileSystem.Utils.assert_regular_path!(path)
Git.Client.etag(file_system, path)
end
def exists?(file_system, path) do
Git.Client.exists?(file_system, path)
end
def resolve_path(_file_system, dir_path, subject) do
FileSystem.Utils.resolve_unix_like_path(dir_path, subject)
end
def write_stream_init(_file_system, _path, _opts), do: raise("not implemented")
def write_stream_chunk(_file_system, _state, _chunk), do: raise("not implemented")
def write_stream_finish(_file_system, _state), do: raise("not implemented")
def write_stream_halt(_file_system, _state), do: raise("not implemented")
def read_stream_into(file_system, path, collectable) do
try do
Git.Client.stream_file(file_system, path, collectable)
rescue
error in File.Error -> FileSystem.Utils.posix_error(error.reason)
end
end
def load(file_system, %{"hub_id" => _} = fields) do
load(file_system, %{
id: fields["id"],
repo_url: fields["repo_url"],
branch: fields["branch"],
key: fields["key"],
external_id: fields["external_id"],
hub_id: fields["hub_id"]
})
end
def load(file_system, fields) do
%{
file_system
| id: fields.id,
repo_url: fields.repo_url,
branch: fields.branch,
key: fields.key,
external_id: fields.external_id,
hub_id: fields.hub_id
}
end
def dump(file_system) do
file_system
|> Map.from_struct()
|> Map.take([:id, :repo_url, :branch, :key, :external_id, :hub_id])
end
def external_metadata(file_system) do
%{name: file_system.repo_url, error_field: "repo_url"}
end
def mount(file_system) do
if mounted?(file_system) do
FileSystem.Git.Client.fetch(file_system)
else
FileSystem.Git.Client.init(file_system)
end
end
def unmount(file_system) do
path = FileSystem.Git.git_dir(file_system)
key_path = FileSystem.Git.key_path(file_system)
if File.exists?(key_path) do
File.rm!(key_path)
end
case File.rm_rf(path) do
{:ok, _} -> :ok
{:error, reason, _file} -> FileSystem.Utils.posix_error(reason)
end
end
defp mounted?(file_system) do
file_system
|> FileSystem.Git.git_dir()
|> File.exists?()
end
end