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]
| flag | meaning |
|---|---|
--base | otp (mix new), api or web (both phx.new). Required. |
--github-org | Written into target.exs. Required — a silent default here would |
| silently generate the wrong project. | |
--app | Override the OTP app name derived from NAME. |
--module | Override the root module derived from NAME. |
--sv-ex-path | Splice 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.
Mix.Project.pop/0is mandatory when a project is on the stack, and a harmless no-op returningnilwhen none is. Omitting it produces a state that looks correct and then fails atdeps.compilewith "cannot retrieve dependencies information".- The process must stay changed into the target directory for the rest of the
run, because
compile_path/0,deps_path/0andbuild_path/0re-expand against the current working directory on every call. Mix.Task.get/1is not a valid probe for the delegated task: it returnsnilwhileMix.Task.run/2on the same name succeeds, becauserun/2triggers loadpaths first.
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.