LivebookTest.DependencyPatcher (livebook_test v0.1.1)

Copy Markdown View Source

Rewrites Mix.install dependency declarations for local testing.

This is the core feature of LivebookTest and the third stage in the pipeline:

Discovery  Exporter  **DependencyPatcher**  Runner  Report

Why dependency patching matters

Livebook notebooks are standalone documents. They declare their dependencies using Mix.install/2 with Hex version specifiers:

Mix.install([
  {:my_lib, "~> 0.5"}
])

When testing notebooks as part of a project's CI pipeline, you want those notebooks to use the current checkout of my_lib, not the published Hex version. This module rewrites the dependency declarations to use local paths instead.

Modes

  • Remote (:remote) - leave the script untouched
  • Local (:local) - rewrite Hex deps and existing path: deps to stable absolute paths from local_deps

How patching works

The patcher operates on the exported Elixir script (not the .livemd), using regex-based replacement to rewrite Mix.install calls:

# Hex dep - before (remote)
Mix.install([{:my_lib, "~> 0.5"}])

# Hex dep - after (local)
Mix.install([{:my_lib, path: "/abs/path/to/project"}])

# Path dep - before
Mix.install([{:my_lib, path: Path.join(__DIR__, "..")}])

# Path dep - after (local, when my_lib is in local_deps)
Mix.install([{:my_lib, path: "/abs/path/to/project"}])

Summary

Types

Local dependency mapping

Patch mode matching LivebookTest.Config.dependency_mode()

Functions

Patches an Elixir script according to the given dependency mode.

Types

local_deps()

@type local_deps() :: LivebookTest.Config.local_deps()

Local dependency mapping

patch_mode()

@type patch_mode() :: :remote | :local

Patch mode matching LivebookTest.Config.dependency_mode()

Functions

patch(script, atom, local_deps)

@spec patch(String.t(), patch_mode(), local_deps()) :: String.t()

Patches an Elixir script according to the given dependency mode.

In :remote mode, the script is returned unchanged. In :local mode, dependency declarations within Mix.install are rewritten to use local paths.

Examples

iex> script = ~S|Mix.install([{:ex_arrow, "~> 0.5"}])|
iex> LivebookTest.DependencyPatcher.patch(script, :remote, [])
~S|Mix.install([{:ex_arrow, "~> 0.5"}])|

iex> script = ~S|Mix.install([{:ex_arrow, "~> 0.5"}])|
iex> patched = LivebookTest.DependencyPatcher.patch(script, :local, ex_arrow: ".")
iex> String.contains?(patched, "path:")
true