govard-wordpress ยท diff

git:20261008.2a8fb9e to git:20261008.206a88e

2 added, 2 removed. Audit A to A.

---
name: govard-wordpress
description: |
This skill should be used when the user asks to "clear WordPress cache", "run wp cli", "run wp commands", "flush rewrite rules", "manage WordPress plugins via govard", "wordpress wp-config", "audit WordPress", "lint WordPress", "govard audit", "deploy WordPress", or "govard deploy". Provides WordPress-specific Govard shortcuts. DEPENDENT on govard-toolbox for base commands.
compatibility: claude, codex, opencode, copilot, dsh
depends: [govard-toolbox, php-dev-core]
metadata:
audience: developers
workflow: wordpress
---
# Govard WordPress Commands
WordPress-specific shortcuts and commands for Govard environments.
## Related Skills
**REQUIRED BACKGROUND:** Load `govard-toolbox` first. This skill only covers WordPress-specific behavior layered on top of Govard's base environment commands (`govard up`, `govard sh`, `govard db`, remote sync, Xdebug setup). For generic PHP (strict_types, PSR-12, PHPStan, security) see `php-dev-core`.
**Docker requirement:** stack commands here need Docker. Check what a command needs with `govard capabilities`. Unlike some frameworks, WordPress has **no** container-free `govard audit run --checks integrity` (it fails with "does not support integrity audit").
## WP-CLI
`wp` runs inside the PHP container. `govard up` installs it when missing.
```bash
govard tool wp --info
govard tool wp core version
govard tool wp plugin list
# Alternative: open a shell, then run wp directly
govard sh
wp --info
```
Most `wp` commands that touch the database (`plugin list`, `theme list`, `cache flush`, `rewrite flush`) fail with "The site you have requested is not installed" until the site is installed. A checkout brought up with `govard up` has `wp-config.php` wired to the Govard database but an empty database, so installing is your step, not a red flag:
```bash
govard tool wp core install --url=https://<project-domain> --title="Local" --admin_user=admin --admin_email=admin@example.com
govard tool wp core is-installed && govard db query "SHOW TABLES"
```
(`govard bootstrap --fresh` runs the install itself.) Read the project's runtime values instead of assuming them:
```bash
govard config get stack.php_version
govard config get stack.db_version
govard config get domain
```
`govard bootstrap --framework-version <v>` is **pinned, not "latest"**: a bare `6` is normalised to `6.0` and that exact archive is downloaded from wordpress.org. Omit the flag to track latest. Bedrock layout (`web/wp/wp-includes/version.php`) is auto-detected (see `govard-toolbox` for provider details).
## Audit
Lint is the supported check: `govard audit run --checks lint --lint-provider govard --mode project --format json` (see `govard-toolbox` ## Audit for the cross-framework matrix). On WordPress:
- `--checks integrity` and `--checks profiler` are **not supported** (exit 1, "framework wordpress does not support ... audit").
- With Xdebug enabled (the default in many `.govard.yml` files) audit refuses to run: pass `--allow-xdebug` or run `govard config set stack.features.xdebug false` first.
- Linting a full WordPress tree can take minutes and exits 1 when it reports findings; scope with `--scope diff --base <ref>` for day-to-day use.
## Cache Management
```bash
# Object cache (needs an installed site; a no-op without an object-cache drop-in)
govard tool wp cache flush
# Transients
govard tool wp transient delete --all
# Caching plugins expose their own wp subcommands; they only work once the plugin is installed
govard tool wp rocket clean --confirm
```
There is no Govard-level `frontend_sync` for WordPress; use `govard tool npm run watch` if the theme has a build step.
## Plugin & Theme Management
```bash
govard tool wp plugin list
govard tool wp theme list
govard tool wp plugin install query-monitor --activate
govard tool wp theme install twentytwentyfour --activate
govard tool wp plugin update --all
govard tool wp theme update --all
```
Must-use plugins live in `wp-content/mu-plugins` and are not managed by `wp plugin`.
## Rewrite & Core
```bash
# After CPT/taxonomy changes. Warns "Rewrite rules are empty" until a permalink structure is set.
govard tool wp rewrite flush
govard tool wp rewrite list
govard tool wp core verify-checksums # exits 1 if any core file was modified
# After a domain change (e.g. staging sync); try --dry-run first
govard tool wp search-replace 'https://staging.example.com' 'https://<project-domain>' --all-tables
```
`wp-config.php` is generated (DB credentials, fresh salts, proxy/HTTPS support) only when it is **missing** at bootstrap time. `govard up` does not rewrite an existing `wp-config.php` or regenerate its salts. For multisite, add `extra_domains` in `.govard.yml` for each site domain, then `govard env up`:
```yaml
extra_domains:
- shop.<project-domain>
```
`govard domain add|remove|list` manages the same list.
## Database
```bash
govard db info # connection details, never the password
govard db query "SHOW TABLES"
govard db query "SELECT option_value FROM wp_options WHERE option_name='siteurl'"
govard db dump --file backup.sql
govard db import --file backup.sql --drop # --drop recreates the database first
govard db dump --no-noise --no-pii -e staging # privacy-filtered dump from a remote
# Same thing through WP-CLI inside the container
govard tool wp db query "SHOW TABLES"
govard tool wp db export --add-drop-table
```
**Privacy filters match by table name, so they need the prefix.** `--no-noise` and `--no-pii` exclude WordPress tables (`users`, `usermeta`, `comments`, `commentmeta` for PII; a few cache/log tables for noise) with the table prefix prepended. Govard reads the literal `$table_prefix` from `wp-config.php`, locally and, for a remote, from the remote's own `wp-config.php` through the credentials probe. It never borrows the local prefix for a remote.
`--no-pii` fails closed: when no table can match (the remote's prefix cannot be read, or `$table_prefix` is built dynamically, for example from `getenv()`), the command is refused instead of writing a dump that still holds `wp_users`. `--no-noise` and `--plan` only warn. Still spot-check a dump before sharing it (`grep -c 'INSERT INTO `wp_users`'` must be 0). `db dump -e <remote> --file <path>` writes `<path>` on the **remote**; add `--local` to stream the dump into the project's `var/` directory.
## Environment
- Do not commit `wp-config.php` secrets.
- For staging/prod overrides use `GOVARD_ENV=staging govard env up` (loads `.govard.staging.yml`).
- Xdebug: `govard debug on|off|status` (`off` rewrites `.govard.yml` and re-runs `env up`).
- `govard shell -c "..."` / `govard sh -c "..."` run one command in the PHP container.
## Deployment
WordPress ships a deploy recipe for the **classic layout only**: core files and `wp-content/` in the repository root. A Bedrock layout (core in `vendor/`, docroot `web/`) and a content-only checkout are not supported.
```bash
govard deploy plan production --branch main # resolved pipeline: no connection, no Docker
govard deploy check production --branch main # preflight over ssh
govard deploy production --branch main --yes
```
The remote needs a branch (`--branch`, `--revision`, `--tag`, or a configured `branch`) for `plan`, and a `deploy_path` (or an existing deploy layout) for `check`/`status`; otherwise they exit `4`. Add `--json` to `plan` for machine-readable output. See `govard deploy --help` for every flag.
| Step | Command on the target |
|---|---|
| `build:vendors` | `composer install ...` **only when** `composer.json` exists |
| `db:migrate` | `wp core update-db`, or `wp_upgrade()` through `wp-load.php` without wp-cli |
| `app:cache:flush` | `wp cache flush` + `wp rewrite flush --hard`, or the PHP equivalents |
| `maintenance:enable` / `disable` | writes/removes `.maintenance` and a marked `wp-content/maintenance.php` in the **served** path |
| `db:backup` / restore | `wp db export` / `wp db import` |
| `deploy:verify` (`app`) | `wp core is-installed`, or `is_blog_installed()` without wp-cli |
Three steps are **hybrids**: `wp` when the target has wp-cli, a `wp-load.php` PHP bootstrap when it does not.
- **Seed `shared/wp-config.php` before the first deploy.** `deploy:shared` links a shared entry only when it already exists, so an unseeded `shared/` leaves the release with the repository's `wp-config.php`, the one naming the development database. The first run fails at `db:migrate` ("Error establishing a database connection"), and the lock and release directory are kept. **Seeding afterwards does not repair that release:** `--resume` skips `deploy:shared` as already done (and `--from` does not re-run it), so link the shared file into the release by hand (`ln -sfn <deploy_path>/shared/wp-config.php <release>/wp-config.php`) before resuming, or start a fresh release. In one resumed run the unlinked release passed `db:migrate` and was **activated** before `app:cache:flush` failed with the same connection error, so an unseeded release can go live.
- `deploy:check` refuses a verify URL that redirects. A database copied from another environment keeps that environment's `siteurl`, so WordPress answers the verify URL with a 301 and the preflight stops. The sandbox seed rewrites `siteurl`/`home` to its own URL; for any other target point `siteurl`/`home` at the target URL (`wp option update`), or set `deploy.verify.follow_redirects: true`.
- Maintenance writes `time() + 86400`, not WordPress's own `time()`: a flag older than ten minutes expires, so a longer window would silently reopen the site mid-migration. The drop-in carries a marker, so a project's own maintenance page is kept. A deploy that fails after `maintenance:enable` leaves the flag and the lock behind; `--resume` (after `deploy unlock --force` if the lock is fresh) finishes the release and removes the flag.
- `--db-backup` uses `wp db export`/`wp db import` when the target has wp-cli, and otherwise a plain `mariadb-dump`/`mysqldump` that reads `wp-config.php` and hands the credentials to the client through a private option file (never on a command line). A target with none of the three is refused up front as a configuration error (exit `4`). Without the flag the `db:backup` row is reported as skipped, with the reason. With it the dump lands in `shared/backups/deploy/<release>/dump.sql`.
- `deploy rollback --with-db` restores the dump recorded by the release after the rollback target, and refuses (exit `1`) when that release has none, for example the newest one.
- `deploy unlock` refuses a fresh lock without `--force`. `deploy --resume` with nothing unfinished starts a new release, and says "already runs ... nothing to do" when the revision is already live (use `--force`).
- In artifact mode the only build step is the guarded `composer install`; the database steps still run on the target.
- Shared: `wp-config.php` (file), `wp-content/uploads` (dir); writable: `wp-content/{uploads,cache,upgrade,languages}`.
### Rehearsing on the sandbox
`govard sandbox up` gives a disposable target with wp-cli, a MySQL client, the usual PHP extensions and `mariadb` + `redis-server`, so a deploy can be rehearsed with `govard deploy --remote sandbox --yes`. The origin environment must be running to seed.
- A seed that fails half way is retried by the next `sandbox up`; the seed creates the deploy tree before it changes ownership, and the readiness, seed and import steps use the MariaDB client names (`mariadb-admin`, `mariadb`, `mariadb-dump`) when the image has no `mysql*` ones.
- The seed copies the database but writes no `wp-config.php` and no shared tree for WordPress. Seed `shared/wp-config.php` yourself (the sandbox database user keeps the origin credentials; set `DB_HOST` to `localhost`), over `ssh -p <port> -i .govard/sandbox/id_ed25519 deployer@127.0.0.1`.
- - `sandbox ssh` is interactive only and takes no command (it forces a tty). Use `govard remote exec sandbox -- <cmd>`, which fails on the dangling `current` until the first deploy and again after `sandbox reset` (reset also wipes `shared/`, so re-seed `wp-config.php`).
+ - `sandbox ssh` is interactive; `sandbox ssh -- <cmd>` runs one command and returns its exit status. `govard remote exec sandbox -- <cmd>` is the deploy-aware alternative, which fails on the dangling `current` until the first deploy and again after `sandbox reset` (reset also wipes `shared/`, so re-seed `wp-config.php`).
- `govard remote exec`, `remote test`, `deploy plan|check|status|releases|rollback|unlock`, `db dump -e sandbox`, `snapshot create|list -e sandbox` and real `sync -s sandbox` (`--db`, `--file --path wp-content/themes`, `--media` which maps to `wp-content/uploads`) all work against it. `sync -s sandbox --db` needs a deployed `wp-config.php` on the target to detect credentials.
- - `snapshot create -e <remote>` writes into `<remote path>/.govard/snapshots/` (mode `0700`), which on a deploy layout is inside the served release, so the next deploy leaves it behind in the previous release directory. `snapshot restore` asks for confirmation; pass `-y` without a TTY.
+ - `snapshot create -e <remote>` stores under `<deploy path>/shared/.govard/snapshots` when the remote has a deploy layout (`releases/`, `shared/`, `current`), so a deploy does not leave it behind; other remotes keep `<remote path>/.govard/snapshots` and get a warning. `list`, `restore`, `delete` and `pull` read both locations. `snapshot restore` asks for confirmation; pass `-y` without a TTY.
- `govard open admin` opens `/wp-admin`, and `open admin -e sandbox` resolves the synthetic remote and uses its published port.
Reference: <https://govard.ddtcorex.com/workflows/deployment#laravel-symfony-and-wordpress> and <https://govard.ddtcorex.com/workflows/deploy-case-studies#case-11-wordpress>.
## Common Workflows
### After Pulling Code
```bash
govard tool composer install # only if the project has composer.json
govard tool wp plugin update --all
govard tool wp rewrite flush
govard tool wp cache flush
```
### Sync from Staging
`--no-pii` refuses to run when the remote's table prefix cannot be read (see Database); without that flag the copy contains real users, so treat it accordingly:
```bash
govard bootstrap --clone -e staging --yes # --yes (or -y) is required non-interactively
# or DB-only
govard sync -s staging --db
govard tool wp search-replace 'https://staging.example.com' 'https://<project-domain>' --all-tables
```
A `bootstrap -e <remote>` run rewrites `siteurl`/`home` to the local domain after the import. Preview any of these with `--plan` first (`govard sync ... --plan`, `govard bootstrap ... --plan`); `sync --db --plan` still probes the remote over ssh for DB credentials. The clone plan lists Magento-only excludes (`app/etc/env.php`, `pub/static`) that mean nothing for WordPress.