Files
shopify-ai-backup/EXTERNAL_WP_CLI_TESTING_IMPLEMENTATION.md
2026-02-08 20:15:38 +00:00

399 lines
9.5 KiB
Markdown

# External WP-CLI Testing - Implementation Reference
## Complete Data Flow
### 1. User Enables Toggle in Builder
**File:** `chat/public/builder.js`
```javascript
// Line ~36
builderState.externalTestingEnabled = false // default
// User clicks toggle → sets to true
builderState.externalTestingEnabled = true
// When sending message (line ~3967)
const payload = {
content: messageContent,
model,
cli,
externalTestingEnabled: !!builderState.externalTestingEnabled
}
```
---
### 2. Chat Server Receives Message
**File:** `chat/server.js`
```javascript
// Line ~8804
const wpCliMcpEnabled = message?.externalTestingEnabled === true
// Line ~8885-8901: If enabled, inject MCP server
if (wpCliMcpEnabled) {
const wpMcpServerPath = path.resolve(__dirname, '../opencode/mcp-servers/wp-cli-testing/index.js')
executionEnv.OPENCODE_EXTRA_MCP_SERVERS = JSON.stringify([
{
name: 'wp-cli-testing',
command: 'node',
args: [wpMcpServerPath],
disabled: false
}
])
} else {
delete executionEnv.OPENCODE_EXTRA_MCP_SERVERS // Safety gate
}
// Pass env to OpenCode
await opencodeManager.executeInSession(..., { env: executionEnv })
```
---
### 3. OpenCode Reads Environment Variable
**File:** `opencode/packages/opencode/src/config/config.ts`
```typescript
// Line ~233-294: Parse OPENCODE_EXTRA_MCP_SERVERS
const extraMcpRaw = process.env.OPENCODE_EXTRA_MCP_SERVERS
if (extraMcpRaw && typeof extraMcpRaw === "string") {
const parsed = JSON.parse(extraMcpRaw)
if (Array.isArray(parsed)) {
result.mcp ??= {}
for (const entry of parsed) {
result.mcp[entry.name] = {
type: "local",
command: [...commandParts, ...argsParts],
env: envRecord,
enabled: true,
...
}
}
}
}
```
Result: OpenCode config now has `mcp['wp-cli-testing']` configured
---
### 4. OpenCode Starts MCP Server
**File:** `opencode/packages/opencode/src/mcp/index.ts`
OpenCode's MCP system automatically:
1. Reads `config.mcp['wp-cli-testing']`
2. Spawns: `node opencode/mcp-servers/wp-cli-testing/index.js`
3. Connects via stdio transport
4. Calls `listTools()` to get available tools
---
### 5. MCP Server Registers Tools
**File:** `opencode/mcp-servers/wp-cli-testing/index.js`
```javascript
server.setRequestHandler(ListToolsRequestSchema, async () => {
return {
tools: [
{
name: "test_plugin_external_wp",
description: "Run WP-CLI based verification...",
inputSchema: { ... }
},
{
name: "external_wp_testing_config",
description: "Return resolved config...",
inputSchema: { ... }
}
]
}
})
```
---
### 6. AI Invokes test_plugin_external_wp Tool
**File:** `opencode/packages/opencode/src/session/prompt.ts`
OpenCode's session handler:
1. Tool calls go through `resolveTools()` (line ~668)
2. MCP tools added via `MCP.tools()` (line ~745)
3. AI invokes tool with parameters
```javascript
test_plugin_external_wp({
plugin_path: "/workspace/my-plugin",
plugin_slug: "my-plugin",
test_scenarios: [...]
})
```
---
### 7. MCP Server Handles Tool Call
**File:** `opencode/mcp-servers/wp-cli-testing/index.js`
```javascript
server.setRequestHandler(CallToolRequestSchema, async (req) => {
const toolName = req.params.name
const args = req.params.arguments
// Import external-wp-testing module
const externalTesting = await import("../../../chat/external-wp-testing.js")
const mod = externalTesting.default ?? externalTesting
const tester = mod.createExternalWpTester({})
const result = await tester.runTest(
{
plugin_path: args.plugin_path,
plugin_slug: args.plugin_slug,
...
},
{ configOverrides: {} }
)
return { content: [{ type: "text", text: JSON.stringify(result) }] }
})
```
---
### 8. External WP Testing Executes
**File:** `chat/external-wp-testing.js`
```javascript
async function runTest(input, opts) {
const config = getExternalTestingConfig(opts.configOverrides)
// Queue the test
return queue.enqueue(async () => {
// 1. Create subsite (if multisite enabled)
if (config.enableMultisite) {
subsite = await createSubsite(config, sessionId)
// Runs: wp site create --slug=test-abc123
}
// 2. Upload plugin via SCP
if (pluginPath) {
await installPluginFromLocal(config, pluginPath, pluginSlug, sessionId)
}
// 3. Install required plugins
const installedPlugins = await installRequiredPlugins(config, subsite.url, requiredPlugins)
// 4. Activate test plugin
if (pluginSlug) {
await runSshCommand(config,
buildWpCliCommand(config, subsite.url, `plugin activate ${pluginSlug}`)
)
}
// 5. Run test scenarios
const cliTests = await runTestScenarios(config, subsite.url, scenarios)
// 6. Schedule cleanup
if (config.autoCleanup && subsite.slug) {
setTimeout(() => { deleteSubsite(config, subsite.slug) }, config.cleanupDelayMs)
}
return { ok: true, test_results: { cli_tests: cliTests }, ... }
})
}
```
---
### 9. SSH Commands Execute on WordPress Server
**Functions:**
- `buildWpCliCommand(config, url, command)``wp --path=/var/www/html --url=https://site.com/test-abc command`
- `runSshCommand(config, command)` → Executes via SSH with key auth
**Example Commands Executed:**
```bash
# Create subsite
ssh -i ~/.ssh/wp-key wordpress@wp-test.com "wp --path=/var/www/html site create --slug=test-abc123"
# Copy plugin
scp -i ~/.ssh/wp-key -r /local/plugin wordpress@wp-test.com:/var/www/html/wp-content/plugins/
# Activate plugin
ssh -i ~/.ssh/wp-key wordpress@wp-test.com "wp --path=/var/www/html --url=https://wp-test.com/test-abc123 plugin activate my-plugin"
# Run test
ssh -i ~/.ssh/wp-key wordpress@wp-test.com "wp --path=/var/www/html --url=https://wp-test.com/test-abc123 plugin status my-plugin"
# Later: Delete subsite
ssh -i ~/.ssh/wp-key wordpress@wp-test.com "wp --path=/var/www/html site delete --slug=test-abc123 --yes"
```
---
### 10. Results Return to AI
**Return Path:**
```
WordPress Server (via SSH)
↓ command output
external-wp-testing.js runTest()
↓ formatted result
MCP Server CallToolRequestSchema handler
↓ JSON response
OpenCode MCP client
↓ tool result
AI Session Handler
↓ assistant message
Chat Server
↓ SSE stream
Builder UI
```
**Result Format:**
```json
{
"ok": true,
"session_id": "abc123",
"subsite_url": "https://wp-test.example.com/test-abc123",
"test_results": {
"mode": "cli",
"cli_tests": {
"passed": 3,
"failed": 0,
"results": [...]
}
},
"installed_plugins": [...],
"duration": 15230,
"cleanup_scheduled": "2026-02-08T11:30:00Z"
}
```
---
## Key Files
| File | Purpose |
|------|---------|
| `chat/public/builder.js` | Builder UI toggle state |
| `chat/server.js` | Gate MCP injection via env var |
| `opencode/packages/opencode/src/config/config.ts` | Parse OPENCODE_EXTRA_MCP_SERVERS |
| `opencode/packages/opencode/src/mcp/index.ts` | Load and connect MCP servers |
| `opencode/mcp-servers/wp-cli-testing/index.js` | MCP server implementation |
| `chat/external-wp-testing.js` | Core testing logic |
---
## Environment Variables Flow
```
Host Environment
Chat Server process.env
executionEnv = {...process.env, OPENCODE_EXTRA_MCP_SERVERS: "..."}
OpenCode spawned with executionEnv
process.env.TEST_WP_HOST → external-wp-testing.js getExternalTestingConfig()
SSH connection using TEST_WP_SSH_KEY
Commands execute on WordPress server
```
**Required Environment Variables:**
- `TEST_WP_HOST`
- `TEST_WP_SSH_USER`
- `TEST_WP_SSH_KEY`
- `TEST_WP_PATH`
- `TEST_WP_BASE_URL`
- etc. (see EXTERNAL_WP_CLI_TESTING_SETUP.md)
---
## Security Gates
1. **Builder Toggle** - User must explicitly enable
2. **Environment Variable Gate** - Only set when toggle is on
3. **MCP Loading** - Only loads when env var present
4. **SSH Key Auth** - Requires valid key file
5. **Tool Gating** - Tools not available if toggle off
When toggle is OFF:
- `externalTestingEnabled: false` in payload
- `wpCliMcpEnabled = false` on server
- `OPENCODE_EXTRA_MCP_SERVERS` is deleted from env
- MCP server never loads
- Tools never available to AI
---
## Testing The Flow
### 1. Check Environment
```bash
echo $TEST_WP_HOST
echo $TEST_WP_SSH_KEY
```
### 2. Test SSH Connection
```bash
ssh -i $TEST_WP_SSH_KEY $TEST_WP_SSH_USER@$TEST_WP_HOST "wp site list"
```
### 3. Enable Toggle in Builder
- Open Builder
- Enable "External WP CLI testing"
- Verify toggle shows enabled state
### 4. Check Server Logs
```bash
tail -f logs/chat-server.log | grep "WP CLI Testing"
# Should see: "Enabling WP CLI Testing MCP server"
```
### 5. Ask AI to Test
```
"Create a plugin and test it on the external WordPress server"
```
### 6. Verify Subsite Created
```bash
ssh -i $TEST_WP_SSH_KEY $TEST_WP_SSH_USER@$TEST_WP_HOST "wp site list | grep test-"
```
---
## Multisite Architecture
```
WordPress Multisite Network
├── Main Site (/)
│ └── Core WP, Plugins, Themes (shared)
├── Subsite: /test-abc123 (Session 1)
│ ├── Isolated content/options
│ ├── Test Plugin A activated
│ └── Tests running
├── Subsite: /test-def456 (Session 2)
│ ├── Isolated content/options
│ ├── Test Plugin B activated
│ └── Tests running
└── Subsite: /test-xyz789 (Session 3)
└── ...
```
**Concurrency:** 20+ simultaneous tests via queue system + isolated subsites
**Cleanup:** Each subsite auto-deleted after 1 hour (configurable via `TEST_CLEANUP_DELAY`)