Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 19 additions & 17 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,16 @@
"minimum-stability": "stable",
"prefer-stable": true,
"repositories": [
{
"name": "satispress",
"type": "composer",
"url": "https://packagist.yard.nl/satispress/"
},
{
"name": "satis",
"type": "composer",
"url": "https://satis.yard.nl"
},
{
"type": "composer",
"url": "https://wpackagist.org",
Expand Down Expand Up @@ -51,7 +61,6 @@
"spatie/laravel-google-fonts": "^1.4",
"spatie/laravel-ignition": "^2.0",
"vlucas/phpdotenv": "^5.5",
"wp-cli/wp-cli-bundle": "^2.11",
"wp-media/imagify-plugin": "^2.2",
"wpackagist-plugin/cookie-law-info": "^3.2",
"wpackagist-plugin/relay": "^1.3",
Expand All @@ -61,6 +70,7 @@
"yard/brave-csp": "^1.0",
"yard/brave-hooks": "^2.0",
"yard/config-expander": "^1.0",
"yard/wp-cli-bundle": "^2.0",
"yard/data": "^1.0",
"yard/multisite-url-fixer": "^1.0",
"yard/nutshell": "^2.1",
Expand All @@ -79,16 +89,6 @@
"yard/lando-brave": "^1.0",
"yard/php-cs-fixer-rules": "^1.0"
},
"suggest": {
"gravity/gravityforms": "",
"deliciousbrains-plugin/wp-migrate-db-pro": "",
"plugin/yard-dashboard": "",
"wpengine/advanced-custom-fields-pro": "",
"ypackagist/facetwp": "",
"ypackagist/searchwp": "",
"ypackagist/wp-seopress-pro": "",
"yard/wp-deployer": "dev"
},
"autoload": {
"psr-4": {
"App\\": "web/app/themes/sage/app/"
Expand Down Expand Up @@ -118,12 +118,14 @@
"post-autoload-dump": [
"Roots\\Acorn\\ComposerScripts::postAutoloadDump"
],
"private-setup": [
"composer repo add satis composer https://satis.yard.nl",
"composer repo add satispress composer https://packagist.yard.nl/satispress/",
"composer require yard/brave-private-setup --dev",
"wp acorn setup:dependencies",
"wp acorn setup:deployer"
"private-deployer": [
"composer require yard/wp-deployer --dev"
Comment thread
SimonvanWijhe marked this conversation as resolved.
],
Comment on lines +121 to +123
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🟡 The private-deployer script runs composer require yard/wp-deployer --dev with no version constraint, meaning each developer gets whatever version is latest at install time. If yard/wp-deployer releases a major version (e.g. v2.0) that changes the Host, Loader, Stage, or wp() API used in deploy.php, the deploy tool will silently break for anyone who installs the newer version. Fix by pinning to a semver range, e.g. composer require yard/wp-deployer:^1.5 --dev.

Extended reasoning...

What the bug is

The private-deployer Composer script (composer.json lines 121–123) runs:

"composer require yard/wp-deployer --dev"

No version constraint is specified. While Composer will add a caret constraint based on the currently-available major version when resolving, this means the constraint written into composer.json and composer.lock depends entirely on what version is published at the moment each developer runs the script. Developer A running it today gets ^1.5, developer B running it after a future major release gets ^2.0 — with incompatible API.

The specific code path

deploy.php directly imports and uses the Yard Deployer API:

use Yard\Deployer\Host;
use Yard\Deployer\Loader;
use Yard\Deployer\Stage;
use function Yard\Deployer\wp;

If any of these classes or the wp() function signature changes between major versions, deploy.php will throw a fatal error or silently misbehave on hosts where the newer major version was installed via this unconstrained script.

Why existing code doesn't prevent it

yard/wp-deployer is NOT listed in the require-dev section of composer.json — the private-deployer script is the only installation mechanism. Unlike private-packages and gemeente-packages which run with --no-interaction, this script has no version guard at all. The previous iteration of the code pinned yard/wp-deployer to a feature branch (dev-feat/migrate-to-localhost), which Copilot flagged as brittle. This PR replaced the branch pin with no constraint at all — arguably a step backwards in reproducibility.

Impact

This is a developer setup script, not part of the automated CI/CD pipeline, so it won't cause immediate failures. However, in a boilerplate/template repo shared across a team, running composer private-deployer at different points in time will produce divergent composer.json and composer.lock states. A future yard/wp-deployer v2.0 release with API changes would silently break deploys for any team member who ran the script after the major release, while others on v1.x continue to work.

Step-by-step proof

  1. Developer A runs composer private-deployer today — gets yard/wp-deployer: ^1.5 written to their composer.json + composer.lock.
  2. yard/wp-deployer v2.0 is published, with a renamed Loader class or changed wp() signature.
  3. Developer B runs composer private-deployer next week — gets yard/wp-deployer: ^2.0 written to their files.
  4. Developer B's dep deploy fails with a fatal PHP error because deploy.php still uses the v1.x API.
  5. Meanwhile Developer A's setup continues to work, making the bug hard to reproduce and diagnose.

How to fix

Pin to the version that is confirmed working (v1.5.3 per composer.lock):

"private-deployer": [
    "composer require yard/wp-deployer:^1.5 --dev"
]

This allows patch and minor updates (non-breaking per semver) while protecting against a future major-version API break.

"private-packages": [
Comment thread
SimonvanWijhe marked this conversation as resolved.
"composer require --no-interaction gravity/gravityforms deliciousbrains-plugin/wp-migrate-db-pro plugin/yard-dashboard wpengine/advanced-custom-fields-pro ypackagist/facetwp ypackagist/searchwp ypackagist/wp-seopress-pro"
],
"gemeente-packages": [
"composer require --no-interaction yard/brave-owc"
]
}
}
Loading