Skip to content

cwp settings

shipped 1.0.0
cwp settings [flags]

cwp settings groups the commands below and takes no action of its own; run it alone and it prints its help.

FlagWhat it doesDefault
--with-agentwith —dry-run on a host with no shell: install and remove the PHP agent so the plan is realoff
SubcommandWhat it does
cwp settings pullRead the declared options into settings/
cwp settings pushWrite the committed options to an environment (an upward write against a remote)

What it does

Carries the decisions that are not posts as committed files under settings/. Those are the permalink structure, the front page, the timezone, the theme’s modifications and a plugin’s configuration.

They all live in wp_options, and nothing carried them. A checkout plus cwp fetch plus cwp inventory apply reproduces the code of a site and none of its settings. A second machine then builds something that is not the same site.

pull reads the declared options into settings/. push writes them back. That is an upward write, with every key named before it happens.

An allowlist, never a dump

A project declares which keys it carries, in cwp.yml:

settings:
  core:
    preset: wordpress-settings    # a list cwp keeps current
  frontpage:
    preset: wordpress-pages       # the options holding a post ID
  shop:
    keys: ["woocommerce_*"]
    exclude: ["woocommerce_debug_*"]
  stats:
    keys: [stats_settings]
    hold: [stats_settings.geoip_license_key]
  smtp:
    keys: [custom_smtp_settings]
    classify:
      custom_smtp_settings.smtp_password: secret
      custom_smtp_settings.smtp_host: environment
  local-only:
    keys: [blog_public, admin_email]
    environments: [local]

A preset is a list cwp maintains. Twenty-three core option keys therefore stay out of every project and do not go stale there when WordPress adds one. keys: beside a preset adds to it.

hold: names a path inside an option whose value is the target’s own. A plugin keeps its whole configuration in one option, and one field in it is a licence key. The refusal reads the name and would refuse the option whole. A held path leaves the tree on the pull and comes back from the target on the push. The thirty settings beside it travel. exclude: is a different sentence: an excluded option is not the group’s in either direction.

classify: says what a path inside an option is. setting travels. environment, secret, log and derived stay on the target. attachment, post and term name an id that travels as an identity. The push resolves it on the target. user stays on the target. The file records every path that stayed behind with its class under withheld:, so a reader sees why. hold: is the one-word spelling of environment.

environments: scopes a group, the way it scopes an inventory item. A setting is not always one value for a project: blog_public is 0 on staging and 1 in production. A tree holding one value for both would deindex a live site on the first push from a laptop. cwp skips a scoped group in both directions.

A group can also come from a family: features: in cwp.yml switches one on, and its options arrive here as a group named <owner>/<family>, with its file under settings/<owner>/<family>.yml. The cwp.yml reference has the file a family reads from.

You do not write that from memory. cwp coverage with --suggest groups the site’s options by the plugin or theme that owns them and prints this block with the counts that justify each line.

as: post-reference is for the options holding a post ID. The file stores the page’s identity and a push resolves it on the target. An ID means a different page on every environment.

Four things never travel

  • Credentials. cwp refuses *_key, *_secret, *_token, *_password, *_license and auth_* whatever the block says. A literal one in cwp.yml is a refusal with the reason. One a glob happens to match drops out, and the report names it. No key turns this off. A secret in the tree is a secret in the history, permanently.

    cwp judges the value too, not only the name. A plugin that keeps its whole configuration in one option puts its password three levels down, where a name can never catch it. A security log puts visitor IP addresses in the keys of a map. cwp refuses an option holding either whole and records it as refused.

  • Caches and derived state. Transients, cron, rewrite_rules, version markers.

  • Anything the inventory owns. inventory.yml records active_plugins and WPLANG. It does not only note the locale but installs the translation. Two writers of one name is a bug waiting for a race.

  • Anything the page builder owns. cwp refuses a group naming a key the design system already carries when it reads the block.

What it does not do

  • It deletes nothing. An option on the site that no group describes gets a report and stays as it is. Committed silence is not an instruction.
  • It carries no values it was not asked for. Only the declared keys, and only the ones that survive the refusals above.
  • It is not a database push. Named options, listed before the write, under the full guard set. Never a bulk movement.