Skip to content

Commit ba72370

Browse files
committed
Probe canonical lifecycle boundaries
1 parent 2be2d25 commit ba72370

3 files changed

Lines changed: 85 additions & 2 deletions

File tree

packages/runtime-cloudflare/src/worker.ts

Lines changed: 46 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -566,6 +566,16 @@ async function runBootProbe(phase: string, bucket: R2Bucket): Promise<Response>
566566
}
567567
}
568568

569+
if (["canonical-current-user", "canonical-init", "canonical-site-status", "canonical-wp-loaded"].includes(phase)) {
570+
const runtime = await bootWordPressRuntime("do-not-attempt-installing", true, true, undefined, await packagedCanonicalMarkdownSeed(), new Uint8Array(markdownPrimaryBootstrapIndex), "https://canonical-probe.invalid", {}, bucket, true)
571+
try {
572+
const evidence = (await runtime.php.run({ code: canonicalLifecycleProbeCode(phase) })).text.trim()
573+
return probeResponse(phase, JSON.parse(evidence) as Record<string, boolean | number | string>)
574+
} finally {
575+
runtime.php.exit()
576+
}
577+
}
578+
569579
if (phase === "canonical-wordpress" || phase === "canonical-bootstrap-setup") {
570580
const canonicalSeed = await packagedCanonicalMarkdownSeed()
571581
const runtime = await bootWordPressRuntime("do-not-attempt-installing", true, true, undefined, canonicalSeed, new Uint8Array(markdownPrimaryBootstrapIndex), "https://canonical-probe.invalid", {}, bucket, true)
@@ -993,6 +1003,41 @@ file_put_contents($settings_path, str_replace($needle, $replacement, $settings))
9931003
require '/wordpress/wp-load.php';`
9941004
}
9951005

1006+
function canonicalLifecycleProbeCode(phase: string): string {
1007+
const stops: Record<string, { needle: string; after?: boolean }> = {
1008+
"canonical-current-user": { needle: "// Set up current user." },
1009+
"canonical-init": { needle: "do_action( 'init' );", after: true },
1010+
"canonical-site-status": { needle: "// Check site status." },
1011+
"canonical-wp-loaded": { needle: "do_action( 'wp_loaded' );", after: true },
1012+
}
1013+
const stop = stops[phase]
1014+
if (!stop) throw new Error(`Unknown canonical lifecycle probe phase: ${phase}`)
1015+
1016+
return `<?php
1017+
$settings_path = '/wordpress/wp-settings.php';
1018+
$settings = file_get_contents($settings_path);
1019+
$needle = ${JSON.stringify(stop.needle)};
1020+
if (substr_count($settings, $needle) !== 1) {
1021+
throw new Exception('WordPress canonical lifecycle probe needle was not uniquely found.');
1022+
}
1023+
$stop = <<<'PHP'
1024+
echo json_encode(array(
1025+
'wordpressVersion' => $wp_version,
1026+
'bootstrapPhase' => '${phase}',
1027+
'completed' => true,
1028+
'memoryBytes' => memory_get_usage(true),
1029+
'peakMemoryBytes' => memory_get_peak_usage(true),
1030+
'wpCronInitAttached' => false !== has_action('init', 'wp_cron'),
1031+
'updateScheduleInitAttached' => false !== has_action('init', 'wp_schedule_update_checks'),
1032+
'privacyScheduleInitAttached' => false !== has_action('init', 'wp_schedule_delete_old_privacy_export_files'),
1033+
));
1034+
return;
1035+
PHP;
1036+
$replacement = ${stop.after ? "$needle . \"\\n\" . \$stop" : "$stop . \"\\n\" . $needle"};
1037+
file_put_contents($settings_path, str_replace($needle, $replacement, $settings));
1038+
require '/wordpress/wp-load.php';`
1039+
}
1040+
9961041
async function bootWordPressRuntime(
9971042
wordpressInstallMode: WordPressInstallMode = "install-from-existing-files",
9981043
includeSqlite = true,
@@ -1195,6 +1240,6 @@ function instantiatePrecompiledWasm(module: WebAssembly.Module) {
11951240
return (imports: WebAssembly.Imports, receiveInstance: (instance: WebAssembly.Instance, wasmModule: WebAssembly.Module) => void) => receiveInstance(new WebAssembly.Instance(module, imports), module)
11961241
}
11971242

1198-
function probeResponse(phase: string, evidence: Record<string, number | string>): Response {
1243+
function probeResponse(phase: string, evidence: Record<string, boolean | number | string>): Response {
11991244
return Response.json({ schema: "wp-codebox/cloudflare-boot-probe/v1", phase, completed: true, evidence })
12001245
}

scripts/cloudflare-local-gate.mjs

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -171,6 +171,14 @@ async function assertHealthResponse() {
171171
async function assertCanonicalProbes() {
172172
const wordpress = await (await request(`${origin}/?phase=canonical-wordpress`)).json()
173173
const setup = await (await request(`${origin}/?phase=canonical-bootstrap-setup`)).json()
174+
const lifecyclePhases = ["canonical-current-user", "canonical-init", "canonical-site-status", "canonical-wp-loaded"]
175+
const lifecycle = await Promise.all(lifecyclePhases.map(async (phase) => {
176+
const response = await request(`${origin}/?phase=${phase}`)
177+
const body = await response.text()
178+
assertNoPhpDiagnostics(body, phase)
179+
if (!response.ok) throw new Error(`${phase} failed with ${response.status}: ${body}`)
180+
return JSON.parse(body)
181+
}))
174182
const counts = ["canonicalSeedFiles", "postCount", "pageCount", "userCount", "optionCount", "widgetOptionCount", "widgetStateOptionCount", "memoryBytes", "peakMemoryBytes"]
175183
if (wordpress.completed !== true || typeof wordpress.evidence?.wordpressVersion !== "string" || counts.some((key) => !Number.isInteger(wordpress.evidence?.[key])) || wordpress.evidence.widgetStateOptionCount !== 19 || wordpress.evidence.wpCronInitAttached !== false || wordpress.evidence.updateScheduleInitAttached !== false || wordpress.evidence.privacyScheduleInitAttached !== false) {
176184
throw new Error(`Canonical WordPress probe did not boot the complete seed: ${JSON.stringify(wordpress)}`)
@@ -179,7 +187,14 @@ async function assertCanonicalProbes() {
179187
if (setup.completed !== true || typeof setup.evidence?.wordpressVersion !== "string" || changedCounts.some((key) => !Number.isInteger(setup.evidence?.[key])) || setup.evidence.changedPathCount < 1) {
180188
throw new Error(`Canonical bootstrap setup probe did not produce ephemeral changes: ${JSON.stringify(setup)}`)
181189
}
182-
console.log(`Canonical probes: WordPress ${wordpress.evidence.wordpressVersion}, disabled-cron scheduling callbacks absent at init, ${wordpress.evidence.widgetOptionCount} widget_* options plus sidebars_widgets, ${setup.evidence.changedPathCount} changed ephemeral paths.`)
190+
for (const probe of lifecycle) {
191+
const evidence = probe.evidence
192+
const schedulingAttached = probe.phase === "canonical-current-user"
193+
if (probe.completed !== true || !lifecyclePhases.includes(probe.phase) || evidence?.bootstrapPhase !== probe.phase || typeof evidence?.wordpressVersion !== "string" || !Number.isInteger(evidence?.memoryBytes) || !Number.isInteger(evidence?.peakMemoryBytes) || evidence?.wpCronInitAttached !== schedulingAttached || evidence?.updateScheduleInitAttached !== schedulingAttached || evidence?.privacyScheduleInitAttached !== schedulingAttached) {
194+
throw new Error(`Canonical lifecycle probe did not stop at its bounded phase: ${JSON.stringify(probe)}`)
195+
}
196+
}
197+
console.log(`Canonical probes: WordPress ${wordpress.evidence.wordpressVersion}, disabled-cron scheduling callbacks absent at init, ${lifecyclePhases.join(", ")} completed, ${wordpress.evidence.widgetOptionCount} widget_* options plus sidebars_widgets, ${setup.evidence.changedPathCount} changed ephemeral paths.`)
183198
}
184199

185200
async function assertMdiDiagnostics() {

tests/cloudflare-runtime.test.ts

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -313,6 +313,29 @@ test("Cloudflare MDI lifecycle diagnostics use the complete packaged seed withou
313313
assert.match(worker, /function canonicalChangedPathCounts/)
314314
})
315315

316+
test("Cloudflare canonical lifecycle diagnostics compose the disabled-cron patch with unique bounded stops", async () => {
317+
const worker = await readFile(new URL("../packages/runtime-cloudflare/src/worker.ts", import.meta.url), "utf8")
318+
const probes = worker.slice(worker.indexOf("async function runBootProbe"), worker.indexOf("if (phase?.startsWith(\"seeded-\"))"))
319+
const lifecycle = worker.slice(worker.indexOf("function canonicalLifecycleProbeCode"), worker.indexOf("\nasync function bootWordPressRuntime"))
320+
321+
for (const phase of ["canonical-current-user", "canonical-init", "canonical-site-status", "canonical-wp-loaded"]) {
322+
assert.match(probes, new RegExp(`"${phase}"`))
323+
assert.match(lifecycle, new RegExp(`"${phase}"`))
324+
}
325+
assert.match(probes, /packagedCanonicalMarkdownSeed\(\), new Uint8Array\(markdownPrimaryBootstrapIndex\), "https:\/\/canonical-probe\.invalid", \{\}, bucket, true\)/)
326+
assert.match(lifecycle, /substr_count\(\$settings, \$needle\) !== 1/)
327+
assert.match(lifecycle, /WordPress canonical lifecycle probe needle was not uniquely found\./)
328+
assert.match(lifecycle, /'completed' => true/)
329+
assert.match(lifecycle, /'wpCronInitAttached' => false !== has_action\('init', 'wp_cron'\)/)
330+
assert.match(lifecycle, /'updateScheduleInitAttached' => false !== has_action\('init', 'wp_schedule_update_checks'\)/)
331+
assert.match(lifecycle, /'privacyScheduleInitAttached' => false !== has_action\('init', 'wp_schedule_delete_old_privacy_export_files'\)/)
332+
assert.doesNotMatch(lifecycle, /get_option|\$wpdb|\$_COOKIE|AUTH_KEY|password|token/i)
333+
assert.equal((lifecycle.match(/do_action\( 'init' \);/g) ?? []).length, 1)
334+
assert.equal((lifecycle.match(/do_action\( 'wp_loaded' \);/g) ?? []).length, 1)
335+
assert.match(lifecycle, /stop\.after \? "\$needle/)
336+
assert.match(lifecycle, /: "\$stop/)
337+
})
338+
316339
test("Cloudflare canonical runtime patches the unique init call with the disabled-cron scheduling policy", async () => {
317340
const worker = await readFile(new URL("../packages/runtime-cloudflare/src/worker.ts", import.meta.url), "utf8")
318341
const patcher = worker.slice(worker.indexOf("function patchCanonicalDisabledCronSchedulingPolicyAtInit"), worker.indexOf("\nfunction collectRuntimeFiles"))

0 commit comments

Comments
 (0)