lib/pardall_markdown/repository_providers/git.ex

defmodule PardallMarkdown.RepositoryProviders.Git do
  @moduledoc """
  This implementation is based on the implementation found in:
  https://github.com/meddle0x53/blogit/blob/master/lib/blogit/settings.ex
  Many thanks to @meddle0x53 for this!
  """
  require Logger

  @behaviour PardallMarkdown.RepositoryProvider

  @repository_url Application.get_env(:pardall_markdown, PardallMarkdown.Content)[
    :remote_repository_url
  ]
  @local_path PardallMarkdown.Content.Utils.remote_repository_local_path()

  # Callbacks

  @impl true
  def repository do
    repo = git_repository()

    case Git.pull(repo) do
      {:ok, msg} ->
        Logger.info("Pulling from git repository #{msg}")

      # Crash in case of unstaged changes

      {_, %Git.Error{
        args: [], code: 128, command: "pull",
        message: "error: cannot pull with rebase: You have unstaged changes.\nerror: please commit or stash them.\n" = message
      }} ->
        raise message

      {_, error} ->
        Logger.error("Error while pulling from git repository #{inspect(error)}")
    end

    repo
  end

  @impl true
  def fetch(repo) do
    Logger.info("Fetching data from #{@repository_url}")

    case Git.fetch(repo) do
      {:error, _} ->
        {:no_updates}

      {:ok, ""} ->
        {:no_updates}

      {:ok, _} ->
        updates =
          repo
          |> Git.diff!(["--name-only", "HEAD", "origin/master"])
          |> String.split("\n", trim: true)
          |> Enum.map(&String.trim/1)

        Logger.info("There are new updates, pulling them.")
        Git.pull!(repo)

        {:updates, updates}
    end
  end

  @impl true
  def local_path, do: @local_path

  # Private

  defp git_repository do
    if repository_exists?() do
      Git.new(@local_path)
    else
      case Git.clone([@repository_url, @local_path]) do
        {:ok, repo} ->
          Logger.info("Cloning repository #{@repository_url}, into #{@local_path}")
          repo
        {:error, %Git.Error{}} ->
          Git.new(@local_path)
      end
    end
  end

  defp repository_exists?, do: File.exists?(Path.join(@local_path, ".git"))
end