lib/openagents/scv/workspace.ex

main at 58e6347eeb72 · 1 KB

defmodule OpenAgents.SCV.Workspace do
  @moduledoc "Creates and verifies one disposable exact-revision SCV workspace."

  alias OpenAgents.Forge.Repos
  alias OpenAgents.Repositories.Repository

  @spec prepare(Repository.t(), String.t(), Ecto.UUID.t()) ::
          {:ok, Path.t()} | {:error, atom()}
  def prepare(%Repository{} = repository, revision, run_id) do
    root = workspace_root()
    path = Path.join(root, run_id)
    source = Repos.bare_path(repository.storage_key)

    with true <- Repos.valid_storage_key?(repository.storage_key),
         true <- Regex.match?(~r/\A[0-9a-f]{40}\z/, revision),
         true <- File.dir?(source),
         :ok <- File.mkdir_p(root),
         :ok <- File.chmod(root, 0o700),
         false <- File.exists?(path),
         {_, 0} <-
           git([
             "-c",
             "core.hooksPath=/dev/null",
             "clone",
             "--no-local",
             "--no-checkout",
             "--",
             source,
             path
           ]),
         {_, 0} <- git(["-C", path, "checkout", "--detach", revision]),
         {head, 0} <- git(["-C", path, "rev-parse", "HEAD"]),
         ^revision <- String.trim(head),
         {_, 0} <- git(["-C", path, "diff", "--quiet"]),
         {_, 0} <- git(["-C", path, "diff", "--cached", "--quiet"]) do
      {:ok, path}
    else
      _error ->
        File.rm_rf(path)
        {:error, :workspace_preparation_failed}
    end
  end

  @spec destroy(Path.t()) :: :ok
  def destroy(path) when is_binary(path) do
    root = workspace_root()
    expanded = Path.expand(path)

    if Path.dirname(expanded) == root do
      File.rm_rf(expanded)
      :ok
    else
      :ok
    end
  end

  defp git(arguments), do: System.cmd("git", arguments, stderr_to_stdout: true)

  defp workspace_root do
    :openagents
    |> Application.fetch_env!(:scv_codex)
    |> Keyword.get(:temporary_root, System.tmp_dir!())
    |> Path.expand()
    |> Path.join("openagents-scv-workspaces")
  end
end