mix composer.new creates an Elixir app and applies a
composer recipe to it, in
one command:
mix composer.new my_app cb_lab:base/completions --with phx.new --with-args=--no-ecto
It drives igniter.new and igniter.install; it doesn't replace them.
Install
mix archive.install hex igniter_new # 0.5.32 or later
mix archive.install hex composer_new
mix archive.install hex phx_new # only for --with phx.new
composer_new and composer have the same version, and an app created
by composer_new X.Y.Z gets composer ~> X.Y.Z unless --composer says
otherwise.
igniter_new must be 0.5.32 or later: earlier releases either ignore
--no-git or pass it on to the installers of --install packages. Upgrade
with mix archive.install hex igniter_new --force.
Usage
mix composer.new APP [REF] [RECIPE-FLAGS] [OPTIONS]APP comes first, as with igniter.new and phx.new. It may have a
directory part (/tmp/my_app); its last segment must be a valid app name.
REF
REF = COOKBOOK:GROUP/RECIPE[@SOURCE]
SOURCE = <version> hex, e.g. @0.3 or @~> 0.3.1 (no @: the latest)
| github:OWNER/REPO[@GITREF]
| git:URL
| path:PATHREF is split on its first @: COOKBOOK:GROUP/RECIPE goes to
mix composer.recipe.run, and COOKBOOK@SOURCE to mix igniter.install.
# hex
mix composer.new my_app cb_lab:base/completions
# github, default branch or pinned
mix composer.new my_app cb_lab:base/completions@github:elixir-composer/cb_lab
mix composer.new my_app cb_lab:base/completions@github:elixir-composer/cb_lab@v0.3.0
# a local checkout
mix composer.new my_app cb_home:lv/base@path:~/src/Gen/cb_home --with phx.new
# a base that is a package, not a task
mix composer.new my_blog cb_home:astral/min@path:~/src/Gen/cb_home \
--install astral@path:~/src/Fork/astral
# recipe flags pass through to composer.recipe.run
mix composer.new my_app cb_mine:phx/saas --oban-pro --gigalixir
Rules:
- REF must name its cookbook:
lv/basealone is an error (there are no shortnames). core:GROUP/RECIPEinstalls no cookbook (coreships with composer), so it takes no@SOURCE.hex:refs name packages, not cookbooks, and are rejected.- Without REF, the app gets composer and nothing else. Run
mix composer.initin it to start the app's own cookbook.
Options
| Option | Meaning |
|---|---|
--with TASK | the base generator, as in igniter.new (phx.new, ...); default new |
--with-args ARGS | arguments for --with; prefer --with-args="--no-ecto --binary-id" |
--install PKG[,PKG] | base packages installed with the app as prod deps (repeatable) |
--composer SOURCE | composer's source: path:..., github:... or a version; default ~> <archive version> |
--extra-dep PKG | another dev/test dep, such as a second cookbook (repeatable) |
--igniter-local PATH | a local igniter checkout, passed as igniter.new --local (made relative to APP's parent) |
--reset | delete APP first |
--dry-run | print the plan and run nothing |
--no-recipe | create, install and commit, but don't run the recipe |
Every other switch is a recipe flag and goes to mix composer.recipe.run,
which rejects any flag the recipe doesn't declare before it changes
anything. Everything after -- is a recipe flag too. There is no --yes:
the command runs unattended.
A path: source, relative or starting with ~, is made absolute, since
the install runs inside the new app.
Composer reserves these option names: no recipe may declare a flag called
with, install, reset, dry_run, and so on, and
mix composer.cookbook.lint says so.
What it runs
The plan (app, cookbook, source, base generator and every command) is
printed first, every time. --dry-run stops there.
- Preflight: the ref and options are valid; APP doesn't exist (unless
--reset);igniter_new>= 0.5.32 is installed; the--withtask's archive is installed;gitis on the PATH. - Create:
mix igniter.new APP --no-git --yes, with--with,--with-args,--installand--local. - Install: in APP,
mix igniter.install composer@SRC,COOKBOOK@SRC,... --only dev,test --yes. The cookbook's own deps come with it. - Record: in APP,
git init,git add -A, and a commit whose message is themix composer.newcommand line.git log --reverseshows how to rebuild the app, andrecipe.rungets the clean tree it needs. - Run: in APP,
mix composer.recipe.run COOKBOOK:GROUP/RECIPE [RECIPE-FLAGS], which commits the recipe's changes. Skipped without a REF, or with--no-recipe.
Each command is its own mix (or git) process, with its output streamed
to the terminal.
When something fails
The run stops at the first command that fails, and exits non-zero.
Create or install failed: APP is partial. Fix the cause and rerun the same command with
--reset.The recipe failed (a mistyped flag, an unmet
requires, a step error): APP is left at its generation commit, and the exact command to retry is printed:cd my_app && mix composer.recipe.run cb_lab:base/completions --gigalixrThe install failed with the default
--composer: that composer version may not be on Hex yet; point at a checkout with--composer path:/path/to/composer.
A cookbook installed from @github: or @git: must declare hex or git
deps, not path deps: a path is resolved next to the checkout in deps/,
where it doesn't exist. mix composer.cookbook.lint warns about path deps
in a project with a package: section.
A recipe is code
A cookbook from @github: or @git: runs someone else's code on your
machine, just as igniter.new --install does. To see what a recipe would do
first:
mix composer.new my_app cb_lab:base/completions@github:elixir-composer/cb_lab --no-recipe
cd my_app
mix composer.recipe.show cb_lab:base/completions
mix composer.recipe.run cb_lab:base/completions --dry-run
License
MIT, see LICENSE.md.