Files
claude-websimple-devops/references/wp-local-site.md

215 lines
9.7 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# WP Local Site
Use this skill for local WordPress site lifecycle operations. For project naming and derived values, use `references/wp-project.md`. For stack/host environment assumptions, use `references/websimple-stack.md`. For Composer-managed plugins/themes, use `references/wp-composer.md`.
## Derived values
For project slug `${slug}`, use `references/wp-project.md` conventions:
- Local project path: `${WP_LOCAL_ROOT_PATH}/${slug}`
- Local database name: `wp_${slug}`
- Local site URL: `${WEBSIMPLE_STACK_PROTOCOL}://${slug}.${WEBSIMPLE_STACK_DOMAIN}`
- Default local DB table prefix: `wp_`, unless the production/source instance uses a different prefix
- Repository URL: `https://gitea.websimple.com/wp-sites/${slug}`
Resolve project values with `references/wp-project.md`; do not hard-code local paths, protocol, or domain.
## Safety
- Prefer read-only checks before creating, deleting, or resetting anything.
- Ask before destructive actions such as dropping a database, deleting a project directory, or overwriting local files.
- Use recoverable deletion where practical. If shelling out, prefer moving to the user's trash over permanent removal.
- Never delete source-controlled project-specific plugins/themes as part of Composer repair; use the `references/wp-composer.md` integrity guidance.
- Treat remote hosts as read-only unless a future approved workflow says otherwise.
## Create local site skeleton
A local site creation workflow should generally:
1. Resolve `${slug}` and derived values with `references/wp-project.md`.
2. Verify `${WP_LOCAL_ROOT_PATH}` exists.
3. Verify `${WP_LOCAL_ROOT_PATH}/${slug}` does not already exist, or ask how to proceed.
4. Clone or create the project directory from `https://gitea.websimple.com/wp-sites/${slug}`.
5. Create the local database `wp_${slug}` using host `mysql` credentials from `~/.my.cnf`.
6. Determine the DB table prefix: use `wp_` by default, or the production/source prefix when provisioning from an existing site.
7. Run `wp core config` with only the database name and table prefix when WP-CLI defaults provide DB host/user/password.
8. Run Composer install if the project uses Composer.
9. Either run `wp core install` for a fresh local site or synchronize/import from an existing remote site.
10. Verify local URL and WP-CLI access.
Example database creation command:
```bash
mysql -e 'CREATE DATABASE `wp_example` DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;'
```
Use the real derived DB name; quote identifiers safely.
## WP core config
The host should have WP-CLI defaults in `~/.wp-cli/config.yml` for DB host, user, and password. Local provisioning should normally only specify the DB name:
```bash
wp core config --dbname="wp_${slug}" --dbprefix="wp_"
```
Run from the local project path. Do not duplicate DB host/user/password flags unless defaults are missing or the user explicitly asks.
Use `wp_` as the default table prefix for new local sites. When provisioning from production/staging, detect and preserve the source table prefix if it differs from `wp_`; the imported database and local `wp-config.php` prefix must agree.
## WP core install
Use `wp core install` only when creating a fresh local site without importing an existing database. Do not run it before a remote DB import because the import will replace the local tables anyway.
Example for a new local site:
```bash
wp core install \
--url="${WEBSIMPLE_STACK_PROTOCOL}://${slug}.${WEBSIMPLE_STACK_DOMAIN}" \
--title="${slug}" \
--skip-email
```
`~/.wp-cli/config.yml` contains default admin user, email, and password values, so do not pass `--admin_user`, `--admin_email`, or `--admin_password` unless the user asks for custom values. Avoid inventing real production credentials.
## Standard admin user
Local sites should have the standard admin user from `~/.wp-cli/config.yml`. After installing or importing a site, check whether that user exists. If missing, create it using the configured default admin values.
Do not hard-code admin credentials in commands. Do not echo the password in chat or log summaries. Refer to credentials by variable name only.
The exact YAML shape varies by user setup. Inspect the file before constructing any command, so the keys you read match the keys that actually exist:
```bash
# Inspect — show keys only, not the password value
grep -E '^[[:space:]]*(user_login|user_email|user_pass|role)' ~/.wp-cli/config.yml \
| sed 's/\(user_pass:\).*/\1 <redacted>/'
```
Two common shapes:
```yaml
# Shape A — defaults nested under `user create:`
user create:
user_login: someuser
user_email: someone@example.com
user_pass: <set>
role: administrator
```
```yaml
# Shape B — flat top-level keys consumed by custom commands
admin_user: someuser
admin_email: someone@example.com
admin_password: <set>
```
If the file uses Shape A, WP-CLI will already apply the defaults when `wp user create` runs without explicit flags. If it uses Shape B, the values have to be passed explicitly.
When `yq` is available, prefer it over hand-parsing:
```bash
ADMIN_USER="$(yq '.["user create"].user_login // .admin_user' ~/.wp-cli/config.yml)"
ADMIN_EMAIL="$(yq '.["user create"].user_email // .admin_email' ~/.wp-cli/config.yml)"
# Keep ADMIN_PASSWORD only in the subshell that needs it; do not export or print it.
```
If `yq` is not available and the YAML doesn't match a known shape, ask the user for the admin login and email rather than guessing — and have them provide the password via stdin (`read -s`) instead of an argument that would land in shell history.
Typical flow:
1. Inspect `~/.wp-cli/config.yml` and identify the admin user/email key names actually used.
2. Check whether that user exists locally.
3. Create the user only if missing.
4. Grant the administrator role if needed.
```bash
# Step 24 — assuming ADMIN_USER, ADMIN_EMAIL, ADMIN_PASSWORD are set in the current shell
if ! wp user get "$ADMIN_USER" >/dev/null 2>&1; then
wp user create "$ADMIN_USER" "$ADMIN_EMAIL" \
--role=administrator \
--user_pass="$ADMIN_PASSWORD"
fi
wp user set-role "$ADMIN_USER" administrator
```
If `wp user create` works without the explicit `--user_pass` flag (Shape A in use), prefer that form so the password never appears on the command line.
## Synchronize from remote site
Synchronizing from a remote site means pulling production/staging state into the local site. Keep remote actions read-only: export/read/copy from remote is okay; writing to or mutating remote is not.
Typical flow:
1. Resolve local metadata with `references/wp-project.md` and remote metadata with `references/websimple-gitea.md` Actions variables, especially `WP_SITE_URL`.
2. Confirm source remote URL/host and destination local path/database.
3. Create/reset the local database only after confirmation.
4. Detect the remote/source DB table prefix before export/import. Use `wp_` only if the source also uses `wp_` or the source prefix is unknown and no imported DB is involved.
5. Export the remote database in a read-only way, preferably streaming to local instead of leaving files on the remote host.
6. Import into local DB.
7. Ensure local `wp-config.php` uses the same table prefix as the imported DB.
8. Search-replace remote URLs to the derived local URL.
9. Pull uploads/media from remote unless specified otherwise.
10. Verify `siteurl` and `home` locally.
Preferred DB sync path: stream a compressed remote DB dump over SSH into the local MySQL client. Use this when both remote WP-CLI and local `mysql` are available:
```bash
ssh "$REMOTE_SSH" 'cd "$REMOTE_PATH" && wp db export - --skip-plugins --skip-themes --single-transaction --quick --lock-tables=false | gzip -c' \
| gunzip \
| mysql "$LOCAL_DB_NAME"
```
After DB import, run URL replacement locally:
```bash
wp search-replace "$REMOTE_SITE_URL" "${WEBSIMPLE_STACK_PROTOCOL}://${slug}.${WEBSIMPLE_STACK_DOMAIN}" --all-tables --skip-columns=guid
wp option get siteurl
wp option get home
```
Do not run `wp search-replace` on the remote site during local synchronization.
Preferred uploads sync path: use `rsync` over SSH and include `--delete` so local `wp-content/uploads/` matches remote exactly, unless the user asks to preserve extra local files. Websimple SSH usually requires an explicit port, so include the SSH transport option when needed:
```bash
rsync -az --delete -e "ssh -p $REMOTE_SSH_PORT" "$REMOTE_SSH:$REMOTE_PATH/wp-content/uploads/" "wp-content/uploads/"
```
If the SSH host alias already encodes the port in `~/.ssh/config`, the `-e "ssh -p ..."` option may be omitted. If remote uploads path differs, inspect the project before syncing. Be explicit when `--delete` will remove local-only upload files.
## Provision from existing project
When provisioning from an existing local or remote project:
1. Resolve local and remote metadata using `references/wp-project.md` and `references/websimple-gitea.md`.
2. Confirm the source and destination clearly.
3. Keep remote actions read-only.
4. Import database/content only after confirming the target local DB and path.
5. After import, replace remote URLs with the derived local site URL.
6. Run small verification checks, such as `wp option get siteurl` and `wp option get home`.
## Delete/reset local site
A local delete/reset workflow may include:
- local project directory `${WP_LOCAL_ROOT_PATH}/${slug}`
- local database `wp_${slug}`
- generated/cache artifacts
Before deleting:
1. Show the resolved path, DB name, and local URL.
2. Check whether the path exists and whether it has uncommitted Git changes.
3. Ask for confirmation.
4. Prefer backup/trash for files where practical.
5. Drop the local DB only after explicit confirmation.
Example DB drop command after confirmation:
```bash
mysql -e 'DROP DATABASE `wp_example`;'
```
Use the real derived DB name; quote identifiers safely.