Skip to content

cwp init

shipped 1.0.0
cwp init [flags]

Acts on dev. Name another environment to act there.

FlagWhat it doesDefault
--name <name>project name
--app <domain>Cloudron app / domain for the environment
--provider <id>host for the environment: cloudron | sshcloudron
--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 | developermanaged
--php <version>PHP version for the local container
--plugin-slug <slug>site-plugin directory name (default: -site)
--keep-admin-email <email>this user survives scrubbing (pull.keep_admin_email)
--env <env>environment name to createdev
--non-interactivedo not prompt; use flags and defaultsoff
--with-agentwith —dry-run on a host with no shell: install and remove the PHP agent so the plan is realoff

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:

  • .gitignore does not cover cwp/tmp — where cwp db pull writes the database dump. Add /cwp/tmp/ and /cwp/bricks-snapshots/;
  • hooks_dir points at a directory that is not there — hooks then silently do not run. One cwp 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 pull is not cwp 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. Prefer cwp db pull. If you do use the provider, run cwp scrub afterwards.

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.