Skip to content

[PHP] Add streaming tar.zst WordPress bundle extraction - #3925

Merged
adamziel merged 3 commits into
trunkfrom
adamziel/tar-zst-runtime-extraction
Jul 7, 2026
Merged

[PHP] Add streaming tar.zst WordPress bundle extraction#3925
adamziel merged 3 commits into
trunkfrom
adamziel/tar-zst-runtime-extraction

Conversation

@adamziel

@adamziel adamziel commented Jul 7, 2026

Copy link
Copy Markdown
Collaborator

What it does

Adds the runtime extraction path for tar.zst WordPress core bundles while keeping the existing wordPressZip / unzipWordPress() API names.

This PR does not switch committed WordPress core bundle artifacts to tar.zst. ZIP inputs from wordpress.org, custom URLs, trunk/nightly downloads, plugins, themes, Blueprints, and GitHub artifacts stay on the existing ZIP path.

Rationale

#3926 added the TAR parser and #3927 added the zstd decoder stream. The next step for #3913 is making WordPress boot able to consume a tar.zst core bundle before changing the produced bundle artifacts.

Keeping runtime support separate from the artifact switch lets reviewers validate the extraction behavior, API compatibility, and ZIP fallback path independently.

Implementation

unzipWordPress() now sniffs the first four bytes of the supplied File:

  1. zstd magic (28 b5 2f fd) routes through createDecodedTarStream(..., 'zstd').
  2. Decoded TAR bytes stream into extractTarStreamToPhp() one entry at a time.
  3. ZIP and all other inputs continue through unzipFile() / PHP ZipArchive.
  4. wordPressBundleFileCount enables a tar.zst-only file-count parity check when bundle metadata provides one.

The extractor writes files under /tmp/unzipped-wordpress, rejects unsafe TAR entries through the parser, preserves overwriteFiles=false, and handles targetRoot='/' as an absolute root instead of producing relative paths.

Testing instructions

npx nx test playground-wordpress --testFile=streaming-tar-extract.spec.ts
npx nx run playground-wordpress:lint

CI should run the broader package, typecheck, and browser/CLI coverage for the rebased PR.

Base automatically changed from adamziel/wordpress-bundle-metadata to trunk July 7, 2026 11:47
@adamziel
adamziel force-pushed the adamziel/tar-zst-runtime-extraction branch from f1cb4aa to 2d8a456 Compare July 7, 2026 11:48
@adamziel
adamziel marked this pull request as ready for review July 7, 2026 11:48
@adamziel
adamziel requested review from a team, brandonpayton and Copilot July 7, 2026 11:48

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Note

Copilot was unable to run its full agentic suite in this review.

Adds runtime support for WordPress core bundles shipped as solid .tar.zst archives by sniffing magic bytes and extracting via a bounded-memory streaming TAR parser, while keeping existing ZIP extraction behavior and public API names intact.

Changes:

  • Add streaming TAR parser + MEMFS extractor and a zstddec fallback decoder (with native DecompressionStream('zstd') when available).
  • Route unzipWordPress() to either .tar.zst streaming extraction or existing ZIP path based on magic bytes, with optional file-count parity checking.
  • Add focused unit tests for streaming parsing/extraction edge cases and wire expected bundle file counts through the remote worker + boot flows.

Reviewed changes

Copilot reviewed 8 out of 9 changed files in this pull request and generated 5 comments.

Show a summary per file
File Description
packages/playground/wordpress/vite.config.ts Includes .tar.zst bundles as Vite assets for build/runtime availability.
packages/playground/wordpress/src/streaming-tar-extract.ts Implements streaming tar parsing, safe path handling, zstd decoding, and MEMFS extraction.
packages/playground/wordpress/src/streaming-tar-extract.spec.ts Adds unit tests for parser/extractor correctness and security edge cases.
packages/playground/wordpress/src/legacy-wp/legacy-boot.ts Passes expected file-count parity option through legacy boot path.
packages/playground/wordpress/src/index.ts Routes unzipWordPress() by magic bytes and exports new streaming utilities.
packages/playground/wordpress/src/boot.ts Documents wordPressZip as bundle, adds wordPressBundleFileCount option, and passes it through.
packages/playground/remote/src/lib/playground-worker-endpoint-blueprints-v1.ts Supplies expected file count for tar.zst descriptors and renames downloaded file to wp.bundle.
package.json Adds zstddec dependency needed for zstd streaming decode fallback.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread packages/playground/wordpress/src/streaming-tar-extract.ts
Comment on lines +197 to +201
if (this.zeroBlocks === 0) {
throw new Error(
'Truncated tar stream: missing end-of-archive marker'
);
}
Comment thread packages/playground/wordpress/src/index.ts
Comment on lines +379 to +380
// @ts-ignore -- no bundled type resolution for the zstddec/stream subpath
const { ZSTDDecoder } = await import('zstddec/stream');
Comment thread packages/playground/wordpress/src/streaming-tar-extract.ts
@adamziel
adamziel force-pushed the adamziel/tar-zst-runtime-extraction branch 3 times, most recently from 629d334 to fa60a18 Compare July 7, 2026 12:29
@adamziel
adamziel force-pushed the adamziel/tar-zst-runtime-extraction branch from 007c129 to c458d81 Compare July 7, 2026 21:14
@adamziel
adamziel merged commit 21163fb into trunk Jul 7, 2026
53 checks passed
@adamziel
adamziel deleted the adamziel/tar-zst-runtime-extraction branch July 7, 2026 21:42
adamziel added a commit that referenced this pull request Jul 7, 2026
## What it does

Related to #3913.

Replaces the committed minified WordPress core bundle artifacts from
`wp-*.zip` to deterministic `wp-*.tar.zst` files.

This PR builds on the already-merged runtime/parser work: #3926, #3927,
and #3925. It does not change non-core ZIP flows. WordPress.org
downloads, custom URLs, `trunk`/`nightly`, plugins, themes, GitHub
artifacts, and Blueprint ZIP imports continue to use ZIP.

## Rationale

After #3916 removed the duplicated embedded `wordpress-static.zip`, the
remaining minified WordPress tree compresses better as a solid
zstd-compressed tar than as ZIP. The runtime can now stream-decode zstd
and parse TAR entries incrementally, so the committed core bundle can
switch formats without changing the public boot API.

Keeping remote and user-supplied archives as ZIP avoids expanding the
scope of this change beyond the committed Playground-managed core
bundles.

## Implementation

Adds a deterministic core-bundle build path that:

- re-containers the minified WordPress ZIP build output into
`wp-<version>.tar.zst`,
- writes `format`, `container`, `codec`, `size`, `sha256`, and
`fileCount` descriptors,
- verifies committed tar.zst artifacts against those descriptors,
- fails loudly for unsafe source archive paths instead of silently
dropping them,
- removes committed `wp-*.zip` artifacts for minified core versions, and
- teaches Vite/offline asset handling about `.tar.zst` and hashed `.zst`
files.

The generated descriptor still marks `trunk`/`nightly` as ZIP because
those versions are fetched directly from GitHub as ZIP archives instead
of using committed Playground core artifacts.

## Testing instructions

```bash
node packages/playground/wordpress-builds/build/build-tar-zst.mjs --verify
npx nx test playground-wordpress-builds
npx nx test playground-wordpress
npx nx test playground-remote
npx nx typecheck playground-remote
npx nx run-many -t lint -p playground-wordpress-builds,playground-remote,playground-wordpress
```
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment