cwp content
shipped 1.0.0cwp content [flags]
cwp content groups the commands below and takes no action of its own; run it alone and it prints its help.
| Subcommand | What it does |
|---|---|
cwp content list | List the posts in scope on a site, and whether cwp manages them |
cwp content status | Compare the site against the committed tree, in both directions, without writing |
cwp content pull | Read named posts down into the committed tree |
cwp content push | Write named posts from the committed tree to an environment |
What it does
Moves individual pages and posts between an environment and a committed tree of JSON. This is the command for the work that is most of the work: building a page and getting it live.
pull and push name the direction relative to the tree, not to your laptop.
cwp content pull dev brings dev’s pages into content/; cwp content push dev
sends them there. Omitting the environment means the local site.
cwp content pull --post about-us # from the local site into content/
git add content/ && git commit -m "…"
cwp content status dev # what differs, both directions, no writes
cwp content push dev --post about-us
This is not a hole in the safety model
The database flows downward. What that rule protects against is a write of unbounded extent — one where nobody can say beforehand what will be different afterwards.
A content push is a named list of posts, printed before anything happens. cwp has
shipped a bounded upward database write since 0.4.0:
cwp bricks push replaces global classes and templates on a
protected environment under the full guard set, and nobody calls that a breach.
The only structural difference to a page is which table the row sits in. So:
Bulk database state flows downward. Individually named items may flow upward, under the full guard set, with the affected items listed before the write.
The artifact
content/<type>/<slug>.json, one file per post, committed. That is half the
point of the feature: a Bricks page is database state, so without this it has no
diff, no review and no history — you build one and git status stays empty.
The file is environment-neutral. The site URL is stored as {{site}} and
every attachment id as its upload path, so the same page pulled from two
environments produces the same bytes. Without that, every push would look like a
conflict.
It is canonical: sorted keys throughout, two-space indent, trailing newline — and a pull that changes nothing writes nothing, so re-running one does not dirty the tree or move an mtime.
Identity is _cwp_uid, a UUID in post meta, so a renamed slug moves the file and
keeps the item. A post that has none is adopted — named in the output and
confirmed, because an adoption that guesses wrong marries two unrelated pages.
media describes each attachment by path, with a note field that is yours: cwp
reads it, carries it through a round trip and never writes it. It is there
because the committed file is the only place a remark about an image survives a
cwp pull, which replaces the local database wholesale.
Configuration
Absent means the project does not use the transfer, and cwp content says so.
One group inline is the common case:
content:
dir: content/
post_types: [page, post]
meta:
include: ["_bricks_*", "_thumbnail_id", "_wp_page_template"]
exclude: ["_edit_lock", "_edit_last"]
…or a map of named groups, which is what a custom post type wants — its own directory and usually its own meta rules:
content:
pages:
post_types: [page]
event:
dir: events/
post_types: [acme_event]
meta:
include: ["_bricks_*", "_event_*"]
A group’s dir defaults to its own name. The two shapes are told apart by
whether dir/post_types/meta sit at the top, and they live under content:
rather than at the top level of .cwp.yml because the top level is a reserved
namespace — a post type called bricks or pull would collide with a real key.
The meta allowlist is short, and every export names what it skipped, once, with the key to add. Losing a site’s SEO fields silently on the first push is the failure class this project does not ship. Bricks’ own page content and settings never travel as raw meta — they go through Bricks’ abilities, which is what normalises them and takes the revision.
What it does not do
It deletes nothing on the target and has no --prune. Files are replaceable;
content is not.
It does not merge two versions of a page, and it does not transfer post types no group claims. Editorial content that lives and changes on production should keep doing that — this serves the page you build, which is a design artifact stored as a post.