Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

v1 WordPress Boot Protocol #1390

Merged
merged 17 commits into from
May 23, 2024
Merged

v1 WordPress Boot Protocol #1390

merged 17 commits into from
May 23, 2024

Conversation

adamziel
Copy link
Collaborator

@adamziel adamziel commented May 14, 2024

What is this PR doing?

Implements a bootWordPress() function aiming to provide a reliable and consistent boot pipeline for every Playground-based app, whether it's a browser app, server app, a VS Code extension, or anything else.

As a nice side-effect, Playground can now boot not just from the custom minified WordPress builds, but also from the official WordPress releases!

Related issue: #1398

How does it work?

bootWordPress() ties together all the pieces from different Playground-based apps. Boot flow diagram demonstrates the idea. A tl;dr pipeline is:

  • Mount resources (stage=before WordPress files)
  • Unzip WordPress, if that's requested
  • Mount resources (stage=before database setup)
  • Setup SQLite, MySQL (TODO), or rely on a mounted database
  • Run WordPress installer, if the site isn't installed yet

Here's a usage example in a Node.js app:

const requestHandler = await bootWordPress({
	siteUrl: absoluteUrl,
	createPhpInstance() {
		return new NodePHP();
	},
	createPhpRuntime: async () =>
		await NodePHP.loadRuntime(compiledBlueprint.versions.php),
	wordPressZip,
	sqliteIntegrationPluginZip: fetchSqliteIntegration(monitor),
	sapiName: 'cli',
	createFiles: {
		'/internal/shared/ca-bundle.crt':
			rootCertificates.join('\n'),
	},
	phpIniEntries: {
		'openssl.cafile': '/internal/shared/ca-bundle.crt',
		allow_url_fopen: '1',
		disable_functions: '',
	},
	hooks: {
		async beforeWordPressFiles(php) {
			if (args.mountBeforeInstall) {
				mountResources(php, args.mountBeforeInstall);
			}
		},
	},
});

Testing instructions

Follow-up work

  • Replace manual calls to setupPlatformLevelMuPlugins() in unit tests with bootWordPress()
  • Add unit tests to confirm boot works with minified WordPress build, official WordPress release, SQLite, MySQL. Test all the BootOptions and confirm PHP.ini directives are used, constants are created etc.
  • Remove the createPhpInstance() argument in favor of using just the PHP class – once Breaking: PHP: Remove NodePHP and WebPHP classes in favor of a single "PHP" class #1457 merges
  • Figure out storing debug.log in the configureErrorLogging mu-plugin – right now it's stored in /wordpress/wp-content
  • Support more database modes: MySQL and custom where Playground relies on db.php brought in from
  • Replace hooks with a Mount data type that would have different implementations, like OPFS, Native FS, and Node FS – explorations started in Breaking: PHP: Remove NodePHP and WebPHP classes in favor of a single "PHP" class #1457
  • Once the API settles, document the usage for developers

cc @brandonpayton @bgrgicak

Base automatically changed from install-sqlite-function to trunk May 15, 2024 08:48
@adamziel adamziel changed the title WordPress Boot Protocol Explorations: WordPress Boot Protocol May 15, 2024
@adamziel adamziel mentioned this pull request May 15, 2024
1 task
@bgrgicak
Copy link
Collaborator

I really like the direction of this PR. It will at least make it much easier to understand how Playground is booting.

@adamziel adamziel force-pushed the playground-boot-protocol branch from 0ef6ea0 to ce3e9e5 Compare May 20, 2024 22:52
@adamziel adamziel changed the title Explorations: WordPress Boot Protocol WordPress Boot Protocol May 23, 2024
@adamziel adamziel changed the title WordPress Boot Protocol v1 WordPress Boot Protocol May 23, 2024
@adamziel adamziel marked this pull request as ready for review May 23, 2024 16:17
@adamziel
Copy link
Collaborator Author

adamziel commented May 23, 2024

There are follow-up tasks, but there's a clear value add already so I'm going to merge this as it is. Let's keep iterating.

(the unit test failures are intermittent)

@adamziel adamziel merged commit 7c71449 into trunk May 23, 2024
4 of 5 checks passed
@adamziel adamziel deleted the playground-boot-protocol branch May 23, 2024 16:26
adamziel added a commit that referenced this pull request May 29, 2024
## What is this PR doing?

Ships a `DirectoryHandleMount` class that can be used to mount OPFS
using the `php.mount()` method:

```ts
await php.mount(
	php.documentRoot,
	createDirectoryHandleMountHandler(opfsHandle);
);
```

### Other changes

This PR removes the `Mountable` interface in favor of functional API to
simplify the implementation and remove any state management concerns.

**Before:**

```ts
php.mount(dir, new NodeFSMount(dir));
```

**After:**

```ts
php.mount(dir, createNodeFsMountHandler(dir));
```

## What problem is it solving?

It enables mounting OPFS and local directory handles in the browser
version of Playground using the same abstraction as we use for mounting
local directories in Node.js. This, in turn, enables using OPFS mounts
in the [boot
protocol](#1390)
and relying on the same general code paths and abstractions.

## How is the problem addressed?

DirectoryHandleMount is not a "real" mount in that it doesn't actually
plug in an Emscripten FS implementation into the PHP Emscripten module.
Instead, it copies all the OPFS files into Playground MEMFS (or the
other way around). After that initial sync, it journals all the MEMFS
filesystem operations and replays them in OPFS. This is good enough for
the in-browser directory handle, since the underlying files aren't going
to change on their own, but for the Local Filesystem directory handle we
also have an explicit "Synchronize files" button to bring any local
changes back into Playground.

Once Emscripten supports asynchronous filesystem operations via wasmfs,
we'll be about to get rid of the journal and mount an OPFS
implementation to directly delegate writes, reads, etc to the underlying
filesystem.

## Testing Instructions

Open local Playground and change the storage option to "browser", make
some visible changes, refresh Playground a few times, confirm the site
still loads and the changes are still around. Close the browser, reopen
it, confirm it still works.

Them repeat the test with the local directory storage option.

Also, confirm the following Playground CLI command works and sets up a
site in a local `new-wordpress-site` directory:

```bash
bun packages/playground/cli/src/cli.ts server --mount-before-install=`pwd`/new-wordpress-site:/wordpress
```
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants