$npx -y skills add edutrul/drupal-ai --skill drupal-contrib-mgmtDrupal contrib module upgrade triage — Drupal.org issue queue research, local patch creation (git diff), composer-patches workflows, Drupal 11 compatibility via core_version_requirement, Drupal Lenient handling, and upgrade_status analysis. Not for generic Composer dependency man
| 1 | # Drupal Contrib Module Management |
| 2 | |
| 3 | ## Core Update Commands |
| 4 | |
| 5 | ```bash |
| 6 | composer require drupal/module_name --with-all-dependencies |
| 7 | composer require drupal/module_name:^3.0 --with-all-dependencies |
| 8 | drush updb -y && drush cr |
| 9 | ``` |
| 10 | |
| 11 | ## Checking Drupal 11 Compatibility |
| 12 | |
| 13 | Fastest method — check `.info.yml`: |
| 14 | ```bash |
| 15 | cat docroot/modules/contrib/MODULE_NAME/MODULE_NAME.info.yml | grep core_version_requirement |
| 16 | ``` |
| 17 | |
| 18 | ```yaml |
| 19 | core_version_requirement: ^9.5 || ^10 || ^11 # D11 compatible |
| 20 | core_version_requirement: ^9 || ^10 # Not D11 compatible |
| 21 | ``` |
| 22 | |
| 23 | - `upgrade_status` warnings ≠ incompatible — module may still declare `^11` support |
| 24 | - "Check manually" status is often a false positive (runtime version checks) |
| 25 | |
| 26 | ## Drupal Lenient Plugin |
| 27 | |
| 28 | For modules that haven't updated version requirements yet: |
| 29 | |
| 30 | ```json |
| 31 | { |
| 32 | "require": { "mglaman/composer-drupal-lenient": "^1.0" }, |
| 33 | "config": { "allow-plugins": { "mglaman/composer-drupal-lenient": true } }, |
| 34 | "extra": { |
| 35 | "drupal-lenient": { "allowed-list": ["drupal/module_name"] } |
| 36 | } |
| 37 | } |
| 38 | ``` |
| 39 | |
| 40 | ## Patch Management (cweagans/composer-patches) |
| 41 | |
| 42 | **IMPORTANT**: Use 2.x — uses `git apply` (reliable). Version 1.x uses `patch` binary (fragile). |
| 43 | |
| 44 | ```json |
| 45 | { |
| 46 | "require": { "cweagans/composer-patches": "^2.0" }, |
| 47 | "config": { "allow-plugins": { "cweagans/composer-patches": true } }, |
| 48 | "extra": { |
| 49 | "composer-exit-on-patch-failure": true, |
| 50 | "patches": { |
| 51 | "drupal/module_name": { |
| 52 | "Description of patch - Issue #3552531": "patches/module-fix-3552531-2.patch" |
| 53 | } |
| 54 | } |
| 55 | } |
| 56 | } |
| 57 | ``` |
| 58 | |
| 59 | **2.x gotcha**: Hash-based caching — patches can silently go missing after reinstalls. |
| 60 | |
| 61 | ### Verifying Patches Are Applied |
| 62 | |
| 63 | ```bash |
| 64 | ./scripts/verify-patches.sh # Check all critical patches |
| 65 | ./scripts/verify-patches.sh --fix # Auto-reinstall affected modules |
| 66 | ``` |
| 67 | |
| 68 | Adding a new patch to verification — edit `scripts/verify-patches.sh`: |
| 69 | ```bash |
| 70 | CRITICAL_PATCHES=( |
| 71 | "docroot/modules/contrib/MODULE:src/File.php:patternToFind:Description" |
| 72 | ) |
| 73 | ``` |
| 74 | |
| 75 | **When patches go missing**: |
| 76 | 1. `./scripts/verify-patches.sh` to identify |
| 77 | 2. `composer reinstall drupal/module_name` or `./scripts/verify-patches.sh --fix` |
| 78 | 3. If still failing: `composer update --lock` |
| 79 | |
| 80 | ### Finding Patches — Search BEFORE Creating |
| 81 | |
| 82 | **CRITICAL**: Always search drupal.org issue queue before writing a custom patch. |
| 83 | |
| 84 | 1. Extract exact error string: e.g. `"Unsupported operand types: array + null"` |
| 85 | 2. Search: `https://www.drupal.org/project/drupal/issues?text=Unsupported+operand+types+array+null` |
| 86 | 3. Look for RTBC (Reviewed & Tested by Community) issues with `.patch` attachments |
| 87 | 4. Download to `patches/` and add to `composer.json` |
| 88 | |
| 89 | **Patch naming**: `module-issue-NODEID-COMMENT.patch` (e.g. `audiofield-d11-3432063-12.patch`) |
| 90 | |
| 91 | **When existing patch fails after update**: |
| 92 | - Extract node ID from filename → visit `drupal.org/node/NODEID` → find updated patch in latest comments |
| 93 | |
| 94 | ### Creating Local Patches |
| 95 | |
| 96 | **Always patch from a separate clone of the contrib repo, not the installed vendor copy.** |
| 97 | |
| 98 | ```bash |
| 99 | cd ~/Sites && git clone git@git.drupal.org:project/module_name.git module_name-contrib |
| 100 | cd module_name-contrib && git checkout 1.0.3 # match installed version |
| 101 | # make changes... |
| 102 | git diff > ~/Sites/your-project/patches/module_name-custom-fix.patch |
| 103 | ``` |
| 104 | |
| 105 | Then add to `composer.json` and run `composer reinstall drupal/module_name`. |
| 106 | |
| 107 | ### Patch Application |
| 108 | |
| 109 | ```bash |
| 110 | composer install # Install with patches |
| 111 | composer reinstall drupal/module_name # Re-patch a single module |
| 112 | composer patches-repatch # Re-patch all patched deps |
| 113 | ``` |
| 114 | |
| 115 | ## Drupal 11 Deprecation Quick Reference |
| 116 | |
| 117 | | Deprecated | Replacement | |
| 118 | |---|---| |
| 119 | | `REQUEST_TIME` | `\Drupal::time()->getRequestTime()` | |
| 120 | | `user_roles()` | `\Drupal\user\Entity\Role::loadMultiple()` | |
| 121 | | `file_validate_extensions()` | `file.validator` service | |
| 122 | | `_drupal_flush_css_js()` | `AssetQueryStringInterface::reset()` | |
| 123 | |
| 124 | ## Upgrade Status Scan |
| 125 | |
| 126 | ```bash |
| 127 | drush upgrade_status:analyze --all |
| 128 | drush upgrade_status:analyze module1 module2 |
| 129 | drush upgrade_status:analyze --all --ignore-contrib # custom code only |
| 130 | ``` |
| 131 | |
| 132 | ## Troubleshooting |
| 133 | |
| 134 | | Error | Solution | |
| 135 | |---|---| |
| 136 | | `Cannot apply patch` | Module version changed — find updated patch at `drupal.org/node/NODEID` | |
| 137 | | `requires drupal/core ^9` | Add to `drupal-lenient` allowed-list | |
| 138 | | `patch has already been applied` | Patch merged upstream — remove from composer.json | |
| 139 | | `drush updb` fails | Try uninstall → update → re-enable module | |