Capstone.New.Bootstrap (Capstone v0.33.3)

Copy Markdown View Source

The mix capstone.new sequence, with every effect injected.

It lives here rather than in Mix.Tasks.Capstone.New.run/1 for a coverage reason that is structural, not stylistic: @impl Mix.Task fixes run/1's arity at 1, so there is no parameter through which a fake runner could reach the sequence. Measured with the sequence inlined in run/1, 12 of 14 relevant lines were reachable only from a @tag :toolchain test that ExUnit.start(exclude:) removes from the coverage run, and rescuing them with coveralls-ignore blew the 5% budget several times over.

The call order is load-bearing

The generator probe must precede any dependency work, because mix deps.compile prunes every archive off the code path (Code.delete_paths(current_paths -- loaded_paths)). Measured: archive paths go from three entries to [] across that one call, after which Mix.Task.get("phx.new") returns nil even though phx_new is installed on disk.

Patching and config-writing precede dependency work too, so a project whose mix.exs cannot be patched is never compiled against a dep it does not declare.

Plugin application sits between the target.exs write and dependency work, for two independent reasons: it must follow the write, because Capstone.Plugin.Install reads target.exs back through Capstone.Config; and it must precede deps.get/deps.compile, because a plugin's deps: are written into mix.exs there and would otherwise never be fetched.

Each plugin is synced (effects.sync, Capstone.Plugin.Remote.sync!/2 by default) immediately before it is installed, never once for the whole batch up front — a later plugin's sync failure must not undo an earlier plugin's already-completed install.

Summary

Types

Every side effect run/3 performs, injected so each is fake-able in-process.

Functions

The real effects.

Runs the bootstrap. See this module's documentation for the ordering rules.

Types

effects()

@type effects() :: %{
  getenv: (-> %{optional(String.t()) => String.t()}),
  lookup: Capstone.New.Shell.lookup(),
  generator: (String.t(), [String.t()] -> any()),
  runner: Capstone.New.Shell.runner(),
  shell: module(),
  sync: (atom(), Path.t() -> :ok)
}

Every side effect run/3 performs, injected so each is fake-able in-process.

Functions

defaults()

@spec defaults() :: effects()

The real effects.

run(opts, effects, registry_dir \\ Registry.default_dir())

@spec run(Capstone.New.Options.t(), effects(), Path.t()) :: :ok

Runs the bootstrap. See this module's documentation for the ordering rules.