sv_ex_new

The mix sv_ex.new bootstrap archive — one of SvEx's two outputs, and the only one a developer installs globally.

It generates a stock Elixir or Phoenix project, splices the sv_ex dev dependency into its mix.exs, writes target.exs, and fetches and compiles dependencies. Everything after that — applying plugins, recording what was written — belongs to the hex package, which the generated project now depends on.

mix archive.install hex sv_ex_new
mix sv_ex.new NAME --base otp|api|web --github-org ORG [--sv-ex-path PATH]
flagmeaning
--baseotp (mix new), api or web (both phx.new). Required.
--github-orgWritten into target.exs. Required — a silent default here would
silently generate the wrong project.
--appOverride the OTP app name derived from NAME.
--moduleOverride the root module derived from NAME.
--sv-ex-pathSplice a path: dep instead of the hex requirement — for developing
SvEx itself against a real generated project.

--base api and --base web generate the same stock tree (phx.new --no-html --no-assets). That is deliberate: priv/baselines.exs records the :web baseline as derived_from: :api plus the :web_layer plugin, so the asset pipeline arrives when that plugin is applied, never from phx.new. Generating :web with HTML and assets would hand the plugin a tree it did not derive against.

Zero dependencies, on purpose

This archive declares no hex requirements, and SvEx.ArchiveTest in the root suite asserts it against the built .ez and the unpacked hex metadata.

A Mix archive goes on the developer's global code path, where an archive-resident module permanently shadows the real one a target project depends on. Every dependency carries only: excluding :prodandruntime: false; runtime: false alone is not enough, because a dep without only: still emits a full entry in the package's requirements.

That same shadowing is why this archive is a build target of its own rather than part of the root project — the root README's "One project, two outputs" carries that half.

Post-MVP continuation — verified working, deliberately NOT implemented

Once mix sv_ex.gen exists in the hex package, Mix.Tasks.SvEx.New continues with this sequence. It is recorded here rather than in the task's @moduledoc because SvEx.BoundaryGuard substring-scans this perimeter's lib/**/*.ex — documentation and comments included — and two of these four calls are on its banned list. Prose that names a banned API is indistinguishable from a call to it, by design: the guard exists because a rule expressed only in prose rots.

File.cd!(target)
Mix.Project.pop()
Code.compile_file("mix.exs", ignore_module_conflict: true)
Mix.Task.run("sv_ex.gen", argv)

All four steps are required and were verified at Elixir 1.20.3.

Running this today buys nothing — sv_ex.gen does not exist — and deps.compile prunes every archive off the code path before it could.

Development

This is a separate Mix project. Its tests are not reached by the root mix test:

cd installer/sv_ex_new
mix test
mix test --include toolchain # the tests that shell out to a real generator

bin/check at the repository root runs format, credo, coveralls, doctor and dialyzer here as well as in the root — that is what "both outputs" means.