Skip to content

Commit 4c7bd06

Browse files
committed
Boot Cloudflare WordPress from install seed
1 parent 394d62b commit 4c7bd06

5 files changed

Lines changed: 51 additions & 3 deletions

File tree

Binary file not shown.
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
declare module "*.sqlite" {
2+
const database: ArrayBuffer
3+
export default database
4+
}

packages/runtime-cloudflare/src/worker.ts

Lines changed: 35 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,13 @@ import { bootWordPressAndRequestHandler, type WordPressInstallMode } from "@wp-p
66
import { dependenciesTotalSize, init } from "../../../node_modules/@php-wasm/web-8-5/asyncify/php_8_5.js"
77
import phpWasmModule from "../../../node_modules/@php-wasm/web-8-5/asyncify/8_5_8/php_8_5.wasm"
88
import { CLOUDFLARE_RUNTIME_HEALTH_MARKER, CLOUDFLARE_RUNTIME_HEALTH_SCHEMA, cloudflareRuntimeHealthResponse } from "./health-envelope.js"
9+
import wordpressInstallSeed from "../assets/wordpress-install-seed.sqlite"
910

1011
const PHP_VERSION = "8.5.8"
1112
const WORDPRESS_ARCHIVE_URL = "https://wordpress.org/latest.zip"
1213
const SQLITE_INTEGRATION_ARCHIVE_URL = "https://github.com/WordPress/sqlite-database-integration/releases/download/v2.2.23/plugin-sqlite-database-integration.zip"
1314
const SITE_URL = "https://wp-codebox-runtime.invalid"
15+
const DATABASE_PATH = "/wordpress/wp-content/database/.ht.sqlite"
1416
let bootPromise: Promise<{ php: PHP; wordpressVersion: string }> | undefined
1517

1618
export default {
@@ -80,6 +82,27 @@ async function runBootProbe(phase: string): Promise<Response> {
8082
}
8183
}
8284

85+
if (phase === "seeded-wordpress") {
86+
const runtime = await bootWordPressRuntime(
87+
"install-from-existing-files-if-needed",
88+
true,
89+
true,
90+
new Uint8Array(wordpressInstallSeed),
91+
)
92+
try {
93+
const wordpress = (await runtime.php.run({
94+
code: "<?php require '/wordpress/wp-load.php'; echo json_encode(['siteUrl' => get_option('siteurl'), 'wordpressVersion' => get_bloginfo('version')]);",
95+
})).text.trim()
96+
try {
97+
return probeResponse(phase, JSON.parse(wordpress) as Record<string, string>)
98+
} catch {
99+
throw new Error(`WordPress boot returned invalid JSON: ${wordpress}`)
100+
}
101+
} finally {
102+
runtime.php.exit()
103+
}
104+
}
105+
83106
if (phase === "wordpress-files" || phase === "sqlite" || phase === "full" || phase === "streamed-sqlite" || phase === "streamed-wordpress") {
84107
const runtime = await bootWordPressRuntime(
85108
phase === "full" || phase === "streamed-wordpress" ? "install-from-existing-files" : "do-not-attempt-installing",
@@ -101,10 +124,18 @@ async function bootWordPressRuntime(
101124
wordpressInstallMode: WordPressInstallMode = "install-from-existing-files",
102125
includeSqlite = true,
103126
streamWordPressFiles = false,
127+
databaseSeed?: Uint8Array,
104128
): Promise<{ php: PHP; wordpressVersion: string }> {
105129
const requestHandler = await bootWordPressAndRequestHandler({
106130
createPhpRuntime,
107-
hooks: streamWordPressFiles ? { beforeWordPressFiles: materializeWordPressServerFiles } : undefined,
131+
dataSqlPath: DATABASE_PATH,
132+
hooks: streamWordPressFiles || databaseSeed ? {
133+
beforeWordPressFiles: streamWordPressFiles ? materializeWordPressServerFiles : undefined,
134+
beforeDatabaseSetup: databaseSeed ? (php: PHP) => {
135+
php.mkdir("/wordpress/wp-content/database")
136+
php.writeFile(DATABASE_PATH, databaseSeed)
137+
} : undefined,
138+
} : undefined,
108139
maxPhpInstances: 1,
109140
phpVersion: "8.5",
110141
siteUrl: SITE_URL,
@@ -141,7 +172,9 @@ async function materializeWordPressServerFiles(php: PHP): Promise<{ materialized
141172
}
142173

143174
function isWordPressServerFile(path: string): boolean {
144-
return /\.(?:php|json|crt|html)$/.test(path) || path.endsWith("/style.css")
175+
return /\.(?:php|json|crt|html)$/.test(path)
176+
|| path.endsWith("/style.css")
177+
|| path.endsWith("/wp-admin/css/view-transitions.min.css")
145178
}
146179

147180
function createPhpRuntime() {

packages/runtime-cloudflare/wrangler.jsonc

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,5 +4,8 @@
44
"compatibility_date": "2026-07-18",
55
"compatibility_flags": ["nodejs_compat"],
66
"limits": { "cpu_ms": 300000 },
7-
"rules": [{ "type": "CompiledWasm", "globs": ["**/*.wasm"], "fallthrough": false }]
7+
"rules": [
8+
{ "type": "CompiledWasm", "globs": ["**/*.wasm"], "fallthrough": false },
9+
{ "type": "Data", "globs": ["**/*.sqlite"], "fallthrough": false }
10+
]
811
}

tests/cloudflare-runtime.test.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,3 +28,11 @@ test("Cloudflare runtime declares the paid-plan WordPress boot CPU budget", asyn
2828
const config = JSON.parse(await readFile(new URL("../packages/runtime-cloudflare/wrangler.jsonc", import.meta.url), "utf8")) as { limits?: { cpu_ms?: number } }
2929
assert.equal(config.limits?.cpu_ms, 300_000)
3030
})
31+
32+
test("Cloudflare runtime packages the disposable WordPress install seed", async () => {
33+
const config = JSON.parse(await readFile(new URL("../packages/runtime-cloudflare/wrangler.jsonc", import.meta.url), "utf8")) as { rules?: Array<{ type?: string; globs?: string[] }> }
34+
const seed = await readFile(new URL("../packages/runtime-cloudflare/assets/wordpress-install-seed.sqlite", import.meta.url))
35+
36+
assert.equal(seed.subarray(0, 16).toString(), "SQLite format 3\0")
37+
assert.ok(config.rules?.some((rule) => rule.type === "Data" && rule.globs?.includes("**/*.sqlite")))
38+
})

0 commit comments

Comments
 (0)