# Deploy notes

## Local development

Docker is available, so local development uses `wp-env` (`@wordpress/env`), configured
in `.wp-env.json` at the repo root (gitignored, machine-local).

```bash
npx @wordpress/env start
```

This brings up WordPress with WooCommerce active on `http://localhost:8888`
(admin at `/wp-admin`, user `admin`, pass `password` unless changed), and a
second instance for tests on `http://localhost:8889`.

```bash
npx @wordpress/env stop
npx @wordpress/env destroy   # tears down the containers and volumes
npx @wordpress/env run cli <wp-cli-command>
```

If Docker is ever unavailable on this machine, fall back to LocalWP
(localwp.com): create a new site on PHP 8.2 with MySQL 8, install WooCommerce,
then symlink or copy `wp-content/themes/brand-theme` and
`wp-content/plugins/brand-commerce` into the LocalWP site's `wp-content`.

### Known issue on this machine: `wp-env start` exits early

On this machine, the `wp-env` Node CLI reliably exits with code 0 right after
the `mysql` container starts, without going on to build the `wordpress`
image, install WordPress, or extract plugin/theme zips. The cause was not
tracked down (the underlying `docker compose` calls work fine on their own),
so if `npx wp-env start` returns almost instantly and `http://localhost:8888`
is not reachable, drive `docker compose` directly against the generated
compose file instead:

```bash
WPENV_DIR=~/.wp-env/<hash-shown-in-wp-env-debug-output>
cd "$WPENV_DIR"

# build and start the site container (mysql is usually already up)
WP_ENV_PORT=8888 docker compose -f docker-compose.yml up -d --build wordpress

# build the wp-cli container
docker compose -f docker-compose.yml build cli

# if a remote zip source (e.g. WooCommerce) is empty, wp-env never got to
# extract it, unzip it into place by hand:
unzip -q woocommerce.latest-stable.zip -d /tmp/wc-extract
cp -R /tmp/wc-extract/woocommerce/. woocommerce.latest-stable/
rm -rf /tmp/wc-extract

# install WordPress
docker compose -f docker-compose.yml run --rm cli wp core install \
  --url="http://localhost:8888" --title="Bedsheets Store" \
  --admin_user=admin --admin_password=password \
  --admin_email=admin@example.com --skip-email

docker compose -f docker-compose.yml run --rm cli wp plugin activate woocommerce.latest-stable brand-commerce
docker compose -f docker-compose.yml run --rm cli wp theme activate brand-theme
```

The `<hash>` is printed at the top of `npx wp-env start --debug` output
(`workDirectoryPath`), or find it with
`ls ~/.wp-env` (there is usually only one entry).

### Table prefix and HPOS, set once per fresh environment

`wp-env`'s own config has no field for a non-default table prefix, and the
official WordPress Docker image reads it from a `WORDPRESS_TABLE_PREFIX`
environment variable at container boot. Because that variable is not part of
`.wp-env.json`, it has to be added by hand to the generated
`$WPENV_DIR/docker-compose.yml`, under **both** the `wordpress` service's and
the `cli` service's `environment:` block (the wp-cli container resolves
`wp-config.php` independently and needs the same value):

```yaml
WORDPRESS_TABLE_PREFIX: unxx_
```

Then, before running `wp core install` (or after, followed by
`wp db reset --yes` and reinstalling), the tables come up under that prefix.
This edit lives in the machine-local `~/.wp-env` cache, not in this repo, so
it is lost on `wp-env destroy` and needs to be reapplied on the next fresh
environment. The current local environment uses prefix `unxx_`, confirmed
against the database, see `docs/PROGRESS.md`.

HPOS (`custom_order_tables`) is off by default on a fresh WooCommerce
install, even though the plugin declares compatibility with it. Turn it on
once per fresh environment:

```bash
docker compose -f docker-compose.yml run --rm cli wp eval '
$c = wc_get_container()->get( \Automattic\WooCommerce\Internal\Features\FeaturesController::class );
$c->change_feature_enable( "custom_order_tables", true );
'
```

None of this applies to the real Namecheap hosting install: there, the table
prefix is typed once into the Softaculous/manual installer form at install
time (see phase 00 item 11, do it right there, never after), and HPOS is
turned on the same way through WooCommerce, Settings, Advanced, Features in
wp-admin.

## Production deployment, Namecheap shared hosting

Primary path: cPanel Git Version Control, driven by `.cpanel.yml` at the repo
root. cPanel's git has no npm and no composer, which is why `assets/dist/` is
committed to the repo, built assets are never compiled on the server.

1. In cPanel, Git Version Control, create a repository pointing at this repo's
   remote, with the deployment path set so `.cpanel.yml` can resolve
   `$DEPLOYPATH` correctly.
2. Replace the `USER` placeholder in `.cpanel.yml` with the real cPanel
   username once the hosting account exists.
3. Every deploy: push to the branch cPanel tracks, then trigger "Update from
   Remote" and "Deploy HEAD Commit" in the Git Version Control UI. This runs
   `.cpanel.yml`, which copies only the theme and plugin directories into
   `wp-content`. Nothing else on the server is touched.

Fallback: scripted SFTP push of the two tracked directories only, never a
full-site upload. Emergency: zip upload via cPanel File Manager.

## Staging subdomain: NOT YET PROVISIONED

Phase 00 scope calls for a `staging.` subdomain to exist on the Namecheap
account from day one (decision D-24), because phases 12 to 14 verify caching,
rewrite rules, email deliverability and backup restores against a real
LiteSpeed host, none of which can be verified against a local Docker
container.

This step needs the Namecheap hosting account's own cPanel login, so it was
not done as part of this session. When you're ready, in cPanel:

1. **Domains > Create A New Domain**, subdomain `staging`, document root
   e.g. `staging.<domain>` under its own folder (not inside `public_html`).
2. **MySQL Databases**, create a dedicated database and user for staging,
   distinct from production.
3. Install WordPress into the staging document root (WP Toolkit or the
   Softaculous installer), matching the production PHP version (8.2) and a
   random table prefix (never `wp_`, see `CLAUDE.md` and phase 00 item 11).
4. In staging's `wp-config.php`, set:
   ```php
   define( 'WP_ENVIRONMENT_TYPE', 'staging' );
   ```
5. **Security > Password Protect Directories**, lock the staging document
   root behind a cPanel HTTP auth prompt.
6. Force `noindex` on staging: **Settings > Reading > Discourage search
   engines**, and additionally add `X-Robots-Tag: noindex, nofollow` at the
   server or `.htaccess` level as a second layer, because plugins can
   silently override the Settings checkbox.
7. Set every payment gateway to test/sandbox mode on staging. Never let
   staging touch a live payment credential.
8. Set up Git Version Control on staging too, pointed at the same repo, so
   `.cpanel.yml` deploys there the same way it deploys to production.

Once this is done, record the staging URL and the cPanel username in
`docs/PROGRESS.md` (not in this repo's tracked docs if it contains any
credential-adjacent detail, keep credentials in the client's password
manager, never in git).
