Install
Upload it under Plugins → Add New Plugin → Upload Plugin, then activate.
Install straight from GitHub with WP-CLI:
wp plugin install https://github.com/lukaszwilczakhostinger/wp-cli-workshops/releases/download/v1.0.0/20260810_traveler_788cd0be09865c8a8377_20260814122912_archive.zipWP-CLI Workshops — WordCamp
This repository was created for the hands-on workshop at WordCamp .
| Session | Building WP-CLI Automation for Large WordPress Content Sites |
| Speaker | Łukasz Wilczak |
| Track | Technical WordPress (North 222) |
| When | 17 August 2026, 10:15 am MST |
| Repository | lukaszwilczakhostinger/wp-cli-workshops |
Large content sites accumulate debt over years: inconsistent headings, broken or absolute internal links, leftover Gutenberg blocks, missing image alt text. In this workshop we build reusable, production-safe WP-CLI commands that process posts in batches — with --dry-run, --limit, --offset, and similar flags — instead of one-off throwaway scripts.
Bring a laptop. This README restores a full testing site in Local before the session — WordPress, this plugin, the database, and media. You only clone the plugin separately if you skip the snapshot and use your own stack (for example Docker).
What you will need
- A computer with 8 GB RAM or more (the sample site archive is large)
- Administrator rights to install software
- Local (free WordPress desktop app)
- The Duplicator package from this repo’s Releases tab (archive zip +
installer.php)
Optional but useful during the workshop: Cursor or another code editor, and a GitHub account.
1. Download the workshop testing site
The sample content lives in a GitHub Release, not in the git tree (the zip is too large).
-
Download both assets into the same folder on your computer, for example
Downloads/wcus26-duplicator/:File Role installer.phpDuplicator installer script 20260810_traveler_788cd0be09865c8a8377_20260814122912_archive.zipFull site: WordPress, plugin, database, media -
Do not rename the zip. The installer looks for that exact archive name.
The archive is about 510 MB. Give the download time to finish.
This is a full WordPress snapshot, not a plugin-only zip. It includes core, themes, Maintenance Tools WCUS26 already installed, the MySQL dump, uploads (photos and media), and the content we will use during the exercises. You do not install the plugin again after a Duplicator restore.
The site is for testing and experimentation only, not production.
2. Install Local
- Go to https://localwp.com/ and click Download for Free.
- Pick the build for your OS:
- macOS: Apple Silicon (M1/M2/M3/M4) vs Intel
- Windows
- Linux
- Install and open Local. You can skip creating a Local account if you only need a site on disk.
Local bundles PHP, Nginx/Apache, MySQL, and WP-CLI. You do not need XAMPP, MAMP, or Docker for this workshop.
3. Create an empty site in Local
Duplicator needs a running local URL and an empty MySQL database. Local creates both when you add a site.
- In Local, click + Create a new site.
- What’s your site’s name?
Usewp-cli-workshop(site URL will behttps://wp-cli-workshop.local). - Environment: Preferred is fine. If you choose Custom, use PHP 8.0 or newer (the plugin requires PHP 8.0+).
- WordPress username/password: Local will generate an admin user. You will not use it after Duplicator runs — the restored site brings its own users.
- Click Add Site and wait until the site shows as Running (green).
Local database credentials (you will type these into Duplicator)
Every Local site uses the same MySQL defaults:
| Field | Value |
|---|---|
| Host / Server | localhost |
| Database | local |
| Username | root |
| Password | root |
| Port | default (leave empty unless the installer asks; Local uses a socket/local port internally) |
You can confirm them anytime: select the site in Local → Database tab.
4. Put the Duplicator files in the site web root
The web root is app/public inside the Local site folder.
-
In Local, select wp-cli-workshop.
-
Click Go to site folder (or right-click the site → Show in Finder / Show in Explorer).
-
Open
app→public. -
Delete everything already in
public(the default WordPress files Local just installed). Leave thepublicfolder itself. -
Copy both Duplicator files into
public:public/ installer.php 20260810_traveler_788cd0be09865c8a8377_20260814122912_archive.zip -
Make sure the site is still Running in Local.
On macOS the folder is typically:
~/Local Sites/wp-cli-workshop/app/public
5. Run the Duplicator installer
-
In Local, click Open site (or visit
https://wp-cli-workshop.local/— you should not see a working WordPress home page yet). -
Open this URL in your browser:
https://wp-cli-workshop.local/installer.php
If the browser warns about a self-signed SSL certificate, continue to the site (Local’s “Trust” button on the SSL tab can add the certificate).
-
Step 1 — Setup / Archive
The installer should find the zip automatically. Accept the terms/notices and continue. -
Step 2 — Database
Enter the Local credentials from the table above:- Host:
localhost - Database:
local - User:
root - Password:
root
For Action, choose Connect and Remove All Data (or equivalent “empty this database”). Local already created a WordPress database; Duplicator must replace those tables with the workshop dump.
Click Validate / Test Connection. When it succeeds, continue.
- Host:
-
Step 3 — Update URLs
Old URLs from the packaged site should be rewritten tohttps://wp-cli-workshop.local(Duplicator usually fills this in). Keep the table prefix the installer detected. Continue. -
Step 4 — Test
Open the site and wp-admin when prompted.
Extraction of a 510 MB archive can take several minutes. Leave the tab open.
After a successful install
-
Confirm the front-end loads at
https://wp-cli-workshop.local/. -
Delete installer leftovers from
app/public(security + Duplicator’s own reminder):installer.phpinstaller-backup.php(if present)dup-installer/(if present)- the
*_archive.zipfile
-
Log in at
/wp-login.phpwith the packaged site account (not the admin Local created when you added the empty site). Local’s WP Admin button often fails for that reason.Username wordpressPassword codeispoetry -
The plugin is already in
wp-content/plugins/maintenance-tools-wcus26/. Confirm it is active:wp plugin activate maintenance-tools-wcus26 wp help wcus26For the workshop, open that plugin folder in Cursor (the Local site’s copy), so
.cursor/rulesapply while we generate commands.
If the installer fails
- Confirm both files are in
publicand the zip name was not changed. - Confirm
publiccontained only those two files before you started. - Confirm the site is Running and you used
localhost/local/root/root. - To retry: delete everything in
publicexcept the two Duplicator files, then openinstaller.phpagain.
6. WP-CLI from Local
Local → select the site → Open site shell. That shell already has wp on the PATH and the correct WordPress root.
wp wcus26
wp wcus26 list-posts --limit=5
wp wcus26 structure-stats --limit=5
wp wcus26 structure-stats-report --limit=20
Alternative: your own WordPress (Docker, MAMP, existing site)
Use this path only if you do not restore the Duplicator snapshot — for example a Docker Compose stack, MAMP, or a site you already have.
The snapshot is the recommended setup: it ships the plugin and the posts, media, and database we will query in the exercises. A blank WordPress install will run the commands, but you will not have the same content to inspect.
-
Clone this repository into
wp-content/plugins/maintenance-tools-wcus26/(or copy the plugin folder there). -
Use PHP 8.0+ and WordPress 6.0+.
-
Activate the plugin and confirm WP-CLI:
wp plugin activate maintenance-tools-wcus26 wp help wcus26 -
Point Cursor at the plugin directory inside that site.
Database host, name, user, and password then come from your stack (Docker Compose, .env, etc.), not from the Local defaults above.
Maintenance Tools WCUS26
WP-CLI maintenance toolkit used as the workshop base. It is not a finished content-fixer: we add real commands live (dead-link reports, relative URLs, heading demotion, Gutenberg block swaps, OpenAI alt text, …).
Requires WordPress 6.0+ and PHP 8.0+.
Built-in commands
| Command | What it does |
|---|---|
wp wcus26 list-posts |
TSV: ID, title, category, image count, URL |
wp wcus26 structure-stats |
TSV: paragraphs, headings, images, internal/external links |
wp wcus26 structure-stats-report |
Same stats saved as a CSV report (download from Maintenance → Reports) |
Shared flags (post commands)
| Flag | Meaning |
|---|---|
--post-type |
Default post. Comma-separated for multiple types |
--post-status |
Default publish |
--limit / --offset |
Cap or skip matching posts (0 = no limit) |
--batch-size |
Posts per query (default 10) |
--sleep |
Pause after each post (seconds, decimals allowed) |
--dry-run |
Preview without writing |
--save-logs |
Store each CLI output line in Maintenance → Logs |
--skip-all-hooks |
Disable all actions/filters during process_post() (keeps WP_Query hooks) |
--skip-hooks=save_post,edit_post |
Disable only those hooks |
Extra WP_Query args |
e.g. --category_in=3,5, --tag_slug_in=europe |
How commands are structured
Drop a class in includes/cli/commands/class-{name}-command.php. It is registered automatically as wp wcus26 {name}.
- No post loop → extend
Abstract_Command, implementrun(). - Post loop → extend
Abstract_Posts_Command, implementprocess_post(). Optional:before_run(),after_batch(),print_summary(). - CSV reports:
Report::create()once (header included), thenReport::append( Report::to_csv( $rows ) )per batch, not per post. Seestructure-stats-report. - Gutenberg updates:
parse_blocks()/serialize_blocks(), notstr_replaceon block comments. - Do not register the
wcus26root command as a closure;Root_Commandmust stay an empty class.
Admin screens: Maintenance → Reports and Maintenance → Logs.
License
GPL v2 or later. See the plugin header in maintenance-tools-wcus26.php.
Releases
1 release. Each count is every asset in that release; expand a row for the breakdown.