Skip to content

Commit 8294e30

Browse files
zaerladamziel
andauthored
[boot] Add check for SQLite driver missing in target folder (#2440)
## Motivation for the change, related issues Context: By running `npx @wp-playground/cli@latest server server --mount-before-install=.:/wordpress --skip-wordpress-setup --skip-sqlite-setup` without: 1. Specifying a working WordPress 2. Specifying a SQLite working driver The CLI crashed. Now, more safety checks take into consideration: 1. If the user did not pass `--skip-sqlite-setup`, it checks if the mu-plugins folder exists in the target folder 2. If it skips everything, it does not crash but returns a more insightful error to explain the situation ## Implementation details ## Testing Instructions (or ideally a Blueprint) Choose a working WordPress folder, such as a WordPress Studio one. It should work because the folder already has a sqlite driver inside: ``` npx nx run playground-cli:dev-node server --mount-before-install=~/Studio/your-test-site:/wordpress --skip-wordpress-setup --skip-sqlite-setup --debug ``` This should not crash, but tell you that "SQLite installation has been skipped and no SQLite mu-plugin has been found". ``` npx nx run playground-cli:dev-node server --mount-before-install=./an-empty-folder:/wordpress --skip-wordpress-setup --skip-sqlite-setup --debug ``` This should work. Notice the `skip-sqlite-setup` missing: ``` npx nx run playground-cli:dev-node server --mount-before-install=./a-test-site:/wordpress --skip-wordpress-setup --debug ``` Other combinations should work as well. --------- Co-authored-by: Adam Zieliński <[email protected]>
1 parent 954464f commit 8294e30

File tree

4 files changed

+143
-3
lines changed

4 files changed

+143
-3
lines changed

packages/playground/cli/src/blueprints-v1/blueprints-v1-handler.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -154,7 +154,7 @@ export class BlueprintsV1Handler {
154154
mountsAfterWpInstall,
155155
wordPressZip: wordPressZip && (await wordPressZip!.arrayBuffer()),
156156
sqliteIntegrationPluginZip:
157-
await sqliteIntegrationPluginZip!.arrayBuffer(),
157+
await sqliteIntegrationPluginZip?.arrayBuffer(),
158158
firstProcessId: 0,
159159
processIdSpaceLength: this.processIdSpaceLength,
160160
followSymlinks,

packages/playground/cli/src/run-cli.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -571,7 +571,7 @@ export async function runCLI(args: RunCLIArgs): Promise<RunCLIServer> {
571571
throw error;
572572
}
573573
let phpLogs = '';
574-
if (await playground.fileExists(errorLogPath)) {
574+
if (await playground?.fileExists(errorLogPath)) {
575575
phpLogs = await playground.readFileAsText(errorLogPath);
576576
}
577577
throw new Error(phpLogs, { cause: error });

packages/playground/wordpress/src/boot.ts

Lines changed: 55 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -182,11 +182,44 @@ export async function bootWordPress(options: BootOptions) {
182182

183183
if (!options.dataSqlPath) {
184184
if (!(await isWordPressInstalled(php))) {
185+
// Install WordPress if it's not installed.
185186
await installWordPress(php);
186187
}
187188

188189
if (!(await isWordPressInstalled(php))) {
189-
throw new Error('WordPress installation has failed.');
190+
// Check if the database connection (MySQL or SQLite) is up and running.
191+
const validConnection = await isDatabaseConnectionValid(php);
192+
193+
if (validConnection) {
194+
// The database connection is valid, but WordPress installation has failed.
195+
// Throw a generic error, not related to the database connection.
196+
throw new Error('WordPress installation has failed.');
197+
} else {
198+
if (php.isFile('/internal/shared/preload/0-sqlite.php')) {
199+
// The core SQLite integration has been installed, but the database connection is not valid.
200+
throw new Error('Error connecting to the SQLite database.');
201+
}
202+
203+
// Check if a SQLite integration plugin has not been provided.
204+
if (!options.sqliteIntegrationPluginZip) {
205+
const sqlitePluginPath = joinPaths(
206+
requestHandler.documentRoot,
207+
'wp-content/mu-plugins/sqlite-database-integration'
208+
);
209+
210+
if (php.isDir(sqlitePluginPath)) {
211+
// The mu-plugin has been installed, but the database connection is not valid.
212+
throw new Error(
213+
'Error connecting to the SQLite database.'
214+
);
215+
}
216+
}
217+
218+
// 1. No core SQLite integration has been installed.
219+
// 2. No valid SQLite integration plugin has been provided.
220+
// The MySQL database connection is not valid.
221+
throw new Error('Error connecting to the MySQL database.');
222+
}
190223
}
191224
}
192225

@@ -368,3 +401,24 @@ export function getFileNotFoundActionForWordPress(
368401
uri: '/index.php',
369402
};
370403
}
404+
405+
async function isDatabaseConnectionValid(php: PHP) {
406+
const result = await php.run({
407+
code: `<?php
408+
ob_start();
409+
$wp_load = getenv('DOCUMENT_ROOT') . '/wp-load.php';
410+
if (!file_exists($wp_load)) {
411+
echo '-1';
412+
exit;
413+
}
414+
require $wp_load;
415+
ob_clean();
416+
echo $wpdb->check_connection( false) ? '1' : '0';
417+
ob_end_flush();
418+
`,
419+
env: {
420+
DOCUMENT_ROOT: php.documentRoot,
421+
},
422+
});
423+
return result.text === '1';
424+
}
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
import { createNodeFsMountHandler, loadNodeRuntime } from '@php-wasm/node';
2+
import { RecommendedPHPVersion } from '@wp-playground/common';
3+
import {
4+
getWordPressModule,
5+
MinifiedWordPressVersions,
6+
} from '@wp-playground/wordpress-builds';
7+
import { mkdirSync, rmdirSync } from 'fs';
8+
import { tmpdir } from 'os';
9+
import { join } from 'path';
10+
import { bootWordPress } from '../boot';
11+
import { getLoadedWordPressVersion } from '../version-detect';
12+
13+
describe('Test database', () => {
14+
let tempDir: string;
15+
16+
beforeAll(() => {
17+
tempDir = join(tmpdir(), 'database-test');
18+
19+
try {
20+
mkdirSync(
21+
join(
22+
tempDir,
23+
'wp-content',
24+
'mu-plugins',
25+
'sqlite-database-integration'
26+
),
27+
{ recursive: true }
28+
);
29+
} catch {
30+
// Ignore error if directory already exists
31+
}
32+
});
33+
34+
afterAll(() => {
35+
rmdirSync(tempDir, { recursive: true });
36+
});
37+
38+
it("should not start WordPress when SQLite ZIP not specified, the SQLite driver directory doesn't exist and MySQL can't be used", async () => {
39+
await expect(async () => {
40+
await bootWordPress({
41+
createPhpRuntime: async () =>
42+
await loadNodeRuntime(RecommendedPHPVersion),
43+
siteUrl: 'http://playground-domain/',
44+
wordPressZip: await getWordPressModule(),
45+
sqliteIntegrationPluginZip: undefined,
46+
});
47+
}).rejects.toThrow('Error connecting to the MySQL database.');
48+
});
49+
50+
it('hould install WordPress when SQL data path specified, even without SQLite ZIP path or SQLite driver directory', async () => {
51+
const handler = await bootWordPress({
52+
createPhpRuntime: async () =>
53+
await loadNodeRuntime(RecommendedPHPVersion),
54+
siteUrl: 'http://playground-domain/',
55+
wordPressZip: await getWordPressModule(),
56+
sqliteIntegrationPluginZip: undefined,
57+
dataSqlPath: '/wordpress/wp-content/database/.ht.sqlite',
58+
});
59+
60+
const loadedWordPressVersion = await getLoadedWordPressVersion(handler);
61+
expect(loadedWordPressVersion).toBeTruthy();
62+
expect(Object.keys(MinifiedWordPressVersions)).toContain(
63+
loadedWordPressVersion
64+
);
65+
});
66+
67+
it("should fail when the SQLite driver directory exists, but doesn't contain a valid driver", async () => {
68+
await expect(async () => {
69+
await bootWordPress({
70+
createPhpRuntime: async () =>
71+
await loadNodeRuntime(RecommendedPHPVersion),
72+
siteUrl: 'http://playground-domain/',
73+
wordPressZip: await getWordPressModule(),
74+
sqliteIntegrationPluginZip: undefined,
75+
hooks: {
76+
beforeWordPressFiles: async (php) => {
77+
await php.mount(
78+
'/wordpress',
79+
createNodeFsMountHandler(tempDir)
80+
);
81+
},
82+
},
83+
});
84+
}).rejects.toThrow('Error connecting to the SQLite database.');
85+
});
86+
});

0 commit comments

Comments
 (0)