Claude-skill-registry drupal-contrib-mgmt
Comprehensive guide for managing Drupal contributed modules via Composer, including updates, patches, version compatibility, and Drupal 11 upgrades. Use when updating modules or resolving dependency issues.
git clone https://github.com/majiayu000/claude-skill-registry
T=$(mktemp -d) && git clone --depth=1 https://github.com/majiayu000/claude-skill-registry "$T" && mkdir -p ~/.claude/skills && cp -r "$T/skills/data/drupal-contrib-mgmt" ~/.claude/skills/majiayu000-claude-skill-registry-drupal-contrib-mgmt && rm -rf "$T"
skills/data/drupal-contrib-mgmt/SKILL.mdDrupal Contrib Module Management
Core Update Workflow
Standard Module Update
# Update a single module composer require drupal/module_name --with-all-dependencies # Update to specific version composer require drupal/module_name:^3.0 --with-all-dependencies # Update multiple modules composer require drupal/module_a drupal/module_b --with-all-dependencies # After any update, ALWAYS run database updates drush updb -y # Clear cache if needed drush cr # CRITICAL: Test by visiting pages to check for fatal errors # Visit at least one page that uses the updated module
Major Version Upgrades
When upgrading to a new major version (e.g., 2.x → 3.x):
- Check compatibility: Ensure module supports your Drupal core version
- Search issue queue for patches:
https://www.drupal.org/project/issues/MODULE_NAME?categories=All - Use Drupal Lenient for version requirement issues (see below)
- Apply patches via composer.json (see Patch Management section)
- Run upgrade_status to check for deprecations
Checking Drupal 11 Compatibility
Three methods to check if a module is D11 compatible (in order of preference):
Method 1: Check .info.yml File (Fastest, Most Reliable)
# Check the module's .info.yml file for core_version_requirement cat docroot/modules/contrib/MODULE_NAME/MODULE_NAME.info.yml | grep core_version_requirement
What to look for:
core_version_requirement: ^9.5 || ^10 || ^11 # ✅ D11 compatible core_version_requirement: ^8 || ^9 || ^10 || ^11 # ✅ D11 compatible core_version_requirement: ^9 || ^10 # ❌ Not D11 compatible yet
Example:
$ cat docroot/modules/contrib/admin_toolbar/admin_toolbar.info.yml | grep core_version core_version_requirement: ^9.5 || ^10 || ^11 # ✅ This module declares D11 support!
Method 2: Use Composer Commands (Works Before Installing)
# Check what versions are available and their constraints composer show drupal/MODULE_NAME --all | grep -A5 "^versions" # Check currently installed version composer show drupal/MODULE_NAME | grep versions
What to look for:
- Version number (e.g., 3.6.2)
- Check Drupal.org for release notes mentioning D11
Method 3: Check Drupal.org Project Page
Only use as fallback when above methods aren't conclusive.
https://www.drupal.org/project/MODULE_NAME
Look for:
- Latest release notes mentioning "Drupal 11"
- Module page header showing D11 compatibility badge
- Issue queue for D11 compatibility issues
Important Notes:
- ⚠️ Module may declare D11 support but still have deprecation warnings
- ⚠️ upgrade_status warnings don't mean module is incompatible
- ⚠️ "Check manually" status often means runtime version checks (false positive)
- ✅ If .info.yml declares
support, module maintainer says it works^11
Real-World Examples:
# admin_toolbar - Already D11 compatible $ cat docroot/modules/contrib/admin_toolbar/admin_toolbar.info.yml | grep core_version core_version_requirement: ^9.5 || ^10 || ^11 # But upgrade_status shows warnings about _drupal_flush_css_js() # This is a FALSE POSITIVE - module handles it with version checks # audiofield - Already D11 compatible $ cat docroot/modules/contrib/audiofield/audiofield.info.yml | grep core_version core_version_requirement: ^8 || ^9 || ^10 || ^11 # Has deprecation warnings but maintainer declares D11 support
Drupal Lenient Plugin
The
mglaman/composer-drupal-lenient plugin allows installing modules that haven't updated their version requirements yet.
Setup
{ "require": { "mglaman/composer-drupal-lenient": "^1.0" }, "config": { "allow-plugins": { "mglaman/composer-drupal-lenient": true } }, "extra": { "drupal-lenient": { "allowed-list": [ "drupal/module_name", "drupal/another_module" ] } } }
Usage
# Add module to allowed-list, then install composer require drupal/module_name --with-all-dependencies
Patch Management (cweagans/composer-patches)
Patch Configuration
{ "require": { "cweagans/composer-patches": "^1.7" }, "config": { "allow-plugins": { "cweagans/composer-patches": true } }, "extra": { "patches": { "drupal/module_name": { "Description of patch": "https://www.drupal.org/files/issues/2024-01-15/module-issue-1234567-8.patch", "Local patch": "patches/custom-fix.patch" } }, "enable-patching": true, "patchLevel": { "drupal/core": "-p2" } } }
Finding Patches
Issue Queue Search:
https://www.drupal.org/project/issues/MODULE_NAME?categories=All
Patch Naming Convention:
- Format:
module-issue-NODEID-COMMENT.patch - Example:
audiofield-d11-3432063-12.patch - Node ID is the issue number (visit
)drupal.org/node/NODEID
When Existing Patches Fail After Update:
- Extract node ID from patch filename (e.g.,
from above)3432063 - Visit
https://www.drupal.org/node/3432063 - Look for updated patch in latest comments
- Update composer.json with new patch URL
Creating Local Patches
# Method 1: Git diff cd docroot/modules/contrib/module_name # Make your changes git diff > /path/to/project/patches/module-custom-fix.patch # Method 2: diff command diff -Naur original/file.php modified/file.php > patches/module-fix.patch # Apply in composer.json { "extra": { "patches": { "drupal/module_name": { "Custom fix description": "patches/module-custom-fix.patch" } } } }
Patch Application
# Install with patches composer install # If patches fail, composer will error # Update or remove failing patches, then retry composer install # Force re-patch composer update drupal/module_name --with-all-dependencies
Drupal 11 Compatibility Workflow
Step 1: Analyze Readiness
# Scan all modules drush upgrade_status:analyze --all # Scan specific modules drush upgrade_status:analyze module1 module2 module3 # Machine-readable output drush upgrade_status:analyze --all --format=json > d11-report.json drush upgrade_status:analyze --all --format=codeclimate > d11-report-ci.json # Scan only custom code drush upgrade_status:analyze --all --ignore-contrib # Scan only contrib drush upgrade_status:analyze --all --ignore-custom
Step 2: Identify Issues
Major Issues (blocking):
constant → UseREQUEST_TIME\Drupal::time()->getRequestTime()
→ Useuser_roles()\Drupal\user\Entity\Role::loadMultiple()
→ Usefile_validate_extensions()
servicefile.validator
→ No replacement (refactor required)system_retrieve_file()
→ Use_drupal_flush_css_js()AssetQueryStringInterface::reset()
Info.yml Issues:
- Update
to includecore_version_requirement^11 - Example:
core_version_requirement: ^9 || ^10 || ^11
Step 3: Fix Custom Code
Example: Inject Time Service
use Drupal\Core\Datetime\TimeInterface; class MyController extends ControllerBase { protected $time; public function __construct(TimeInterface $time) { $this->time = $time; } public static function create(ContainerInterface $container) { return new static( $container->get('datetime.time') ); } public function myMethod() { // OLD: $timestamp = REQUEST_TIME; $timestamp = $this->time->getRequestTime(); } }
Example: Replace user_roles()
// OLD: $roles = user_roles(TRUE); // NEW: use Drupal\user\Entity\Role; $roles = Role::loadMultiple(); $role_options = []; foreach ($roles as $role_id => $role) { if ($role_id !== 'anonymous') { $role_options[$role_id] = $role->label(); } }
Step 4: Create .info.yml Patches
# Create patch for contrib module cd docroot/modules/contrib/module_name git diff module.info.yml > /path/to/patches/module-d11-info.patch # Patch content: --- a/module.info.yml +++ b/module.info.yml @@ -2,7 +2,7 @@ name: Module Name type: module description: Module description -core_version_requirement: ^9 || ^10 +core_version_requirement: ^9 || ^10 || ^11
Step 5: Apply Patches & Update Lenient List
{ "extra": { "patches": { "drupal/module_name": { "Drupal 11 .info.yml support": "patches/module-d11-info.patch" } }, "drupal-lenient": { "allowed-list": [ "drupal/module_name" ] } } }
composer install drush updb -y drush cr
Step 6: Verify Fixes
# Re-scan to confirm issues resolved drush upgrade_status:analyze module_name # Should show "No known issues found"
Complete Update Checklist
- Check current module version:
composer show drupal/module_name - Search issue queue for known issues
- Check if module is D11 compatible
- Update composer.json with new version
- Add to drupal-lenient if needed
- Search for and apply necessary patches
- Run
composer require drupal/module_name:^X.0 --with-all-dependencies - Run
drush updb -y - Run
drush cr - Run
drush upgrade_status:analyze module_name - Test module functionality by visiting relevant pages
- Check for PHP errors/warnings in logs
- Commit changes with descriptive message
Troubleshooting
Patch Won't Apply
# Error: "Cannot apply patch..." # 1. Check if module version changed composer show drupal/module_name # 2. Search issue queue for updated patch # Visit drupal.org/node/NODEID (from patch filename) # 3. Update composer.json with new patch URL # 4. Or remove patch if merged upstream
Version Conflict
# Error: "drupal/module_name requires drupal/core ^9" # Add to drupal-lenient allowed-list
Patch Already Applied
# Error: "patch ... has already been applied" # Module maintainer merged the patch - remove from composer.json
Database Update Fails
# Error during drush updb # 1. Check error message carefully # 2. May need to disable module, update, re-enable drush pm:uninstall module_name composer require drupal/module_name --with-all-dependencies drush pm:enable module_name drush updb -y
Best Practices
- Always use
for module updates--with-all-dependencies - Always run
after composer updatesdrush updb - Test immediately after updates (visit pages, check logs)
- Keep patches organized in a
directorypatches/ - Document patches with descriptive names and comments
- Check issue queues first before creating custom patches
- Use upgrade_status to validate D11 compatibility
- Commit atomically: one module update per commit
- Use descriptive commit messages with patch references
- Keep drupal-lenient list minimal (only when necessary)
Developing Contrib Modules Locally
When actively developing a contrib module for drupal.org, use this workflow to avoid constantly updating via composer:
Symlink Development Workflow
# 1. Set up module repository in temp location cd /tmp git clone git@git.drupal.org:project/module_name.git cd module_name # Make your changes... # 2. Remove composer-installed version and symlink your dev copy cd /path/to/project rm -rf docroot/modules/contrib/module_name ln -s /tmp/module_name docroot/modules/contrib/module_name # 3. Develop and test # Make changes in /tmp/module_name # Test immediately in your Drupal site drush cr # Clear cache as needed # 4. When ready to publish cd /tmp/module_name git add -A git commit -m "Your changes" git push origin 1.0.x # 5. Clean up: remove symlink and reinstall from composer cd /path/to/project rm docroot/modules/contrib/module_name composer install # Reinstalls from drupal.org
Benefits:
- Test changes immediately without composer update cycles
- Keep git history in the module's own repo
- Easy to commit and push changes
- No risk of accidentally committing module code to main project
Important Notes:
- Don't forget to remove the symlink before committing project changes
- Clear Drupal cache after changes:
drush cr - When done developing, always reinstall via composer to ensure clean state
- Useful for fixing autoloader issues, adding features, or troubleshooting
Example: Fixing recurly_commerce_api autoloader issue
# Module needed composer.json autoload section cd /tmp/recurly_commerce_api # Edit composer.json to add autoload section git commit -m "Add PSR-4 autoload configuration" git push origin 1.0.x # Back in main project rm docroot/modules/contrib/recurly_commerce_api composer install # Gets latest with fix drush cr
Common Patterns
Pattern: Update Module with Known Patch
# 1. Find patch in issue queue # 2. Add to composer.json patches section # 3. Update module composer require drupal/module_name:^3.0 --with-all-dependencies drush updb -y drush cr # 4. Test # 5. Commit git add composer.json composer.lock patches/ git commit -m "Update module_name to 3.0 with D11 compatibility patch"
Pattern: Fix Contrib D11 Issue
# 1. Scan for issues drush upgrade_status:analyze module_name # 2. Create info.yml patch if needed cd docroot/modules/contrib/module_name # Edit module.info.yml to add ^11 git diff module.info.yml > ../../../patches/module-d11-info.patch # 3. Add patch to composer.json # 4. Apply composer install drush cr # 5. Verify drush upgrade_status:analyze module_name
Pattern: Major Version Upgrade with Breaking Changes
# 1. Read CHANGELOG/UPDATE.md for breaking changes # 2. Check issue queue for upgrade path documentation # 3. Backup database before upgrade drush sql:dump > backup-before-update.sql # 4. Update module composer require drupal/module_name:^3.0 --with-all-dependencies # 5. Run updates drush updb -y # 6. Check for errors drush watchdog:show --severity=Error --count=20 # 7. Test thoroughly # 8. If issues, can rollback: # git checkout composer.json composer.lock # composer install # drush sql:cli < backup-before-update.sql
Reference Links
- Composer Patches: https://github.com/cweagans/composer-patches
- Drupal Lenient: https://github.com/mglaman/composer-drupal-lenient
- Upgrade Status Module: https://www.drupal.org/project/upgrade_status
- Drupal 11 Deprecations: https://www.drupal.org/about/core/policies/core-change-policies/drupal-deprecation-policy
- Patch Naming Standards: https://www.drupal.org/node/1054616