fix(drupal-contrib): install Drush via Guzzle alias on Drupal 12 HEAD#181
Merged
Conversation
Drupal core `main` moved to `guzzlehttp/guzzle: "^8.0"` on 2026-07-24
(drupal.org #3612544) while every Drush version still requires `^7.0`.
The `skipto` D12 cell has failed since, with all three `ddev poser`
attempts dying at dependency resolution:
- drush/drush 14.x-dev requires guzzlehttp/guzzle ^7.0 -> satisfiable
by guzzlehttp/guzzle[7.5.x-dev, ..., 7.15.x-dev]
- You can only install one version of a package ...: guzzlehttp/guzzle[
..., 8.0.0, 8.0.x-dev]
No `web/index.php` gets created, which is the assertion that fails. The
break is module-agnostic — any module on D12 hits it, and the D11 cells
pass. Contrib was green through the 07-24 nightly and red from 07-25.
#178 fixed the same conflict in drupal-core and deliberately scoped this
template out, because its technique does not transfer: there the exact
Guzzle version is read from `composer.lock` after `composer install` has
already succeeded, but `ddev poser` resolves core and Drush in a single
solve, so the solve fails before any lock exists (and poser deletes
`composer.contrib.lock` afterwards). #178's Notes and #179's Related
section both call for a separately tested approach here. This is it.
- When a poser attempt fails *and* its output shows the Drush/Guzzle
conflict, add `"guzzlehttp/guzzle": "<8.x> as 7.99.0"` to the module's
`require-dev` and retry immediately. `expand_composer_json.php` merges
root `require-dev` into `composer.contrib.json`, so the alias reaches
the install. The exact version comes from packagist, since Composer
rejects a range as an alias source and there is no lock to read.
- Gated on that specific failure, so D11 (core requires Guzzle `^7`)
keeps resolving untouched, and the workaround self-clears the day Drush
is fixed. Removal is tracked in #179.
- Capture each attempt's output to its own file. `$SETUP_LOG` is
append-only across workspace restarts, so grepping it could match a
stale conflict from an earlier start and wrongly pin Guzzle 8 on a D11
build.
- Mirror the fallback in `drupal-contrib/scripts/test-issue-branches.sh`,
and capture poser's exit code there — the previous `cmd | tail -10`
form tested `tail`'s status, so a poser failure went undetected.
- Make the PR smoke cell `skipto` D12 instead of `token` D11. The PR
matrix ran only D11, which is why this reached main green. Same
one-workspace cost; D12 tracks core HEAD, so it breaks first, and the
D11 cells still run on push-to-main and nightly.
Unlike drupal-core, where the alias lands in gitignored
`composer.local.json`, here it lands in the module's tracked
`composer.json`. The existing `git update-index --skip-worktree
composer.json` call covers it, same as the drush `require-dev` line.
Verified against real DDEV on `skipto` with `DRUPAL_CORE=^12`: plain
poser exits 1 with the CI conflict; the fallback installs
`drupal/core dev-main` + `drush/drush 14.x-dev` + `guzzlehttp/guzzle
8.0.0` together; `web/index.php` present; `drush si minimal` reports
`[OK] Installation complete`; `Database: Connected`; `drush en skipto`
installs and `pm:list --status=enabled` lists it; `drush status` reports
Drupal 12.0-dev. Also `terraform fmt -recursive` clean, `terraform
validate` and `terraform test` pass for drupal-contrib (6 passed),
`bash -n` clean on the script, workflow YAML parses with no new
actionlint findings.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
The
skiptoD12 cell of the contrib integration tests has failed since 2026-07-25 (run 30165116093). All threeddev poserattempts die at dependency resolution:No
web/index.phpgets created, which is the assertion that fails. Same root cause as #178: coremainmoved toguzzlehttp/guzzle: "^8.0"on 2026-07-24 (drupal.org #3612544) while every Drush version still requires^7.0. The break is module-agnostic — any module on D12 hits it, the D11 cells pass, and contrib was green through the 07-24 nightly and red from 07-25. Not a stale issue fork: thetoken issue forkcell passed in the same run.#178 fixed drupal-core and deliberately scoped this template out, because its technique does not transfer. There the exact Guzzle version is read from
composer.lockaftercomposer installhas already succeeded; hereddev poserresolves core and Drush in a single solve, so the solve fails before any lock exists (and poser deletescomposer.contrib.lockafterwards). #178's Notes and #179's Related section both ask for a separately tested approach. This is it."guzzlehttp/guzzle": "<8.x> as 7.99.0"to the module'srequire-devand re-run immediately.expand_composer_json.phpmerges rootrequire-devintocomposer.contrib.json, so the alias reaches the install. The version comes from packagist, since Composer rejects a range as an alias source (the alias source must be an exact version) and there is no lock to read.^7) keep resolving untouched, and the workaround self-clears the day Drush is fixed. Removal is tracked in Revert Drush/Guzzle 8 workaround once Drush supports Guzzle 8 #179.$SETUP_LOGis append-only across workspace restarts, so grepping it could match a stale conflict from an earlier start and wrongly pin Guzzle 8 on a D11 build.drupal-contrib/scripts/test-issue-branches.sh, and capture poser's exit code there — the previousddev poser 2>&1 | tail -10form testedtail's status, so a poser failure went undetected withoutpipefail.skiptoD12 instead oftokenD11. The PR matrix ran only D11, which is why this class of breakage reached main green while every PR stayed green. Same one-workspace cost; D12 tracks core HEAD so it breaks first, and the D11 cells still run on push-to-main and nightly.Test plan
terraform fmt -recursivecleanterraform validatepasses fordrupal-contribterraform testpasses fordrupal-contrib(6 passed)bash -n drupal-contrib/scripts/test-issue-branches.shcleanactionlintreports no new findings (only pre-existing nits in theif: falseself-hosted jobs)skiptowithDRUPAL_CORE=^12, following the same steps as the template: plainddev poserexits 1 with the exact conflict above8.0.0(the only stable 8.x), the retry exits 0 and installsdrupal/core dev-main+drush/drush 14.x-dev+guzzlehttp/guzzle 8.0.0togetherweb/index.phppresent,drush si minimal→[OK] Installation complete,drush status→Database: Connectedand Drupal 12.0-dev,drush en skipto→ installed and listed bypm:list --status=enabledskiptoitself is D12-compatible today (it enables cleanly on coredev-main), since it is now the PR gatedrupal-contribD12 workspace from the pushed template — this PR's own CI run now covers that, which is the point of the matrix changeNotes
composer.json, not in gitignoredcomposer.local.jsonas in drupal-core. The existinggit update-index --skip-worktree composer.jsoncall covers it, same as the drushrequire-devline, and the contrib workflow has no dirty-tree assertion. Revert Drush/Guzzle 8 workaround once Drush supports Guzzle 8 #179 has been updated with the contrib-specific removal steps.drupal/core-recommendeddoes not pin Guzzle itself — it currently does not (core requires the^8.0range and the metapackage carries no Guzzle entry). If core-recommended later pins a different exact version, the packagist lookup would need to read that pin instead.🤖 Generated with Claude Code