roots/wordpress-packager: The bot that turns WordPress core into clean Composer snapshots
A deep dive into the orphan-branch trick, the release watcher, and the packaging pipeline that makes legacy WordPress releases behave like modern dependencies.
- roots/wordpress-packager makes WordPress core behave like a modern dependency by flattening each release into a clean Git snapshot.
- Its orphan-branch workflow keeps the package repository small, auditable, and free of upstream history baggage.
- The release watcher and metadata builder do more than copy files, they turn WordPress.org releases into trustworthy Composer artifacts with version-aware packaging rules.
- Roots uses this tool as plumbing for a broader Composer-first WordPress stack, including specialized core builds like full and no-content variants.
WordPress was never meant to be installed like a normal PHP package. roots/wordpress-packager exists to erase that mismatch without pretending the mismatch never existed.
The repo is not a product in the usual sense. It is a release machine, a metadata layer, and a Git transformer wrapped around one goal: make each WordPress release show up as a clean Composer dependency.
Why WordPress needed a packaging layer at all
Composer expects packages to have clear versions, predictable metadata, and a manageable Git history. WordPress ships as a monolithic upstream release stream with its own rules, its own file layout, and a long tail of baggage that does not map neatly onto dependency management.
| Workflow | Packaging method | Release freshness | History size | Composer compatibility | Custom builds | Maintenance burden |
|---|---|---|---|---|---|---|
| roots/wordpress-packager | Automated snapshot builds from WordPress.org releases | High, driven by release detection | Minimal, because each version starts fresh | Native | Yes, including full and no-content variants | Low once the pipeline is stable |
| johnpbloch/wordpress | Prebuilt Composer package for WordPress core | High | Moderate | Native | Less flexible | Moderate |
| Manual WordPress install | Download and unpack the upstream archive | Depends on the operator | None in Git, but no package history either | No | Manual only | High |
| Git submodule workflow | Track WordPress as nested source dependency | Varies by update discipline | Heavy and awkward | Weak fit | Possible but clumsy | High |
That is the real value here. The repo does not just redistribute WordPress. It translates WordPress into the grammar that modern PHP tooling already understands.
The trick: every release becomes a fresh Git snapshot
The center of the repo is a simple but powerful move: git checkout --orphan. Each WordPress version becomes a brand-new branch with no inherited history, then gets committed, tagged, and pushed as if it had always lived there.
That orphan-branch pattern matters because it cuts the past away on purpose. The repository keeps the release identity, but not the release lineage.
git checkout --orphan 6.6.1
git add .
git commit -m "WordPress 6.6.1"
git tag 6.6.1
git push origin 6.6.1 --tags
The result is a package repo that behaves like a catalog, not an archive. There is no need to ship 20 years of history just to install the newest release.
Inside the release watcher
The bot’s job starts with watching WordPress.org for new releases. A GitHub Actions workflow checks on a schedule, compares what it finds against existing tags, and kicks off packaging only when a version is missing.
That is the quiet genius of the design. It is not a giant CI system. It is a small delivery robot that keeps asking one question: has WordPress published something new that the package repo has not captured yet?
This package helps generate Composer packages for any WordPress release.
The workflow stays narrow on purpose. Fetch release data, compare versions, build the package, publish the tag, repeat.
How the package metadata stays version-aware
The repo is doing more than moving ZIP contents around. The package model injects metadata, compatibility rules, and integrity data so the final Composer package is accurate for the exact WordPress release it represents.
| Metadata concern | How the packager handles it | Why it matters |
|---|---|---|
| Minimum PHP version | Maps WordPress versions to their required PHP floor | Prevents installing a release on the wrong runtime |
| Suggested extensions | Adds version-specific extension suggestions during prepareData() | Keeps Composer metadata aligned with real hosting needs |
| Checksum | Pulls SHA1 from WordPress.org | Gives the generated package a trust signal tied to the upstream source |
| Package identity | Extends Composer package modeling directly | Lets the repo express WordPress as a first-class dependency object |
This is what separates a clever mirror from a serious packaging system. The repo encodes compatibility, not just files.
What Roots is really building for
`roots/wordpress-packager` sits inside a larger Composer-first WordPress stack. It helps produce packages such as roots/wordpress, plus specialized variants like full and no-content builds for different project constraints.
That gives Roots a sharper position than a generic WordPress mirror. They are not only tracking core. They are shaping how WordPress enters modern application workflows.
| Tool | Scope | What it solves | Where it fits |
|---|---|---|---|
| roots/wordpress-packager | WordPress core packaging | Turns releases into clean Composer snapshots | Infrastructure layer for Roots packages |
| johnpbloch/wordpress | WordPress core package | Provides a ready-to-require core distribution | Direct alternative for Composer-based installs |
| WPackagist | Plugins and themes | Makes the WordPress directory Composer-friendly | Complements core packaging, not a substitute for it |
| WP Packages | Plugins and themes | Modern metadata-backed package repository | Works alongside core packages in Composer workflows |
The difference is not just technical. It is editorial. Roots is deciding what WordPress should look like when it enters a dependency graph.
Why this repo is a model PHP utility
The codebase reads like infrastructure that expects to live a long time. PHP 8.2+, strict typing, Symfony Console, programmatic Git control, tests with fixtures, and PHPStan at maximum strictness all point in the same direction: small surface area, high confidence.
That matters because utility code only wins when it stays boring in production. This repo does the opposite of flashy. It narrows the problem until the automation becomes dependable.
- Strict types keep the packaging logic honest.
- Fixtures let the release watcher be tested without depending on live network behavior.
- Programmatic Git operations make the snapshot workflow repeatable.
- GitHub Actions turns a maintenance job into a continuously running service.
It is a good example of PHP done with discipline. The repo does one awkward thing extremely well, then gets out of the way.





