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

9.7 KiB
Raw Blame History

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:

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:

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:

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:

# 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:

# Shape A — defaults nested under `user create:`
user create:
  user_login: someuser
  user_email: someone@example.com
  user_pass: <set>
  role: administrator
# 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:

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.
# 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:

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:

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:

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:

mysql -e 'DROP DATABASE `wp_example`;'

Use the real derived DB name; quote identifiers safely.