Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 28 additions & 0 deletions packages/cli/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3985,6 +3985,9 @@ function recipePolicy(recipe: WorkspaceRecipe): RuntimePolicy {
if (recipeWorkflowSteps(recipe).some(({ step }) => step.command === "wp-codebox.agent-sandbox-run")) {
commands.unshift("wordpress.wp-cli")
}
if (recipeWorkflowSteps(recipe).some(({ step }) => step.command === "wordpress.bench" && recipeBenchStepUsesWpCli(step))) {
commands.unshift("wordpress.wp-cli")
}
if (recipeExtraPlugins(recipe).some((plugin) => plugin.activate !== false)) {
commands.unshift("wordpress.run-php")
}
Expand All @@ -4004,6 +4007,31 @@ function recipePolicy(recipe: WorkspaceRecipe): RuntimePolicy {
}
}

function recipeBenchStepUsesWpCli(step: WorkspaceRecipe["workflow"]["steps"][number]): boolean {
const workloadsArg = (step.args ?? []).find((arg) => arg.startsWith("workloads-json="))
if (!workloadsArg) {
return false
}

try {
return recipeBenchWorkloadsUseWpCli(JSON.parse(workloadsArg.slice("workloads-json=".length)))
} catch {
return false
}
}

function recipeBenchWorkloadsUseWpCli(value: unknown): boolean {
if (Array.isArray(value)) {
return value.some(recipeBenchWorkloadsUseWpCli)
}
if (!value || typeof value !== "object") {
return false
}

const record = value as { type?: unknown; run?: unknown }
return record.type === "wp-cli" || recipeBenchWorkloadsUseWpCli(record.run)
}

function recipeStepUsesEvaluate(step: WorkspaceRecipe["workflow"]["steps"][number]): boolean {
const raw = recipeStepArgValue(step.args ?? [], "steps-json")
if (!raw || raw.startsWith("@")) {
Expand Down
50 changes: 45 additions & 5 deletions packages/runtime-playground/src/commands.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ export interface BenchRunCodeOptions {
dependencySlugs: string[]
env: Record<string, unknown>
workloads: unknown[]
wpCliBridge?: { url: string; token: string }
}

export interface PhpunitRunCodeOptions {
Expand Down Expand Up @@ -1619,6 +1620,8 @@ $warmup_iterations = max(0, (int) ${JSON.stringify(String(options.warmupIteratio
$dependency_slugs = json_decode(${JSON.stringify(JSON.stringify(options.dependencySlugs))}, true);
$bench_env = json_decode(${JSON.stringify(JSON.stringify(options.env))}, true);
$configured_workloads = json_decode(${JSON.stringify(JSON.stringify(options.workloads))}, true);
$wp_cli_bridge_url = ${JSON.stringify(options.wpCliBridge?.url ?? null)};
$wp_cli_bridge_token = ${JSON.stringify(options.wpCliBridge?.token ?? null)};

if (is_array($bench_env)) {
foreach ($bench_env as $name => $value) {
Expand Down Expand Up @@ -1690,6 +1693,47 @@ function wp_codebox_bench_record_payload($payload, array &$metric_samples, ?arra
}
}

function wp_codebox_bench_run_wp_cli_step(array $step) {
global $wp_cli_bridge_url, $wp_cli_bridge_token;
$command = isset($step['command']) && is_string($step['command']) ? trim($step['command']) : '';
if ($command === '') {
throw new RuntimeException('wp-cli bench workload steps require a command.');
}
if (!is_string($wp_cli_bridge_url) || $wp_cli_bridge_url === '' || !is_string($wp_cli_bridge_token) || $wp_cli_bridge_token === '') {
throw new RuntimeException('wordpress.bench wp-cli workload steps require the WP-CLI bridge.');
}

$parse = isset($step['parse']) && is_string($step['parse']) ? $step['parse'] : '';
$response = wp_remote_post($wp_cli_bridge_url . '/execute', array(
'headers' => array(
'authorization' => 'Bearer ' . $wp_cli_bridge_token,
'content-type' => 'application/json',
),
'body' => wp_json_encode(array('type' => 'wp_cli', 'command' => $command), JSON_UNESCAPED_SLASHES),
'timeout' => 300,
));
if (is_wp_error($response)) {
throw new RuntimeException('WP-CLI bench workload bridge request failed: ' . $response->get_error_message());
}
$body = wp_remote_retrieve_body($response);
$result = json_decode($body, true);
if (!is_array($result)) {
throw new RuntimeException('WP-CLI bench workload bridge returned invalid JSON.');
}
if (empty($result['success'])) {
$error = isset($result['error']) && is_string($result['error']) ? $result['error'] : 'WP-CLI command failed';
throw new RuntimeException('WP-CLI bench workload step failed: ' . $command . ' - ' . $error);
}
$stdout = isset($result['stdout']) ? (string) $result['stdout'] : '';
if ($parse === 'json' && $stdout !== '') {
$decoded = json_decode($stdout, true);
if (json_last_error() === JSON_ERROR_NONE) {
return $decoded;
}
}
return $stdout;
}

$plugins_to_activate = array();
foreach (is_array($dependency_slugs) ? $dependency_slugs : array() as $dependency_slug) {
$dependency_slug = sanitize_key((string) $dependency_slug);
Expand Down Expand Up @@ -1755,11 +1799,7 @@ function wp_codebox_bench_run_configured_workload(array $workload, string $plugi
throw new RuntimeException($result->get_error_message());
}
} elseif ($type === 'wp-cli') {
if (!class_exists('WP_CLI')) {
throw new RuntimeException('WP-CLI is not loaded inside wordpress.bench yet.');
}
$command = isset($step['command']) ? (string) $step['command'] : '';
$result = WP_CLI::runcommand($command, array('return' => true, 'launch' => false, 'parse' => 'json'));
$result = wp_codebox_bench_run_wp_cli_step($step);
} else {
throw new RuntimeException('Unsupported bench workload step type: ' . $type);
}
Expand Down
28 changes: 24 additions & 4 deletions packages/runtime-playground/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -373,6 +373,18 @@ function phpLiteral(value: string | number | boolean | null): string {
return String(value)
}

function benchWorkloadsUseWpCli(value: unknown): boolean {
if (Array.isArray(value)) {
return value.some(benchWorkloadsUseWpCli)
}
if (!value || typeof value !== "object") {
return false
}

const record = value as { type?: unknown; run?: unknown }
return record.type === "wp-cli" || benchWorkloadsUseWpCli(record.run)
}

interface PluginCheckArtifact {
targetPlugin: string
files: {
Expand Down Expand Up @@ -1520,10 +1532,18 @@ class PlaygroundRuntime implements Runtime {
const dependencySlugs = commaListArg(args, "dependency-slugs")
const env = jsonObjectArg(args, "env-json")
const workloads = jsonArrayArg(args, "workloads-json")
const response = await this.runPlaygroundCommand("wordpress.bench", server, {
code: this.bootstrapPhpCode(benchRunCode({ componentId, pluginSlug, iterations, warmupIterations, dependencySlugs, env, workloads }), []),
})
assertPlaygroundResponseOk("wordpress.bench", response)
const bridge = benchWorkloadsUseWpCli(workloads) ? await this.createRuntimeWpCliBridge(server) : undefined
let response: PlaygroundRunResponse
try {
response = await this.runPlaygroundCommand("wordpress.bench", server, {
code: this.bootstrapPhpCode(benchRunCode({ componentId, pluginSlug, iterations, warmupIterations, dependencySlugs, env, workloads, wpCliBridge: bridge }), []),
})
assertPlaygroundResponseOk("wordpress.bench", response)
} finally {
if (bridge) {
await bridge.close()
}
}

return promoteBrowserMetricsToBenchResults(response.text, this.browserProbes)
}
Expand Down
10 changes: 8 additions & 2 deletions scripts/recipe-bench-smoke.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,9 +45,14 @@ writeFileSync(recipePath, `${JSON.stringify({
`workloads-json=${JSON.stringify([
{
id: "configured-env",
type: "php",
run: [
{ type: "wp-cli", command: "wp option update wp_codebox_bench_wp_cli yes", parse: "json" },
{
type: "php",
code: "return array('metrics' => array('env_value' => (int) getenv('BENCH_FIXTURE_ENV'), 'define_visible' => defined('BENCH_FIXTURE_DEFINE') && BENCH_FIXTURE_DEFINE === 'defined-value' ? 1 : 0, 'wp_cli_option_visible' => get_option('wp_codebox_bench_wp_cli') === 'yes' ? 1 : 0), 'metadata' => array('kind' => 'configured'));",
},
],
artifacts: { report: { path: "workloads/report.json", kind: "json" } },
code: "return array('metrics' => array('env_value' => (int) getenv('BENCH_FIXTURE_ENV'), 'define_visible' => defined('BENCH_FIXTURE_DEFINE') && BENCH_FIXTURE_DEFINE === 'defined-value' ? 1 : 0), 'metadata' => array('kind' => 'configured'));",
},
])}`,
],
Expand Down Expand Up @@ -88,6 +93,7 @@ assert.equal(configured.id, "configured-env")
assert.equal(configured.source, "config")
assert.equal(configured.metrics.env_value_mean, 13)
assert.equal(configured.metrics.define_visible_mean, 1)
assert.equal(configured.metrics.wp_cli_option_visible_mean, 1)
assert.equal(configured.metadata.kind, "configured")
assert.equal(configured.artifacts.report.path, "workloads/report.json")
assert.ok(output.artifacts?.directory)
Expand Down