48 lines
4.8 KiB
Markdown
48 lines
4.8 KiB
Markdown
# CLAUDE.md
|
|
|
|
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
|
|
|
## Project
|
|
|
|
`@lewebsimple/wpop` (`wpop`) — a Node CLI that deploys WordPress projects from a developer's working tree to a remote host over SSH/rsync. Authored ESM TypeScript, bundled with `tsdown` to `dist/cli.mjs` (the `bin` entry).
|
|
|
|
## Commands
|
|
|
|
- `pnpm dev -- <args>` — run the CLI from source via `tsx` (e.g. `pnpm dev -- deploy --dry-run`)
|
|
- `pnpm build` — bundle to `dist/` with tsdown (ESM, node24 target, emits .d.ts)
|
|
- `pnpm typecheck` — `tsc --noEmit` against `tsconfig.json`
|
|
- `pnpm lint` / `pnpm lint:fix` — oxlint
|
|
- `pnpm format` / `pnpm format:check` — oxfmt
|
|
- `pnpm check` — runs format:check + lint + typecheck (use this before declaring work done; there is no test suite)
|
|
- `pnpm release` — `changelogen --release --push --noAuthors`
|
|
|
|
There are no unit tests. Validate changes by running the CLI with `--dry-run` (every shell-out is gated by `run()` and prints `[dry-run] <cmd>` instead of executing).
|
|
|
|
Package manager is pnpm@10 (declared via `packageManager`); husky + lint-staged run oxfmt/oxlint on staged files at commit time.
|
|
|
|
## Architecture
|
|
|
|
Single command today (`deploy`), but the layout assumes more will be added.
|
|
|
|
- `src/cli.ts` — commander entry. Defines global flags (`--cwd`, `--dry-run`, `--json`, `--yes`, `--verbose`) on the root program and registers subcommands. Each subcommand action calls `createContext(program.opts())` and passes the context as the first argument to its handler. New commands should follow this pattern: register in `cli.ts`, implement in `src/commands/<name>.ts`, take `(context, options)`.
|
|
- `src/lib/context.ts` — `WPopContext` carries the resolved cwd and the global flags. Every side-effecting helper takes a context; nothing reads `process.cwd()` or the flags directly.
|
|
- `src/lib/run.ts` — `run(context, cmd, args, opts)` is the single chokepoint for spawning processes via `execa`. **In dry-run mode it logs and returns without executing.** Any new shell-out must go through `run` (or use `execa` directly only when capturing stdout, and in that case branch on `context.dryRun` like `sshOutput` in `deploy.ts`).
|
|
- `src/lib/env.ts` — Zod schema and resolution for deploy-time env vars (`REMOTE_HOST`, `REMOTE_USER`, `REMOTE_PATH`, `REMOTE_PORT`, `SSH_PRIVATE_KEY`, `WPOP_CACHE_DIR`, `WP_VERSION`, `WP_LOCALE`). Schema is parsed lazily inside the command, not at import. If any `REMOTE_*` value is missing, it can read Gitea Actions variables from the inferred repo/org using `WEBSIMPLE_GITEA_API_TOKEN`, `WPOP_GITEA_TOKEN`, or `GITEA_TOKEN`; repo inference comes from `git remote get-url origin` and can be overridden with `WPOP_GITEA_REPO` or `WPOP_GITEA_OWNER` + `WPOP_GITEA_REPO_NAME`.
|
|
- `src/lib/ssh.ts` — builds `PreparedSsh` from env: optionally writes `SSH_PRIVATE_KEY` to a 0600 tempfile, runs `ssh-keyscan` to populate `~/.ssh/known_hosts`, then verifies access with `ssh -o BatchMode=yes`. `sshArgs`/`sshTarget`/`rsyncSshShell` are used to construct ssh and rsync invocations consistently.
|
|
- `src/commands/deploy.ts` — orchestrates the deploy. Order matters:
|
|
1. Parse `--include` (default `vendor,plugins,themes,mu-plugins`; `all` adds `core`).
|
|
2. Build local artifacts: `composer install --no-dev` (skippable), then per-theme `pnpm/yarn/npm install + build` under `wp-content/themes/*/` (lockfile detection picks the package manager). `node_modules` is removed after each theme build.
|
|
3. **Drift check** — for every content component being deployed, list remote top-level dirs under `wp-content/<component>` and abort if any are absent locally. This guards against `rsync --delete` wiping plugins/themes installed out-of-band on the server.
|
|
4. Rsync, in this order: core (excludes `wp-config.php`, `wp-content/`, `.htaccess`, `.user.ini`, `php.ini`, `robots.txt`, `.well-known/`), `vendor/` + `composer.json/lock`, then each content component. After vendor sync, asserts `vendor/autoload.php` is referenced from remote `wp-config.php`.
|
|
5. Remote DB updates: `wp core update-db`, `wp wc update` if WooCommerce is present, `wp acf json sync` if ACF ≥ 6.8 is installed.
|
|
- Caches (`composer/`, `npm/`, `pnpm/`, `yarn/`) live under `WPOP_CACHE_DIR` (default `/tmp/wpop`) and are wired into the child env so repeated runs reuse downloads.
|
|
|
|
Logging uses `consola`; structured output is opt-in via `--json` (currently only `deploy` emits a one-shot JSON header).
|
|
|
|
## Conventions
|
|
|
|
- ESM only (`"type": "module"`); imports use the `node:` prefix for built-ins.
|
|
- JSON imports use the import attribute syntax (`with { type: "json" }`) — required by the node24 target.
|
|
- Prefer adding to `src/lib/` for shared helpers; commands should stay thin orchestrators that call into lib.
|
|
- `oxlint` runs the `correctness` category as errors with `typescript`/`unicorn`/`oxc` plugins enabled — fix lint issues rather than disabling rules.
|