fix: rebuilding posts and media fixed, also removed unnecessary thumbnail rebuild
Co-authored-by: Copilot <copilot@github.com>
This commit is contained in:
@@ -4,6 +4,7 @@ defmodule BDS.Frontmatter do
|
|||||||
alias BDS.Persistence
|
alias BDS.Persistence
|
||||||
|
|
||||||
@list_item_prefix " - "
|
@list_item_prefix " - "
|
||||||
|
@block_scalar_indent " "
|
||||||
|
|
||||||
def serialize_document(fields, body) when is_list(fields) do
|
def serialize_document(fields, body) when is_list(fields) do
|
||||||
frontmatter =
|
frontmatter =
|
||||||
@@ -82,7 +83,14 @@ defmodule BDS.Frontmatter do
|
|||||||
|
|
||||||
String.contains?(line, ": ") ->
|
String.contains?(line, ": ") ->
|
||||||
[key, raw_value] = String.split(line, ": ", parts: 2)
|
[key, raw_value] = String.split(line, ": ", parts: 2)
|
||||||
parse_lines(rest, Map.put(acc, key, parse_scalar(key, raw_value)))
|
|
||||||
|
if block_scalar_indicator?(raw_value) do
|
||||||
|
{value_lines, remaining} = take_block_scalar_lines(rest, [])
|
||||||
|
value = parse_scalar(key, parse_block_scalar(raw_value, value_lines))
|
||||||
|
parse_lines(remaining, Map.put(acc, key, value))
|
||||||
|
else
|
||||||
|
parse_lines(rest, Map.put(acc, key, parse_scalar(key, raw_value)))
|
||||||
|
end
|
||||||
|
|
||||||
true ->
|
true ->
|
||||||
parse_lines(rest, acc)
|
parse_lines(rest, acc)
|
||||||
@@ -100,6 +108,16 @@ defmodule BDS.Frontmatter do
|
|||||||
|
|
||||||
defp take_list_items([], items), do: {items, []}
|
defp take_list_items([], items), do: {items, []}
|
||||||
|
|
||||||
|
defp take_block_scalar_lines([line | rest], lines) do
|
||||||
|
if String.starts_with?(line, @block_scalar_indent) do
|
||||||
|
take_block_scalar_lines(rest, [String.replace_prefix(line, @block_scalar_indent, "") | lines])
|
||||||
|
else
|
||||||
|
{Enum.reverse(lines), [line | rest]}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
defp take_block_scalar_lines([], lines), do: {Enum.reverse(lines), []}
|
||||||
|
|
||||||
defp parse_scalar(key, value) when is_binary(key) and is_binary(value) do
|
defp parse_scalar(key, value) when is_binary(key) and is_binary(value) do
|
||||||
trimmed = String.trim(value)
|
trimmed = String.trim(value)
|
||||||
parsed = parse_generic_scalar(trimmed)
|
parsed = parse_generic_scalar(trimmed)
|
||||||
@@ -153,6 +171,21 @@ defmodule BDS.Frontmatter do
|
|||||||
|
|
||||||
defp parse_string(value), do: value
|
defp parse_string(value), do: value
|
||||||
|
|
||||||
|
defp block_scalar_indicator?(value) do
|
||||||
|
trimmed = String.trim(value)
|
||||||
|
String.starts_with?(trimmed, ">") or String.starts_with?(trimmed, "|")
|
||||||
|
end
|
||||||
|
|
||||||
|
defp parse_block_scalar(indicator, lines) do
|
||||||
|
trimmed = String.trim(indicator)
|
||||||
|
|
||||||
|
cond do
|
||||||
|
String.starts_with?(trimmed, ">") -> Enum.join(lines, " ")
|
||||||
|
String.starts_with?(trimmed, "|") -> Enum.join(lines, "\n")
|
||||||
|
true -> Enum.join(lines, "\n")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
defp serialize_scalar(_key, value) when is_boolean(value) do
|
defp serialize_scalar(_key, value) when is_boolean(value) do
|
||||||
if(value, do: "true", else: "false")
|
if(value, do: "true", else: "false")
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -393,7 +393,6 @@ defmodule BDS.Media do
|
|||||||
media
|
media
|
||||||
|> Media.changeset(attrs)
|
|> Media.changeset(attrs)
|
||||||
|> Repo.insert_or_update!()
|
|> Repo.insert_or_update!()
|
||||||
|> tap(fn reloaded_media -> ensure_thumbnails(project, reloaded_media) end)
|
|
||||||
|> tap(&Search.sync_media/1)
|
|> tap(&Search.sync_media/1)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@@ -196,7 +196,7 @@ defmodule BDS.MediaTest do
|
|||||||
thumbnail_paths = BDS.Media.thumbnail_paths(media)
|
thumbnail_paths = BDS.Media.thumbnail_paths(media)
|
||||||
|
|
||||||
Enum.each(Map.values(thumbnail_paths), fn path ->
|
Enum.each(Map.values(thumbnail_paths), fn path ->
|
||||||
assert File.exists?(Path.join(temp_dir, path))
|
refute File.exists?(Path.join(temp_dir, path))
|
||||||
end)
|
end)
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -240,6 +240,44 @@ defmodule BDS.MediaTest do
|
|||||||
assert media.updated_at == 1_773_847_870_146
|
assert media.updated_at == 1_773_847_870_146
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "rebuild_media_from_files does not regenerate thumbnails", %{
|
||||||
|
project: project,
|
||||||
|
temp_dir: temp_dir
|
||||||
|
} do
|
||||||
|
media_dir = Path.join([temp_dir, "media", "2026", "04"])
|
||||||
|
File.mkdir_p!(media_dir)
|
||||||
|
|
||||||
|
binary_path = Path.join(media_dir, "asset.jpg")
|
||||||
|
sidecar_path = binary_path <> ".meta"
|
||||||
|
|
||||||
|
File.write!(binary_path, tiny_jpeg_binary())
|
||||||
|
|
||||||
|
File.write!(
|
||||||
|
sidecar_path,
|
||||||
|
[
|
||||||
|
"id: media-without-thumbnails",
|
||||||
|
"originalName: original.jpg",
|
||||||
|
"mimeType: image/jpeg",
|
||||||
|
"size: #{byte_size(tiny_jpeg_binary())}",
|
||||||
|
"width: 3",
|
||||||
|
"height: 2",
|
||||||
|
"title: Recovered",
|
||||||
|
"createdAt: 2024-03-30T21:20:00.000Z",
|
||||||
|
"updatedAt: 2024-03-31T21:20:00.000Z",
|
||||||
|
"tags:",
|
||||||
|
" - alpha",
|
||||||
|
""
|
||||||
|
]
|
||||||
|
|> Enum.join("\n")
|
||||||
|
)
|
||||||
|
|
||||||
|
assert {:ok, [media]} = BDS.Media.rebuild_media_from_files(project.id)
|
||||||
|
|
||||||
|
Enum.each(Map.values(BDS.Media.thumbnail_paths(media)), fn path ->
|
||||||
|
refute File.exists?(Path.join(temp_dir, path))
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
|
||||||
test "import_media generates the four thumbnail files in bucketed thumbnail paths", %{
|
test "import_media generates the four thumbnail files in bucketed thumbnail paths", %{
|
||||||
project: project,
|
project: project,
|
||||||
temp_dir: temp_dir
|
temp_dir: temp_dir
|
||||||
|
|||||||
@@ -429,6 +429,85 @@ defmodule BDS.PostsTest do
|
|||||||
assert post.published_at == 1_036_497_600_000
|
assert post.published_at == 1_036_497_600_000
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "rebuild_posts_from_files parses folded multiline title and slug scalars alongside translations" do
|
||||||
|
temp_dir =
|
||||||
|
Path.join(System.tmp_dir!(), "bds-post-rebuild-folded-#{System.unique_integer([:positive])}")
|
||||||
|
|
||||||
|
File.mkdir_p!(temp_dir)
|
||||||
|
on_exit(fn -> File.rm_rf(temp_dir) end)
|
||||||
|
|
||||||
|
assert {:ok, project} =
|
||||||
|
BDS.Projects.create_project(%{name: "Folded Rebuild", data_path: temp_dir})
|
||||||
|
|
||||||
|
posts_dir = Path.join([BDS.Projects.project_data_dir(project), "posts", "2010", "11"])
|
||||||
|
File.mkdir_p!(posts_dir)
|
||||||
|
|
||||||
|
File.write!(
|
||||||
|
Path.join(
|
||||||
|
posts_dir,
|
||||||
|
"introducing-thirty-ten-my-guide-to-creating-a-twenty-ten-child-theme-aaron-jorb-inaaron-jorb-in.md"
|
||||||
|
),
|
||||||
|
[
|
||||||
|
"---",
|
||||||
|
"id: 66865ffe-c6d9-4379-a031-0581f33b68b4",
|
||||||
|
"title: >-",
|
||||||
|
" Introducing Thirty Ten, my guide to creating a Twenty Ten Child Theme |",
|
||||||
|
" aaron.jorb.inaaron.jorb.in",
|
||||||
|
"slug: >-",
|
||||||
|
" introducing-thirty-ten-my-guide-to-creating-a-twenty-ten-child-theme-aaron-jorb-inaaron-jorb-in",
|
||||||
|
"status: published",
|
||||||
|
"createdAt: '2010-11-13T12:37:55.000Z'",
|
||||||
|
"updatedAt: '2026-03-03T12:31:37.661Z'",
|
||||||
|
"tags:",
|
||||||
|
" - wordpress",
|
||||||
|
"categories:",
|
||||||
|
" - aside",
|
||||||
|
"author: hugo",
|
||||||
|
"language: de",
|
||||||
|
"publishedAt: '2010-11-13T11:37:55.000Z'",
|
||||||
|
"---",
|
||||||
|
"Quelle",
|
||||||
|
""
|
||||||
|
]
|
||||||
|
|> Enum.join("\n")
|
||||||
|
)
|
||||||
|
|
||||||
|
File.write!(
|
||||||
|
Path.join(
|
||||||
|
posts_dir,
|
||||||
|
"introducing-thirty-ten-my-guide-to-creating-a-twenty-ten-child-theme-aaron-jorb-inaaron-jorb-in.en.md"
|
||||||
|
),
|
||||||
|
[
|
||||||
|
"---",
|
||||||
|
"translationFor: 66865ffe-c6d9-4379-a031-0581f33b68b4",
|
||||||
|
"language: en",
|
||||||
|
"title: >-",
|
||||||
|
" Introducing Thirty Ten, my guide to creating a Twenty Ten Child Theme |",
|
||||||
|
" aaron.jorb.inaaron.jorb.in",
|
||||||
|
"---",
|
||||||
|
"Translated body",
|
||||||
|
""
|
||||||
|
]
|
||||||
|
|> Enum.join("\n")
|
||||||
|
)
|
||||||
|
|
||||||
|
assert {:ok, [post]} = BDS.Posts.rebuild_posts_from_files(project.id)
|
||||||
|
assert post.id == "66865ffe-c6d9-4379-a031-0581f33b68b4"
|
||||||
|
|
||||||
|
assert post.slug ==
|
||||||
|
"introducing-thirty-ten-my-guide-to-creating-a-twenty-ten-child-theme-aaron-jorb-inaaron-jorb-in"
|
||||||
|
|
||||||
|
assert post.title ==
|
||||||
|
"Introducing Thirty Ten, my guide to creating a Twenty Ten Child Theme | aaron.jorb.inaaron.jorb.in"
|
||||||
|
|
||||||
|
assert {:ok, [translation]} = BDS.Posts.list_post_translations(post.id)
|
||||||
|
|
||||||
|
assert translation.translation_for == post.id
|
||||||
|
|
||||||
|
assert translation.title ==
|
||||||
|
"Introducing Thirty Ten, my guide to creating a Twenty Ten Child Theme | aaron.jorb.inaaron.jorb.in"
|
||||||
|
end
|
||||||
|
|
||||||
defp errors_on(changeset) do
|
defp errors_on(changeset) do
|
||||||
Ecto.Changeset.traverse_errors(changeset, fn {message, opts} ->
|
Ecto.Changeset.traverse_errors(changeset, fn {message, opts} ->
|
||||||
Regex.replace(~r"%{(\w+)}", message, fn _, key ->
|
Regex.replace(~r"%{(\w+)}", message, fn _, key ->
|
||||||
|
|||||||
46
test/bds/real_blog_rebuild_diagnostic_test.exs
Normal file
46
test/bds/real_blog_rebuild_diagnostic_test.exs
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
defmodule BDS.RealBlogRebuildDiagnosticTest do
|
||||||
|
use ExUnit.Case, async: false
|
||||||
|
|
||||||
|
@real_blog_path System.get_env("BDS_REAL_BLOG_PATH")
|
||||||
|
@moduletag timeout: :infinity
|
||||||
|
|
||||||
|
if is_binary(@real_blog_path) and @real_blog_path != "" do
|
||||||
|
setup do
|
||||||
|
:ok = Ecto.Adapters.SQL.Sandbox.checkout(BDS.Repo, ownership_timeout: 600_000)
|
||||||
|
|
||||||
|
now = BDS.Persistence.now_ms()
|
||||||
|
unique = Integer.to_string(System.unique_integer([:positive]))
|
||||||
|
|
||||||
|
project =
|
||||||
|
%BDS.Projects.Project{}
|
||||||
|
|> BDS.Projects.Project.changeset(%{
|
||||||
|
id: "real-blog-#{unique}",
|
||||||
|
name: "Real Blog Diagnostic",
|
||||||
|
slug: "real-blog-diagnostic-#{unique}",
|
||||||
|
data_path: Path.expand(@real_blog_path),
|
||||||
|
created_at: now,
|
||||||
|
updated_at: now,
|
||||||
|
is_active: false
|
||||||
|
})
|
||||||
|
|> BDS.Repo.insert!()
|
||||||
|
|
||||||
|
%{project: project}
|
||||||
|
end
|
||||||
|
|
||||||
|
test "rebuilds posts from the external real blog path", %{project: project} do
|
||||||
|
assert {:ok, _posts} = BDS.Maintenance.rebuild_from_filesystem(project.id, "post")
|
||||||
|
end
|
||||||
|
|
||||||
|
test "rebuilds media from the external real blog path", %{project: project} do
|
||||||
|
assert {:ok, _media} = BDS.Maintenance.rebuild_from_filesystem(project.id, "media")
|
||||||
|
end
|
||||||
|
else
|
||||||
|
@tag skip: "BDS_REAL_BLOG_PATH not set"
|
||||||
|
test "rebuilds posts from the external real blog path" do
|
||||||
|
end
|
||||||
|
|
||||||
|
@tag skip: "BDS_REAL_BLOG_PATH not set"
|
||||||
|
test "rebuilds media from the external real blog path" do
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
Reference in New Issue
Block a user