leestratman / wp-content-bundle
Export and import WordPress content bundles: portable slices of content with their media, for fixtures and test sites. A declared format (spec/) with a WP-CLI implementation.
Package info
github.com/LeeStratman/wp-content-bundle
Type:wp-cli-package
pkg:composer/leestratman/wp-content-bundle
Requires
- php: >=8.1
Requires (Dev)
None
Suggests
None
Provides
None
Conflicts
None
Replaces
None
README
A content bundle is a portable slice of a WordPress site's content: posts of chosen types with their meta, terms, authors, comments and the media they use, as a directory you can commit, review, and load into any WordPress that registers those post types. Seed an e2e site with real-shaped content, share a fixture between projects, reproduce a bug against the content that triggered it.
This repository is the format and its reference implementation:
spec/— the format (version 1.1): what's inbundle.json, the import rules a reader follows, versioning, privacy.spec/schema.jsonis the JSON Schema;spec/examples/minimalis a real bundle.wp content-bundle— a WP-CLI package that exports, imports and removes bundles.ContentBundle\…— the same, as plain PHP functions a plugin can call (src/functions.php).
It is not a backup: ids are reassigned on import, references are rewritten to match, and personal data is scrubbed on export by default.
campaigns/ any name — a directory is a bundle because it holds bundle.json
bundle.json manifest + posts, users, media, comments (+ optional site_config)
media/12-hero.jpg the files of every attachment the posts use
Use it
# Export: personal data scrubbed, media files included wp content-bundle export --post-types=page,campaign --output=tests/Fixtures/campaigns # Import, replacing whatever the last import of this bundle created wp content-bundle import tests/Fixtures/campaigns --replace wp content-bundle import tests/Fixtures/campaigns --dry-run # what would happen wp content-bundle remove tests/Fixtures/campaigns # take it back out
| Export option | |
|---|---|
--scrub-meta=k1,k2 |
Also blank these meta keys (names, phone numbers — what a pattern can't spot). * allowed. The report names keys where emails turned up. |
--content-meta=k1,k2 |
Meta keys that are page content (pages built from ACF or other custom fields): left as published, like post content. * allowed. The report suggests this for ACF fields where it found emails. |
--keep-personal-data |
Don't scrub. Local use only. |
--include-config |
Add allowlisted options, theme and plugins as site_config. |
--overwrite |
Replace an existing bundle; it keeps its id, so --replace still matches. |
--output=x.json |
One file instead of a directory; media then imports by URL. |
| Import option | |
|---|---|
--replace |
First remove what an earlier import of the same bundle created. |
--dry-run |
Report per post type what would import or be skipped. |
--skip-media |
Import no media (offline, deterministic). |
--with-config |
Also apply site_config. |
--force |
Write to a site not marked local or development. |
Imports refuse unless WP_ENVIRONMENT_TYPE is local or development. Posts whose type the
site doesn't register are skipped and reported, never inserted, so activate the theme or plugin
that registers them first.
Install
In a project, with Composer (it's a wp-cli-package; Composer's autoloader loads it):
{
"repositories": [{ "type": "vcs", "url": "https://github.com/LeeStratman/wp-content-bundle.git" }],
"require-dev": { "leestratman/wp-content-bundle": "^1.0" }
}
Then point WP-CLI at it: wp --require=vendor/leestratman/wp-content-bundle/command.php …, or in
wp-cli.yml:
require: - vendor/leestratman/wp-content-bundle/command.php
Globally: wp package install https://github.com/LeeStratman/wp-content-bundle.git
From JS tooling (to mount the PHP into a container, or validate bundles against the schema):
pnpm add -D github:LeeStratman/wp-content-bundle#v1.0.0, then wp-content-bundle/schema.json.
command.php is safe to load more than once and outside WP-CLI, where it only defines the library.
Versions
The package follows semver. The format has its own format_version (MAJOR.MINOR, see the
spec): readers refuse a format major they don't implement and ignore fields they don't know, so
format minors add only optional fields. A package major is the place for a format major.
Developing
tests/smoke # the spec's import rules against a real WordPress; needs only Docker
Change the spec and the code together: a rule in spec/README.md that the smoke test doesn't check
is a rule nothing holds the code to.
Origin
Extracted from WordPress Sidekick, which still writes
and reads bundles (sk export / sk import) through this package. Bundles Sidekick wrote before
format 1.0 (*.skpkg directories with package.json) still import.
License
GPL-2.0-or-later, like WordPress. See LICENSE.