Claude Code Plugins

Community-maintained marketplace

Feedback
0
0

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.

Install Skill

1Download skill
2Enable skills in Claude

Open claude.ai/settings/capabilities and find the "Skills" section

3Upload to Claude

Click "Upload skill" and select the downloaded ZIP file

Note: Please verify skill by going through its instructions before using it.

SKILL.md

name drupal-contrib-mgmt
description 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.

Drupal 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):

  1. Check compatibility: Ensure module supports your Drupal core version
  2. Search issue queue for patches: https://www.drupal.org/project/issues/MODULE_NAME?categories=All
  3. Use Drupal Lenient for version requirement issues (see below)
  4. Apply patches via composer.json (see Patch Management section)
  5. 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 ^11 support, module maintainer says it works

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:

  1. Extract node ID from patch filename (e.g., 3432063 from above)
  2. Visit https://www.drupal.org/node/3432063
  3. Look for updated patch in latest comments
  4. 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):

  • REQUEST_TIME constant → Use \Drupal::time()->getRequestTime()
  • user_roles() → Use \Drupal\user\Entity\Role::loadMultiple()
  • file_validate_extensions() → Use file.validator service
  • system_retrieve_file() → No replacement (refactor required)
  • _drupal_flush_css_js() → Use AssetQueryStringInterface::reset()

Info.yml Issues:

  • Update core_version_requirement to include ^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

  1. Always use --with-all-dependencies for module updates
  2. Always run drush updb after composer updates
  3. Test immediately after updates (visit pages, check logs)
  4. Keep patches organized in a patches/ directory
  5. Document patches with descriptive names and comments
  6. Check issue queues first before creating custom patches
  7. Use upgrade_status to validate D11 compatibility
  8. Commit atomically: one module update per commit
  9. Use descriptive commit messages with patch references
  10. 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