defmodule Mix.Tasks.CommBus.SyncFixtures do
use Mix.Task
@shortdoc "Sync golden fixtures from canonical prompt roots"
@moduledoc """
Syncs prompt files from external project directories into test fixtures.
## Usage
mix comm_bus.sync_fixtures [--clean]
## Configuration
Set source directories via environment variables:
HUMAN_PROMPTS_DIR=/path/to/human/prompts \\
DEVMAN_PROMPTS_DIR=/path/to/devman/prompts \\
mix comm_bus.sync_fixtures
Or pass them as arguments:
mix comm_bus.sync_fixtures --human /path/to/human/prompts --devman /path/to/devman/prompts
## Options
* `--clean` - Remove existing fixtures before syncing
* `--human` - Path to HuMan prompts directory
* `--devman` - Path to DevMan prompts directory
"""
@doc """
Synchronizes prompt files from external project directories into the
golden test fixture directory, optionally cleaning existing fixtures first.
## Parameters
- `args` — Command-line argument list; supports `--clean`, `--human`, `--devman`.
"""
@impl true
def run(args) do
{opts, _rest, _} =
OptionParser.parse(args, switches: [clean: :boolean, human: :string, devman: :string])
clean? = Keyword.get(opts, :clean, false)
pairs =
[
{resolve_source(:human, opts), "test/fixtures/golden/human/prompts"},
{resolve_source(:devman, opts), "test/fixtures/golden/devman/prompts"}
]
|> Enum.filter(fn {src, _dest} -> src != nil end)
if pairs == [] do
Mix.shell().error("""
No source directories configured. Set via environment variables:
HUMAN_PROMPTS_DIR=/path/to/prompts DEVMAN_PROMPTS_DIR=/path/to/prompts mix comm_bus.sync_fixtures
Or pass as arguments:
mix comm_bus.sync_fixtures --human /path/to/prompts --devman /path/to/prompts
""")
else
Enum.each(pairs, fn {src, dest} ->
sync_dir(src, dest, clean?)
end)
end
end
defp resolve_source(:human, opts) do
Keyword.get(opts, :human) || System.get_env("HUMAN_PROMPTS_DIR")
end
defp resolve_source(:devman, opts) do
Keyword.get(opts, :devman) || System.get_env("DEVMAN_PROMPTS_DIR")
end
defp sync_dir(src, dest, clean?) do
unless File.dir?(src) do
Mix.raise("Source directory does not exist: #{src}")
end
if clean? do
File.rm_rf!(dest)
end
File.mkdir_p!(dest)
src
|> Path.join("**/*.md")
|> Path.wildcard()
|> Enum.each(fn path ->
rel = Path.relative_to(path, src)
target = Path.join(dest, rel)
File.mkdir_p!(Path.dirname(target))
File.cp!(path, target)
end)
Mix.shell().info("Synced fixtures: #{src} -> #{dest}")
end
end