Packages

fnord

0.9.28

AI code archaeology

Current section

Files

Jump to
fnord lib resolve_project.ex
Raw

lib/resolve_project.ex

# This module determines the current project based on the current working directory
# or falls back to git worktree and repository roots to ensure the most specific project
# context is identified.
defmodule ResolveProject do
@moduledoc """
Project resolution from the current working directory.
Behavior:
- If inside a git worktree, `resolve/1` will map the cwd to the repository root.
- Select the configured project whose root contains that directory, choosing the one with the deepest (longest) root path.
Returns `{:ok, project_name}` or `{:error, :not_in_project}`.
"""
@type project_name :: binary
# Find the configured project whose root most specifically contains the provided directory.
@spec resolve(cwd :: binary | nil) :: {:ok, project_name} | {:error, :not_in_project}
def resolve(cwd \\ nil) do
projects =
Settings.new()
|> Settings.get_projects()
|> Enum.flat_map(fn
{name, %{"root" => nil}} ->
UI.warn("Project '#{name}' missing 'root' configuration, skipping.")
[]
{name, %{"root" => root}} ->
[{Path.absname(root), name}]
{name, _project_config} ->
UI.warn("Project '#{name}' missing 'root' configuration, skipping.")
[]
end)
# If we are inside a git worktree, prefer resolving at the primary repo root
# so that project selection reflects the clone from which the worktree was created.
base_dir =
case cwd do
nil -> File.cwd!()
dir -> dir
end
repo_root_in_ctx =
case cwd do
nil ->
case System.cmd("git", ["rev-parse", "--is-inside-work-tree"],
cd: base_dir,
stderr_to_stdout: true
) do
{"true\n", 0} ->
case System.cmd("git", ["rev-parse", "--git-common-dir"],
cd: base_dir,
stderr_to_stdout: true
) do
{out, 0} ->
out
|> String.trim()
|> Path.dirname()
_ ->
nil
end
_ ->
nil
end
_ ->
nil
end
cwd_base = repo_root_in_ctx || base_dir
cwd_abs = Path.absname(cwd_base)
# Filter to projects whose root contains the current working directory.
matching_projects =
projects
|> Enum.filter(fn {root_abs, _name} -> Util.path_within_root?(cwd_abs, root_abs) end)
# Decide outcome based on whether any matching projects were found.
case matching_projects do
# No matching project roots found, return error.
[] ->
{:error, :not_in_project}
# Select the project with the deepest root path as the most specific match.
_ ->
{_, project_name} =
Enum.max_by(matching_projects, fn {root_abs, _} -> String.length(root_abs) end)
{:ok, project_name}
end
end
end