Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Wordpress Packager Laravel Package

roots/wordpress-packager

Roots tooling that builds and publishes Composer packages for WordPress core and plugins, so you can install WordPress and extensions as standard dependencies with version constraints. Automates release packaging for Bedrock-style projects managed entirely through Composer.

View on GitHub
Deep Wiki
Context7

Getting Started

Understand what you install first. roots/wordpress-packager is a build tool, not a runtime dependency. It clones/downloads WordPress from wordpress.org, deletes the bundled themes (twentytwenty*) and plugins (akismet, hello.php), rewrites the manifest, and publishes the result as the Composer package roots/wordpress (the repo also publishes a fuller variant that keeps bundled themes — check Packagist before assuming which you want). In day-to-day work you almost never run the packager itself; you consume its artifact. The only time you clone the repo is when you need to produce a bespoke artifact (patched core, internal mirror, air-gapped build).

Where to look first:

  1. composer.json → the scripts block, to see the exact local build invocation, and the require block, to see the WP source/version plumbing.
  2. src/ → the PHP classes doing download, strip and repackage. This is where you'd add exclusions.
  3. .github/workflows/ → the release pipeline; it is the canonical, working example of a full build, more reliable than any README snippet.

Minimal setup in a Bedrock (or Bedrock-style) project:

composer require roots/wordpress-core-installer
composer require roots/wordpress:^6.5

Then in composer.json:

{
  "config": {
    "allow-plugins": {
      "roots/wordpress-core-installer": true,
      "composer/installers": true
    }
  },
  "extra": {
    "wordpress-install-dir": "web/wp",
    "installer-paths": {
      "web/app/plugins/{$name}/": ["type:wordpress-plugin"],
      "web/app/themes/{$name}/": ["type:wordpress-theme"]
    }
  }
}

composer install drops core into web/wp instead of vendor/, leaving web/app (your wp-content) entirely under your control.

First use case — upgrading WordPress: composer require roots/wordpress:^6.6, run wp core update-db, smoke-test the admin, commit composer.json + composer.lock. That single lockfile diff is your entire, reviewable WordPress upgrade — reproducible on every environment and CI run.

Implementation Patterns

Pinned-core, lockfile-driven upgrades. Constrain to a minor (^6.6) and let the lockfile pin the patch. Upgrades become a two-command ceremony:

composer outdated roots/wordpress
composer update roots/wordpress --with-dependencies
wp core version && wp core update-db

Never loosen to *; WordPress patch releases occasionally break plugins, and you want that surfaced as an explicit lockfile diff in a PR.

Treat web/wp as immutable build output. This is the single most important architectural habit the package enables. Add /web/wp/ to .gitignore (Bedrock does this by default), disable WP's own updater (DISALLOW_FILE_MODS, AUTOMATIC_UPDATER_DISABLED, WP_AUTO_UPDATE_CORE => false in config/application.php), and let Composer be the only writer. Anything that writes into core will silently vanish on the next deploy.

Deploy hook pattern (Forge/Deployer/Envoyer):

composer install --no-dev --no-interaction --optimize-autoloader
wp core update-db --path=web/wp
wp cache flush

Install runs before the symlink swap, so the new release is fully composed on disk. Because web/wp is a Composer dist zip, the install step is fast and cacheable — a good candidate for CI artifact caching keyed on composer.lock.

Keep the plugin/theme layer composable. With core packaged, everything in web/app is yours to declare:

"require": {
  "wpackagist-plugin/advanced-custom-fields": "^6.2",
  "wpackagist-plugin/redis-cache": "^2.5"
},
"repositories": [
  { "type": "composer", "url": "https://wpackagist.org" },
  { "type": "path", "url": "packages/my-plugin", "options": { "symlink": true } }
]

Use type: path with symlink: true for in-house plugins you're actively developing — you get hot editing without a cp -r dance, while third-party plugins stay version-pinned.

Self-hosted artifact pattern (private fork of the packager). If you need a patched core, an internal mirror, or a build that strips additional files, fork the repo, adjust the strip list / src/, and publish to Satis, Private Packagist or a cloud Composer registry. You then depend on yourco/wordpress and the whole workflow above is unchanged — that's the real payoff of the packaging abstraction.

CI matrix pattern. Because the core version is data in composer.lock, you can test a plugin against two WP versions cheaply: composer require roots/wordpress:^6.5 in one job, ^6.6 in another, sharing the rest of the install. Cheap, high-signal compatibility checks before a core bump lands on production.

Hybrid Laravel pattern. Common in agencies running Laravel and WordPress side by side: use this to keep core versioned, then read WP data from Laravel via Corcel or expose Bedrock's wp_users for shared auth. The value is that the WP half is now a Composer dependency like any Laravel package — same lockfile discipline, same deploy pipeline, no FTP.

Gotchas and Tips

  • allow-plugins is the #1 first-run failure. Composer 2.2+ refuses to run roots/wordpress-core-installer unless it's allow-listed. On CI, the error is easy to miss behind --no-interaction; add the config to composer.json, not just your local global config.
  • No default themes. A stripped core means twentytwentyfour doesn't exist. Fallbacks, block-theme tests, and wp theme list behave differently. If a plugin assumes a default theme, require it explicitly from WPackagist or switch to the full variant.
  • wp core update / wp core download must not be used. They write into web/wp and get clobbered. Also expect wp core verify-checksums to be noisy or fail — the artifact is repackaged, so don't gate deploys on it. Verify with composer show roots/wordpress instead.
  • Core version ≠ database version. composer update does not run the DB upgrade. Bedrock-era WP is usually tolerant, but always run wp core update-db in the deploy, and check wp core is-installed before it in fresh environments.
  • wp-content ownership. The packager ships core only. WP_CONTENT_DIR/WP_CONTENT_URL point at web/app; if you see themes installed under web/wp/wp-content, your installer paths or constants are wrong. Check with wp --info and wp eval 'echo WP_CONTENT_DIR;'.
  • Version semantics. Package versions mirror WordPress releases, including betas/RCs as pre-release tags — Composer won't select them unless your constraint allows it (good), but a stray @dev stability flag will. Keep minimum-stability at stable and use per-package stability flags if you truly need a beta.
  • Rate limits and dist caching. The dist comes from GitHub releases; behind a proxy or under GitHub rate limiting, composer install fails non-deterministically. Cache ~/.composer/cache in CI or use a Satis mirror for large teams.
  • Translations live outside core. Language packs land in web/app/languages and survive core upgrades — but they aren't managed by Composer. Decide deliberately: wp language core update in deploy, or pin them in a plugin.
  • Debugging checklist when core misbehaves: composer why roots/wordpress (who pulled it in), composer show roots/wordpress (installed version), wp core version, then git diff on the lockfile to see whether the version actually moved. Nine times out of ten the answer is that web/wp was edited in place and never re-installed.
  • Stale builds after a rollback. Rolling back the symlink is fast, but the DB may already be migrated. If you roll back across a major WP version, restore the DB snapshot too — there's no wp core update-db --down.
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
robertmarney/lara-hierarchial-collections
simonschaufi/rule-doc-generator
emissaoja/emissaoja
blackbricksoftware/gravityforms-civicrm-form-processor
al-rahabi/fancy-crud
jinas/dhivatar-component
mltstephane/laravel-quality
bernskiold/laravel-talentlms
bernskioldmedia/laravel-form-architect
youbar/easy-crud
c-point-labs/laravel-hostyourai
dbravoan/dbcms-admin-theme-dbcms
dbravoan/dbcms-theme-db-cyber
dbravoan/dbcms-blocks-basic
dbravoan/dbcms-module-content-audit
dbravoan/dbcms-module-ai-assistant
paygate-to/paygate-php
padosoft/askmydocs-connector-base
defstudio/supercharged-carbon
bernskioldmedia/laravel-matomo