cwp init
shipped 1.0.0cwp init [flags]
Acts on dev. Name another environment to act there.
| Flag | What it does | Default |
|---|---|---|
--name <name> | project name | |
--app <domain> | Cloudron app / domain for the environment | |
--provider <id> | host for the environment: cloudron | ssh | cloudron |
--ssh <host> | ssh host, e.g. deploy@example.com (with —provider ssh) | |
--path <path> | remote docroot (with —provider ssh) | |
--cloudron-package <type> | Cloudron layout: managed | developer | managed |
--php <version> | PHP version for the local container | |
--plugin-slug <slug> | site-plugin directory name (default: | |
--keep-admin-email <email> | this user survives scrubbing (pull.keep_admin_email) | |
--env <env> | environment name to create | dev |
--non-interactive | do not prompt; use flags and defaults | off |
--with-agent | with —dry-run on a host with no shell: install and remove the PHP agent so the plan is real | off |
Plus the shared flags --json, -v, --verbose, -q, --quiet and --dry-run.
What it does
Scaffolds a project. Interactive, and safe to re-run: a second run repairs the scaffold rather than overwriting your work.
It asks for the project name, the environment’s host and the site-plugin
directory. It writes the scaffold: cwp.yml, .ddev/, the site plugin, the
tracking files, CLAUDE.md, README.md, .gitignore. Then it registers the
project in the machine config.
It scaffolds either host. --provider ssh writes an ssh environment and the
matching DDEV provider config instead of a Cloudron one. It needs both --ssh
and --path. cwp cannot derive a docroot on a plain host, and a plausible guess
would send every later transfer somewhere wrong.
--plugin-slug exists because the project name is usually the domain while the
plugin keeps a short slug. It defaults to <name>-site. cwp.yml records it as
plugin_slug, and tracked points at it.
--keep-admin-email records pull.keep_admin_email: the account that survives
scrubbing. Together with pull.keep_roles it keeps you able to log in to the
local copy after a pull.
It probes nothing before the scaffold. It writes the package layout and the
PHP version from what you said or what the project already records.
cwp doctor checks them a moment later with a fix hint naming
the key to change. A probe that has to succeed before a project can exist is a
probe in the wrong place.
--dry-run names every file it would write, one per line, rather than
summarising. It creates nothing and registers no project.
What “repairs” means for cwp.yml
init leaves every other scaffolded file alone if it exists. cwp.yml is
different: it is the one file cwp itself has opinions about, so a second init
brings it up to date in place. The run reports each repair as its own step, so
nothing changes silently:
! repair cwp.yml: pull.truncate_tables — removed snn_301_logs, snn_404_logs,
snn_search_logs — no such table has ever existed (B-001)
It removes entries that cannot do anything and adds keys that were absent. It
renames the keys 1.0 replaced: package to cloudron_package, role and
protected to mode. A rename carries the value across. Where the old key
said what is now the default, it writes nothing, so a repaired file says what it
means and no more.
It changes no value you set, and it writes no default you did not set into the file. So your project keeps following future default corrections instead of pinning today’s values. It leaves a config it cannot read, or that would not validate, exactly as it is.
The rename is the one repair that decides whether the file loads at all. The schema refuses the old keys, so a project nobody has re-inited will not load until somebody does. The refusal names the new key and value rather than reporting an unrecognised one.
It reports what the project’s own layout is missing
At the end of a re-run, init names three things it does not fix. Each is an
edit to a file the project owns:
.gitignoredoes not covercwp/tmp— wherecwp db pullwrites the database dump. Add/cwp/tmp/and/cwp/bricks-snapshots/;hooks_dirpoints at a directory that is not there — hooks then silently do not run. Onecwp config set hooks_dir <path>;cwp/sits inside the document root — a web server would serve your hook scripts.
cwp doctor reports the same three, in the same words.
The DDEV provider it writes
.ddev/providers/<provider>.yaml lets ddev pull fetch the database and uploads
without cwp installed. So someone who has only DDEV can still use the
project. Every command in it runs on the host. The host CLI lives there and not
in the web container.
ddev pullis notcwp db pull. It brings production data down unscrubbed, installs no mail guard, and takes no snapshot of your local database first. So personal data lands on your machine and the site can mail real addresses. Prefercwp db pull. If you do use the provider, runcwp scrubafterwards.
The provider file refuses ddev push. It has no confirmation, no backup, no
protected-environment check and no typed environment name, and a provider file
cannot add them. The refusal is deliberate rather than a missing stanza: DDEV’s
error for a missing stanza says nothing about why.
The uploads fallback it writes
.ddev/nginx/cwp-uploads-fallback.conf serves any file below
/wp-content/uploads/ from the remote when it is missing locally. init writes
it rather than the first pull, because it is nginx configuration and takes
effect on ddev start. If a re-run creates it in a project that is already up,
run ddev restart.
Example
What a dry run names, one file per line, before anything exists:
$ cwp init --non-interactive --name example.com --app example.com --plugin-slug example-site --php 8.3 --dry-run
✓ would create public/wp-content/plugins/example-site/inc/elements/.gitkeep
✓ would create public/wp-content/plugins/example-site/inc/hooks/.gitkeep
✓ would create public/wp-content/plugins/example-site/assets/.gitkeep
✓ would create public/wp-content/mu-plugins/.gitkeep
✓ would create bricks/.gitkeep
✓ would create cwp.yml
✓ would create .ddev/config.yaml
✓ would create .ddev/providers/cloudron.yaml
✓ would create .ddev/nginx/cwp-uploads-fallback.conf
✓ would create public/wp-content/plugins/example-site/example-site.php
✓ would create public/wp-content/plugins/example-site/README.md
✓ would create .gitignore
✓ would create .gitattributes
✓ would create CLAUDE.md
✓ would create README.md
✓ would create FEATURES.md
✓ would create BUGS.md
✓ would create CHANGELOG.md
✓ would create VERSION.txt
✓ would create cwp/hooks/post-pull.sh
✓ would create cwp/hooks/pre-push.sh
✓ would create cwp/hooks/post-deploy.sh
Captured from cwp 1.0.0. Not written by hand.
cwp init --name example --app example.com --plugin-slug example-site
cwp init --provider ssh --ssh deploy@vps.example.com --path /var/www/example
cwp init --dry-run
What it does not do
It does not install WordPress core, start DDEV, or pull any data. Run ddev start,
then cwp adopt <env> and cwp db pull <env>
afterwards.
It does not probe the remote. It does not overwrite a scaffolded file that
already exists, with the single, reported exception of cwp.yml.