fix: working on rebuild from filesystem and analysis warnings
This commit is contained in:
@@ -991,12 +991,14 @@ defmodule BDS.AI do
|
||||
end
|
||||
|
||||
defp await_chat_task(task) do
|
||||
ref = task.ref
|
||||
|
||||
receive do
|
||||
{ref, result} when ref == task.ref ->
|
||||
{^ref, result} ->
|
||||
Process.demonitor(task.ref, [:flush])
|
||||
result
|
||||
|
||||
{:DOWN, ref, :process, _pid, reason} when ref == task.ref ->
|
||||
{:DOWN, ^ref, :process, _pid, reason} ->
|
||||
case reason do
|
||||
:normal ->
|
||||
receive do
|
||||
|
||||
@@ -3,6 +3,8 @@ defmodule BDS.Application do
|
||||
|
||||
use Application
|
||||
|
||||
@compiled_env Application.compile_env(:bds, :current_env, Mix.env())
|
||||
|
||||
def desktop_children(env \\ nil)
|
||||
|
||||
def desktop_children(:test) do
|
||||
@@ -41,8 +43,7 @@ defmodule BDS.Application do
|
||||
end
|
||||
|
||||
defp current_env do
|
||||
Application.get_env(:bds, :current_env_override) ||
|
||||
if(Code.ensure_loaded?(Mix), do: Mix.env(), else: :prod)
|
||||
Application.get_env(:bds, :current_env_override) || @compiled_env
|
||||
end
|
||||
|
||||
defp desktop_window_children do
|
||||
|
||||
@@ -286,8 +286,6 @@ defmodule BDS.Desktop.Automation do
|
||||
end
|
||||
end
|
||||
|
||||
defp port_os_pid(nil), do: nil
|
||||
|
||||
defp port_os_pid(port) do
|
||||
case Port.info(port, :os_pid) do
|
||||
{:os_pid, pid} when is_integer(pid) -> pid
|
||||
|
||||
@@ -213,10 +213,8 @@ defmodule BDS.Desktop.MainWindow do
|
||||
|
||||
defp wx_env_undefined? do
|
||||
try do
|
||||
case :wx.get_env() do
|
||||
:undefined -> true
|
||||
_ -> false
|
||||
end
|
||||
_ = :wx.get_env()
|
||||
false
|
||||
rescue
|
||||
ErlangError -> true
|
||||
end
|
||||
|
||||
@@ -117,7 +117,6 @@ defmodule BDS.Git do
|
||||
case run_git(project_dir, ["fetch", "--all", "--prune"], opts) do
|
||||
{:ok, output} -> {:ok, %{updated: true, output: output}}
|
||||
{:error, {:git_failed, message}} -> structured_git_error(project_dir, :fetch, message, opts)
|
||||
other -> other
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -209,7 +208,6 @@ defmodule BDS.Git do
|
||||
case run_git(project_dir, ["remote", "get-url", "origin"], opts) do
|
||||
{:ok, output} -> {:ok, blank_to_nil(output)}
|
||||
{:error, {:git_failed, _message}} -> {:ok, nil}
|
||||
other -> other
|
||||
end
|
||||
end
|
||||
|
||||
@@ -217,7 +215,6 @@ defmodule BDS.Git do
|
||||
case run_git(project_dir, ["lfs", "ls-files"], opts) do
|
||||
{:ok, _output} -> {:ok, true}
|
||||
{:error, {:git_failed, _message}} -> {:ok, false}
|
||||
other -> other
|
||||
end
|
||||
end
|
||||
|
||||
@@ -335,7 +332,6 @@ defmodule BDS.Git do
|
||||
provider =
|
||||
case remote_url(project_dir, opts) do
|
||||
{:ok, remote} -> provider_info(remote)
|
||||
_other -> nil
|
||||
end
|
||||
|
||||
if auth_error?(message) do
|
||||
@@ -370,9 +366,8 @@ defmodule BDS.Git do
|
||||
end
|
||||
|
||||
defp normalize_optional_string({:ok, value}), do: {:ok, blank_to_nil(value)}
|
||||
defp normalize_optional_string(other), do: other
|
||||
defp normalize_optional_string({:error, _reason} = error), do: error
|
||||
|
||||
defp blank_to_nil(nil), do: nil
|
||||
defp blank_to_nil(value) when value in ["", "\n"], do: nil
|
||||
defp blank_to_nil(value), do: String.trim(value)
|
||||
end
|
||||
|
||||
@@ -217,9 +217,6 @@ defmodule BDS.MCP.Server do
|
||||
|
||||
{:error, :not_found} ->
|
||||
{:error, error_response(id, -32004, "Not found")}
|
||||
|
||||
{:error, reason} ->
|
||||
{:error, error_response(id, -32000, inspect(reason))}
|
||||
end
|
||||
end
|
||||
|
||||
@@ -292,9 +289,7 @@ defmodule BDS.MCP.Server do
|
||||
defp http_error_response(status, headers \\ %{}), do: http_response(status, reason_body(status), "text/plain", headers)
|
||||
|
||||
defp reason_body(400), do: "Bad Request"
|
||||
defp reason_body(403), do: "Forbidden"
|
||||
defp reason_body(404), do: "Not Found"
|
||||
defp reason_body(_status), do: "Internal Server Error"
|
||||
|
||||
defp maybe_allow_repo(owner_pid) do
|
||||
try do
|
||||
|
||||
101
lib/bds/posts.ex
101
lib/bds/posts.ex
@@ -139,12 +139,21 @@ defmodule BDS.Posts do
|
||||
def rebuild_posts_from_files(project_id) do
|
||||
project = Projects.get_project!(project_id)
|
||||
|
||||
posts =
|
||||
rebuild_files =
|
||||
project
|
||||
|> Projects.project_data_dir()
|
||||
|> Path.join("posts")
|
||||
|> list_matching_files("*.md")
|
||||
|> Enum.map(&upsert_post_from_file(project_id, project, &1))
|
||||
|> Enum.map(&parse_rebuild_file(project, &1))
|
||||
|
||||
{translation_files, post_files} = Enum.split_with(rebuild_files, &translation_rebuild_file?/1)
|
||||
|
||||
posts =
|
||||
post_files
|
||||
|> Enum.map(&upsert_post_from_rebuild_file(project_id, &1))
|
||||
|
||||
translation_files
|
||||
|> Enum.map(&upsert_post_translation_from_rebuild_file(project_id, &1))
|
||||
|
||||
{:ok, posts}
|
||||
end
|
||||
@@ -632,9 +641,13 @@ defmodule BDS.Posts do
|
||||
end
|
||||
|
||||
defp upsert_post_from_file(project_id, project, path) do
|
||||
contents = File.read!(path)
|
||||
{:ok, %{fields: fields}} = Frontmatter.parse_document(contents)
|
||||
relative_path = Path.relative_to(path, Projects.project_data_dir(project))
|
||||
project
|
||||
|> parse_rebuild_file(path)
|
||||
|> upsert_post_from_rebuild_file(project_id)
|
||||
end
|
||||
|
||||
defp upsert_post_from_rebuild_file(project_id, rebuild_file) do
|
||||
fields = rebuild_file.fields
|
||||
now = Persistence.now_ms()
|
||||
|
||||
attrs = %{
|
||||
@@ -649,7 +662,7 @@ defmodule BDS.Posts do
|
||||
created_at: Map.get(fields, "created_at", now),
|
||||
updated_at: Map.get(fields, "updated_at", now),
|
||||
published_at: Map.get(fields, "published_at"),
|
||||
file_path: relative_path,
|
||||
file_path: rebuild_file.relative_path,
|
||||
checksum: nil,
|
||||
tags: Map.get(fields, "tags", []),
|
||||
categories: Map.get(fields, "categories", []),
|
||||
@@ -672,9 +685,85 @@ defmodule BDS.Posts do
|
||||
|> tap(&Embeddings.sync_post/1)
|
||||
end
|
||||
|
||||
defp upsert_post_translation_from_rebuild_file(project_id, rebuild_file) do
|
||||
fields = rebuild_file.fields
|
||||
source_post_id = Map.fetch!(fields, "translation_for")
|
||||
source_post = Repo.get!(Post, source_post_id)
|
||||
now = Persistence.now_ms()
|
||||
language = normalize_language(Map.fetch!(fields, "language"))
|
||||
|
||||
translation =
|
||||
Repo.get_by(Translation, translation_for: source_post_id, language: language) || %Translation{}
|
||||
|
||||
attrs = %{
|
||||
id: Map.get(fields, "id") || Ecto.UUID.generate(),
|
||||
project_id: project_id,
|
||||
translation_for: source_post_id,
|
||||
language: language,
|
||||
title: Map.get(fields, "title") || "",
|
||||
excerpt: Map.get(fields, "excerpt"),
|
||||
content: nil,
|
||||
status: parse_translation_status(Map.get(fields, "status", "published")),
|
||||
created_at: Map.get(fields, "created_at", source_post.created_at || now),
|
||||
updated_at: Map.get(fields, "updated_at", source_post.updated_at || source_post.created_at || now),
|
||||
published_at: Map.get(fields, "published_at", source_post.published_at),
|
||||
file_path: rebuild_file.relative_path,
|
||||
checksum: nil
|
||||
}
|
||||
|
||||
translation
|
||||
|> Translation.changeset(attrs)
|
||||
|> Repo.insert_or_update!()
|
||||
|> tap(fn _translation ->
|
||||
:ok = Search.sync_post(source_post_id)
|
||||
end)
|
||||
end
|
||||
|
||||
defp parse_post_status(status) when is_atom(status), do: status
|
||||
defp parse_post_status(status), do: String.to_existing_atom(status)
|
||||
|
||||
defp parse_translation_status(status) when is_atom(status), do: status
|
||||
defp parse_translation_status(status), do: String.to_existing_atom(status)
|
||||
|
||||
defp parse_rebuild_file(project, path) do
|
||||
contents = File.read!(path)
|
||||
{:ok, %{fields: fields}} = Frontmatter.parse_document(contents)
|
||||
|
||||
%{
|
||||
path: path,
|
||||
relative_path: Path.relative_to(path, Projects.project_data_dir(project)),
|
||||
fields: normalize_rebuild_fields(fields)
|
||||
}
|
||||
end
|
||||
|
||||
defp translation_rebuild_file?(%{fields: fields}) do
|
||||
Map.has_key?(fields, "translation_for") and not Map.has_key?(fields, "slug")
|
||||
end
|
||||
|
||||
defp normalize_rebuild_fields(fields) when is_map(fields) do
|
||||
[
|
||||
{"translationFor", "translation_for"},
|
||||
{"doNotTranslate", "do_not_translate"},
|
||||
{"templateSlug", "template_slug"},
|
||||
{"createdAt", "created_at"},
|
||||
{"updatedAt", "updated_at"},
|
||||
{"publishedAt", "published_at"}
|
||||
]
|
||||
|> Enum.reduce(fields, fn {legacy_key, current_key}, acc ->
|
||||
case Map.fetch(acc, legacy_key) do
|
||||
{:ok, value} -> Map.put_new(acc, current_key, normalize_rebuild_field_value(current_key, value))
|
||||
:error -> acc
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
defp normalize_rebuild_field_value(key, value)
|
||||
when key in ["created_at", "updated_at", "published_at"] do
|
||||
Persistence.parse_timestamp(value) || value
|
||||
end
|
||||
|
||||
defp normalize_rebuild_field_value(_key, value), do: value
|
||||
|
||||
defp list_matching_files(dir, pattern) do
|
||||
if File.dir?(dir) do
|
||||
Path.join([dir, "**", pattern])
|
||||
|
||||
@@ -128,7 +128,6 @@ defmodule BDS.Rendering do
|
||||
case Frontmatter.parse_document(source) do
|
||||
{:ok, %{body: body}} -> {:ok, body}
|
||||
{:error, :invalid_frontmatter} -> {:ok, source}
|
||||
{:error, reason} -> {:error, reason}
|
||||
end
|
||||
else
|
||||
{:error, :template_not_found}
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
defmodule BDS.Scripting.Capabilities do
|
||||
@moduledoc false
|
||||
@mix_env Mix.env()
|
||||
|
||||
import Ecto.Query
|
||||
|
||||
@@ -495,7 +494,6 @@ defmodule BDS.Scripting.Capabilities do
|
||||
defp rebuild_post_links(project_id) do
|
||||
case Posts.rebuild_post_links(project_id) do
|
||||
:ok -> true
|
||||
_other -> false
|
||||
end
|
||||
end
|
||||
|
||||
@@ -508,7 +506,6 @@ defmodule BDS.Scripting.Capabilities do
|
||||
defp reindex_project_search(project_id) do
|
||||
case Search.reindex_project(project_id) do
|
||||
:ok -> true
|
||||
_other -> false
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1018,10 +1015,10 @@ defmodule BDS.Scripting.Capabilities do
|
||||
end
|
||||
|
||||
defp do_copy_to_clipboard(text) do
|
||||
if @mix_env == :test do
|
||||
if test_mode?() do
|
||||
true
|
||||
else
|
||||
command = string_or_nil(text) || ""
|
||||
command = string_or_nil(text)
|
||||
|
||||
case :os.type() do
|
||||
{:unix, :darwin} -> match?({_output, 0}, System.cmd("pbcopy", [], input: command, stderr_to_stdout: true))
|
||||
@@ -1060,16 +1057,16 @@ defmodule BDS.Scripting.Capabilities do
|
||||
|
||||
defp open_folder(folder_path, opts) do
|
||||
case Keyword.get(opts, :open_folder) do
|
||||
callback when is_function(callback, 1) -> callback.(string_or_nil(folder_path) || "")
|
||||
callback when is_function(callback, 1) -> callback.(string_or_nil(folder_path))
|
||||
_other -> do_open_folder(folder_path)
|
||||
end
|
||||
end
|
||||
|
||||
defp do_open_folder(folder_path) do
|
||||
if @mix_env == :test do
|
||||
if test_mode?() do
|
||||
""
|
||||
else
|
||||
case open_system_path(string_or_nil(folder_path) || "") do
|
||||
case shell_open_system_path(string_or_nil(folder_path)) do
|
||||
:ok -> ""
|
||||
{:error, reason} -> inspect(reason)
|
||||
end
|
||||
@@ -1084,7 +1081,7 @@ defmodule BDS.Scripting.Capabilities do
|
||||
end
|
||||
|
||||
defp do_select_folder(title) do
|
||||
if @mix_env == :test do
|
||||
if test_mode?() do
|
||||
nil
|
||||
else
|
||||
case FolderPicker.choose_directory(string_or_nil(title) || "Select Folder") do
|
||||
@@ -1104,9 +1101,9 @@ defmodule BDS.Scripting.Capabilities do
|
||||
callback = Keyword.get(opts, :show_item_in_folder)
|
||||
|
||||
cond do
|
||||
is_function(callback, 1) -> callback.(string_or_nil(item_path) || "")
|
||||
@mix_env == :test -> :ok
|
||||
true -> _ = reveal_system_path(string_or_nil(item_path) || "")
|
||||
is_function(callback, 1) -> callback.(string_or_nil(item_path))
|
||||
test_mode?() -> :ok
|
||||
true -> _ = shell_reveal_system_path(string_or_nil(item_path))
|
||||
end
|
||||
|
||||
nil
|
||||
@@ -1116,9 +1113,9 @@ defmodule BDS.Scripting.Capabilities do
|
||||
callback = Keyword.get(opts, :trigger_menu_action)
|
||||
|
||||
cond do
|
||||
is_function(callback, 1) -> callback.(string_or_nil(action) || "")
|
||||
@mix_env == :test -> :ok
|
||||
true -> _ = MenuBar.handle_event(string_or_nil(action) || "", nil)
|
||||
is_function(callback, 1) -> callback.(string_or_nil(action))
|
||||
test_mode?() -> :ok
|
||||
true -> _ = MenuBar.handle_event(string_or_nil(action), nil)
|
||||
end
|
||||
|
||||
nil
|
||||
@@ -1674,7 +1671,7 @@ defmodule BDS.Scripting.Capabilities do
|
||||
end
|
||||
defp parse_datetime(_value), do: nil
|
||||
|
||||
defp open_system_path(path) do
|
||||
defp shell_open_system_path(path) do
|
||||
{command, args} =
|
||||
case :os.type() do
|
||||
{:unix, :darwin} -> {"open", [path]}
|
||||
@@ -1690,7 +1687,7 @@ defmodule BDS.Scripting.Capabilities do
|
||||
error -> {:error, error}
|
||||
end
|
||||
|
||||
defp reveal_system_path(path) do
|
||||
defp shell_reveal_system_path(path) do
|
||||
{command, args} =
|
||||
case :os.type() do
|
||||
{:unix, :darwin} -> {"open", ["-R", path]}
|
||||
@@ -1705,4 +1702,8 @@ defmodule BDS.Scripting.Capabilities do
|
||||
rescue
|
||||
error -> {:error, error}
|
||||
end
|
||||
|
||||
defp test_mode? do
|
||||
Application.get_env(:bds, :test_mode, false)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -195,9 +195,7 @@ defmodule BDS.Scripting.Lua do
|
||||
case values do
|
||||
[] -> nil
|
||||
[value] -> value
|
||||
_other -> values
|
||||
_values -> values
|
||||
end
|
||||
end
|
||||
|
||||
defp unwrap_result(values), do: values
|
||||
end
|
||||
|
||||
@@ -350,7 +350,10 @@ defmodule BDS.Tasks do
|
||||
defp public_task(nil), do: nil
|
||||
|
||||
defp public_task(task) do
|
||||
Map.drop(task, [:last_reported_at])
|
||||
task
|
||||
|> Map.drop([:last_reported_at])
|
||||
|> Map.update(:error, nil, &json_safe_value/1)
|
||||
|> Map.update(:result, nil, &json_safe_value/1)
|
||||
end
|
||||
|
||||
defp build_status_snapshot(state) do
|
||||
@@ -419,6 +422,29 @@ defmodule BDS.Tasks do
|
||||
defp normalize_result({:error, _reason} = result), do: result
|
||||
defp normalize_result(value), do: {:ok, value}
|
||||
|
||||
defp json_safe_value(value) when is_nil(value), do: nil
|
||||
defp json_safe_value(value) when is_binary(value), do: value
|
||||
defp json_safe_value(value) when is_boolean(value), do: value
|
||||
defp json_safe_value(value) when is_number(value), do: value
|
||||
defp json_safe_value(value) when is_atom(value), do: value
|
||||
|
||||
defp json_safe_value(value) when is_list(value) do
|
||||
Enum.map(value, &json_safe_value/1)
|
||||
end
|
||||
|
||||
defp json_safe_value(value) when is_struct(value), do: inspect(value)
|
||||
|
||||
defp json_safe_value(value) when is_map(value) do
|
||||
Map.new(value, fn {key, item} -> {json_safe_key(key), json_safe_value(item)} end)
|
||||
end
|
||||
|
||||
defp json_safe_value(value) when is_tuple(value), do: inspect(value)
|
||||
defp json_safe_value(value), do: inspect(value)
|
||||
|
||||
defp json_safe_key(key) when is_binary(key), do: key
|
||||
defp json_safe_key(key) when is_atom(key), do: key
|
||||
defp json_safe_key(key), do: inspect(key)
|
||||
|
||||
defp max_concurrent do
|
||||
Application.get_env(:bds, :tasks, [])
|
||||
|> Keyword.get(:max_concurrent, @default_max_concurrent)
|
||||
|
||||
Reference in New Issue
Block a user