BUCKYVERSE

OutputPaths Migration Guide

This guide helps developers migrate from legacy path handling to the new OutputPaths system in Codexis.

Overview

As part of Task #20, Codexis has migrated to a unified path management system using the OutputPaths struct. This provides consistent, centralized path handling across all builders and core modules.

Key Changes

1. BuildOrchestrator Changes

Deprecated Functions

New Functions

2. OutputManager Enhancements

New Functions

3. OutputPaths New Utilities

New Functions

Migration Examples

1. Basic Path Resolution

Before (Deprecated)

# Old approach using subdir
pdf_dir = BuildOrchestrator.subdir(orchestrator, "pdf")
pdf_file = Path.join(pdf_dir, "#{book_name}.pdf")
# New approach using OutputPaths
output_paths = BuildOrchestrator.create_output_paths(orchestrator, book_name)
pdf_file = OutputPaths.get(output_paths, :pdf, :temp_file)

2. Output File Copying

Before (Legacy)

# Old approach
{:ok, final_path} = OutputManager.copy_format_output(orchestrator, "pdf", book_name)
# New approach using OutputPaths
output_paths = BuildOrchestrator.create_output_paths(orchestrator, book_name)
{:ok, final_path} = OutputManager.copy_with_output_paths(output_paths, :pdf)

3. Directory Copying

Before (Legacy)

# Old approach - manual path construction
temp_dir = BuildOrchestrator.output_dir(orchestrator)
html_temp = Path.join(temp_dir, "html")
project_html = Path.join(project_root, "output", "html")
{:ok, _} = OutputManager.copy_directory(html_temp, project_html)
# New approach using OutputPaths
output_paths = BuildOrchestrator.create_output_paths(orchestrator, book_name)
{:ok, final_dir} = OutputManager.copy_directory_with_output_paths(output_paths, :html)

4. Working with Multiple Formats

Before (Legacy)

# Old approach - repetitive path construction
pdf_dir = BuildOrchestrator.subdir(orchestrator, "output")
epub_dir = BuildOrchestrator.subdir(orchestrator, "output")
html_dir = BuildOrchestrator.subdir(orchestrator, "output")

pdf_file = Path.join([pdf_dir, "pdf", "#{book_name}.pdf"])
epub_file = Path.join([epub_dir, "epub", "#{book_name}.epub"])
html_dir = Path.join([html_dir, "html"])
# New approach - centralized path management
output_paths = BuildOrchestrator.create_output_paths(orchestrator, book_name)

pdf_file = OutputPaths.get(output_paths, :pdf, :temp_file)
epub_file = OutputPaths.get(output_paths, :epub, :temp_file)
html_dir = OutputPaths.get(output_paths, :html, :temp_dir)

# Check what formats are available
available = OutputPaths.available_formats(output_paths)
# [:pdf, :html, :markdown, :wikijs, :epub, :docx, :rtf]

Builder Pattern Migration

Before (in Builders)

defmodule MyBuilder do
  def do_build(orchestrator, source_dir, book_name, _options) do
    # Manual path construction
    output_dir = BuildOrchestrator.output_dir(orchestrator)
    format_dir = Path.join(output_dir, "myformat")
    File.mkdir_p!(format_dir)
    
    temp_file = Path.join(format_dir, "#{book_name}.ext")
    
    # Build logic...
    
    # Manual copying
    project_root = File.cwd!()
    target_dir = Path.join([project_root, "output", "myformat"])
    File.mkdir_p!(target_dir)
    target_file = Path.join(target_dir, "#{book_name}.ext")
    File.cp!(temp_file, target_file)
    
    {:ok, target_file}
  end
end
defmodule MyBuilder do
  def do_build(orchestrator, source_dir, book_name, _options) do
    # Use OutputPaths for consistent path management
    output_paths = BuildOrchestrator.create_output_paths(orchestrator, book_name)
    
    # Get paths using OutputPaths
    temp_file = OutputPaths.get(output_paths, :myformat, :temp_file)
    temp_dir = OutputPaths.get(output_paths, :myformat, :temp_dir)
    
    # Ensure directories exist
    OutputPaths.ensure_directories(output_paths, :myformat, :temp)
    
    # Build logic...
    
    # Copy using OutputManager with OutputPaths
    case OutputManager.copy_with_output_paths(output_paths, :myformat) do
      {:ok, final_path} -> {:ok, final_path}
      {:error, reason} -> {:error, reason}
    end
  end
end

Path Types Reference

Path Type Description Example
:temp_dir Temporary build directory /tmp/codexis_build_123/output/pdf
:temp_file Temporary output file /tmp/codexis_build_123/output/pdf/book.pdf
:project_dir Final project directory /project/output/pdf
:project_file Final project file /project/output/pdf/book.pdf
:images_dir Images directory (HTML/Markdown) /tmp/codexis_build_123/output/html/images

Supported Formats

Format Description File Extension
:pdf PDF documents .pdf
:html HTML pages .html
:markdown Markdown files .md
:wikijs WikiJS content .md
:epub EPUB e-books .epub
:docx Word documents .docx
:rtf Rich Text Format .rtf

Error Handling

The new OutputPaths system provides better error handling:

# Clear error messages for missing formats
try do
  OutputPaths.get(output_paths, :invalid_format, :temp_file)
rescue
  ArgumentError, msg ->
    # "OutputPaths: format :invalid_format not found."
end

# Clear error messages for missing path types
try do
  OutputPaths.get(output_paths, :pdf, :invalid_type)
rescue
  ArgumentError, msg ->
    # "OutputPaths: key :invalid_type not found for format :pdf."
end

Testing Considerations

Mock Compatibility

The OutputPaths system maintains compatibility with existing mocks:

# Your existing BuildOrchestrator mocks will continue to work
# OutputPaths.new/3 accepts any struct that responds to the expected interface

Test Helpers

Consider creating test helpers for common OutputPaths operations:

defmodule TestHelpers do
  def create_test_output_paths(orchestrator, book_name \\ "test_book") do
    BuildOrchestrator.create_output_paths(orchestrator, book_name, "/tmp/test")
  end
end

Rollback Plan

If issues arise, you can temporarily revert to legacy functions:

  1. Legacy functions remain available with deprecation warnings
  2. No breaking changes to existing builder interfaces
  3. Gradual migration path allows testing individual modules

Performance Benefits

The new system provides:

Next Steps

  1. Update your builders: Migrate builders one at a time to use OutputPaths
  2. Update tests: Ensure test mocks work with the new system
  3. Remove deprecated calls: Search for subdir/2 usage and replace
  4. Update documentation: Update any module docs that reference old patterns

Questions?

For questions about the migration:

  1. Check the updated module documentation
  2. Look at the examples in existing migrated builders
  3. Refer to the test suite for usage patterns