mcpField
D
drupal-contrib-mgmt

drupal-contrib-mgmt

github.com/grasmash开发82255 次安装4 周前更新
代码仓库

关于 drupal-contrib-mgmt

面向使用 Composer 管理 Drupal contrib 模块的开发者,提供模块更新、兼容性检查和补丁处理流程。内容重点包括 composer-patches 2.x 的锁文件同步、补丁验证及行为测试。

主要功能

  1. 01更新单个或多个模块
  2. 02检查 Drupal 11 兼容性
  3. 03管理 Drupal Lenient 配置
  4. 04应用并验证 Composer 补丁
  5. 05为补丁编写行为测试
  6. 06排查补丁应用问题

适用场景

  1. 01升级 Drupal contrib 模块版本
  2. 02为模块添加或维护补丁
  3. 03检查补丁锁文件与代码是否同步
  4. 04准备 Drupal 11 升级

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/grasmash/drupal-claude-skills --skill drupal-contrib-mgmt

安装后在 Claude Code 中输入/drupal-contrib-mgmt确认已加载。

包含的文件

除 SKILL.md 外,安装时一起下载的参考资料和脚本。

examples/
create-custom-patch.sh
find-and-apply-patch.sh
major-version-upgrade.sh
update-with-patch.sh
references/
contributing-upstream.md
d11-common-deprecations.md
d11-upgrade-workflow.md
drupal-lenient.md
drupal-patches-workflow.md
finding-and-creating-patches.md
issue-queue-rss-feeds.md
update-patterns.md

详细介绍

Drupal 贡献模块管理

在进行任何由 Composer 管理的贡献模块工作时,都使用此技能:更新模块、添加或调试补丁、解除版本约束限制、为 Drupal 11 做准备,或向上游提交修复。每次使用时都适用核心更新工作流和以下补丁规则;具体任务的详细信息位于 references/ 中(参见末尾的表格)。

核心更新工作流

标准模块更新
# 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 (updatedb rebuilds caches when it finishes)
drush updb -y

# On a deployed environment, use the full deploy tail instead; see the drupal-deploy-safety skill

# CRITICAL: Test by visiting pages to check for fatal errors
# Visit at least one page that uses the updated module
主版本升级

升级到新的主版本时(例如,2.x → 3.x):

  1. 检查兼容性:确保模块支持你的 Drupal 核心版本
  2. 搜索问题队列中的补丁:https://www.drupal.org/project/issues/MODULE_NAME?categories=All
  3. 使用 Drupal Lenient 处理版本要求问题(见下文)
  4. 通过 composer.json 应用补丁(参见补丁管理部分)
  5. 运行 upgrade_status 检查弃用项

检查 Drupal 11 兼容性

三种方法(.info.yml、Composer、drupal.org)以及完整的 upgrade_status 工作流:参见 references/d11-upgrade-workflow.md。

Drupal Lenient 插件

mglaman/composer-drupal-lenient 插件允许安装尚未更新版本要求的模块。

Lenient/core-compat 覆盖:参见 references/drupal-lenient.md — 该参考文档介绍如何根据上游 composer.json/.info.yml 要求验证候选模块(而非本地打过补丁的副本)、运行 composer prohibits 检查,以及何时将模块添加到允许列表或从中移除。

设置
{
  "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"
      ]
    }
  }
}
用法
# Add module to allowed-list, then install
composer require drupal/module_name --with-all-dependencies

补丁管理(cweagans/composer-patches)

重要:使用 2.x 版本以确保可靠地应用补丁。1.x 版本使用 patch 二进制程序,在某些系统上可能会出现问题。2.x 版本默认使用 git apply。

补丁配置
{
  "require": {
    "cweagans/composer-patches": "^2.0"
  },
  "config": {
    "allow-plugins": {
      "cweagans/composer-patches": true
    }
  },
  "extra": {
    "composer-exit-on-patch-failure": true,
    "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"
      }
    },
    "patchLevel": {
      "drupal/core": "-p2"
    }
  }
}
从 1.x 升级到 2.x

如果你使用的是 1.x 版本,并且遇到补丁应用失败:

composer require cweagans/composer-patches:^2.0 --with-all-dependencies

2.x 的主要区别:

  • 使用 git apply,而不是 patch 二进制程序(更可靠)
  • 已移除 enable-patching 选项(补丁始终启用)
  • 错误消息和调试功能更完善
  • 关键 — patches.lock.json 的应用来源:每当 Composer 安装或更新软件包时(composer install、update、reinstall、patches-repatch),v2 都会从 patches.lock.json 应用补丁。这些命令都不会重新读取 composer.json 中的 extra.patches:只有 composer patches-relock 或缺少 patches.lock.json 时,才会重新生成锁文件(composer-patches 2.0.0,Patches::loadLockedPatches())。因此,在 composer.json 中添加补丁后运行 composer install 或 composer update,在重新锁定之前,该补丁不会生效。这是补丁“不断回退”的首要原因:本地开发环境看起来已修复(你手动应用了补丁或运行了 update),但下一次全新安装——CI、队友的环境或新的部署——读取的是过时的锁文件,于是丢弃该补丁。修改 extra.patches 后务必运行 composer patches-relock,并提交 patches.lock.json。
验证补丁是否已应用

三种不同的问题,一项检查即可发现:

  1. 锁文件同步过时(根本原因):补丁已在 composer.json 的 extra.patches 中注册,但由于跳过了 composer patches-relock,从未添加到 patches.lock.json。v2 在 composer install 时从锁文件应用补丁,因此每次全新安装时,该补丁都会被静默跳过。解决方法是重新锁定;这项检查的作用是通过断言 composer.json 中的每个本地补丁都存在于 patches.lock.json 中,来发现被跳过的情况。

  2. 已提交文件与工作区不一致(提交贡献代码的项目):补丁已应用到工作区,但产生的贡献模块文件更改从未提交到 git。任何从已提交的 git 状态部署、且不运行 composer install 的平台(例如 Pantheon)都看不到这些更改,因此生产环境会静默运行未打补丁的代码。本地开发环境看起来正常。

  3. 补丁哈希缓存过期:即使锁文件同步,意外重新安装或供应商更新也可能导致跳过补丁的重新应用。与(1)和(2)相比,这种情况很少见,但同样可以通过已物化文件检查捕获。

解决方案:项目应有一个补丁验证脚本(例如 scripts/verify-patches.sh;本技能不提供该脚本),并在每次推送前以及 CI 中运行。脚本应执行以下操作:

  • 锁文件同步检查:composer.json 中 extra.patches 下的每个本地补丁也必须出现在 patches.lock.json 中,以捕获跳过 patches-relock 的情况。
  • 已物化文件检查(适用于提交 contrib 代码的项目):补丁修改的行必须存在于已提交的 contrib 文件中,以捕获“已打补丁但未提交”的情况。
  • 从 composer.json 中的 extra.patches 派生验证列表,无需手动整理。添加补丁条目后,检查就应能自动将其纳入。
  • 对每个本地补丁(值以 patches/ 开头),解析所有 +++ b/<path> 标头,提取几行有辨识度的新增内容(例如,最多 5 行,每行至少包含 8 个非空白字符,且不是同一补丁中任何 - 行的子串),然后在目标文件中搜索这些内容。注意 drupal/core 软件包的 core/ 路径前缀:核心补丁以 Drupal 根目录为基准(b/core/lib/...,应用时使用 -p2),因此应将路径映射到 <docroot>/core/...,而不是在软件包安装路径前添加前缀。
  • 跳过基于 URL 的补丁(https://...),并显示提示;如果补丁很关键,请在 patches/ 下添加本地镜像。
  • 在 CI 中,于 composer install 之前运行该脚本,以验证已提交的代码树,而非安装后的状态。关键就在于这个执行顺序。

添加新补丁(重新生成锁文件这一步最容易被忘记):

  1. 将 .patch 文件放入 patches/
  2. 在 composer.json 的 extra.patches 下登记该补丁
  3. 运行 composer patches-relock,将补丁添加到 patches.lock.json。如果不执行这一步,第 4 步将不会应用任何补丁(v2 读取的是锁文件,而不是 composer.json)。
  4. 运行 composer reinstall drupal/module_name(或 composer patches-repatch),将补丁应用到工作树;v2 仅在 Composer 安装或更新软件包时才会为其打补丁,因此,若模块已安装,单纯运行 composer install 不会重新为其打补丁。
  5. git add 并提交 composer.json、patches.lock.json 和新的 .patch 文件。如果项目会提交 contrib 代码,也要提交修改后的 contrib 文件——对于从 git 部署且不运行 composer install 的平台,无法自行应用补丁,因此已提交的 contrib 文件必须已经包含补丁。
  6. 为补丁修改的功能编写行为测试(见下文)。
  7. 推送前,在本地运行项目的补丁验证检查。
  8. 要求 CI 在每次推送时重新运行同一验证。

每个补丁都必须附带行为测试。 补丁验证检查是结构性检查:它能证明已提交文件中存在补丁的代码行,但不能证明打过补丁的代码行为正确。补丁可能已应用,却仍未解决任何问题(代码块位置错误、上游重构移动了相关逻辑,或后续补丁撤销了它)。行为测试能让补丁在模块升级时仍可靠:

  • 反向案例:测试补丁要修复的确切边界条件。对于新补丁,先针对未打补丁的模块编写此测试,并确认它因报告的问题而失败,否则就无法证明该测试确实覆盖了这个缺陷。
  • 正向案例:确保正常路径仍然有效(没有回归)。
  • 将测试放在使用该功能的自定义模块的 tests/ 目录中,并在测试的文档块中引用 .patch 文件,以便升级模块的人能够找到它。
  • 如果某个打过补丁的模块没有行为测试,绝不要升级它。 先编写测试,再升级,然后确认测试仍然通过(或者补丁已进入上游,可以移除)。 验证报告提示缺少补丁时:
  • 锁定文件同步失败 → 有人跳过了 composer patches-relock(步骤 3)。修复方法:运行该命令,提交 patches.lock.json 并推送。
  • 补丁文件物化失败 → 有人忘记提交已打补丁的 contrib 文件(步骤 5)。修复方法:在本地运行 composer patches-relock && composer patches-repatch,使用 git add 添加已打补丁的 contrib/core 目录(例如 web/modules/contrib web/core 或 docroot/modules/contrib docroot/core)以及 patches.lock.json,然后提交并推送。

注意事项:

  • “组合补丁”(一个 .patch 文件包含多个针对同一文件的 +++ b/<same_file> 标头,通常是合并了存在冲突代码块的提交)可能会漏检——只要检查接受了任何一行有辨识度的新增代码,即使只部分匹配也会通过。如果发现 patches/ 中的补丁包含多个修改同一文件的代码块,请将其重新生成为干净的单提交差异。
  • PHPCS:提交已打补丁的 contrib 文件可能会触发提交前的 phpcs 任务(例如 GrumPHP),导致上游代码中原有的 sniff 违规报错。请将该任务配置为忽略 contrib、core 和 libraries 目录,而不是“修复”上游代码。
查找补丁

Issue Queue 搜索:https://www.drupal.org/project/issues/MODULE_NAME?categories=All

补丁命名约定:

  • 格式:module-issue-NODEID-COMMENT.patch
  • 示例:audiofield-d11-3432063-12.patch
  • Node ID 是 issue 编号(访问 drupal.org/node/NODEID)

更新后现有补丁失效时:

  1. 从补丁文件名中提取 node ID(例如上面的 3432063)
  2. 访问 https://www.drupal.org/node/3432063
  3. 在最新评论中查找更新后的补丁
  4. 使用新的补丁 URL 更新 composer.json
调试错误和创建本地补丁

编写补丁之前,先在 issue queue 中搜索现有补丁;分步搜索方法和独立克隆仓库的补丁工作流见 references/finding-and-creating-patches.md。

应用补丁
# Fresh checkout: packages are installed and patched from patches.lock.json
composer install

# After ANY change to extra.patches (add, edit, remove), relock first
composer patches-relock

# Re-patch a single module (most common). A plain `composer install` or
# `composer update` does nothing for a package that is already installed at
# the locked version, so its patches are not re-applied.
composer reinstall drupal/module_name

# Re-patch ALL patched dependencies (deletes and reinstalls them)
composer patches-repatch

详细的补丁工作流请参阅: references/drupal-patches-workflow.md

Drupal 11 兼容性工作流

六步 upgrade_status 工作流(分析、识别、修复自定义代码、.info.yml 补丁、宽松列表、验证)见 references/d11-upgrade-workflow.md。

完整更新检查清单

  • 检查当前模块版本:composer show drupal/module_name
  • 在 issue queue 中搜索已知问题
  • 检查模块是否兼容 D11
  • 使用新版本更新 composer.json
  • 如有需要,添加到 drupal-lenient
  • 搜索并应用所需补丁
  • 确认模块上的每个现有补丁都有行为测试,并在升级后运行测试
  • 运行 composer require drupal/module_name:^X.0 --with-all-dependencies
  • 运行 drush updb -y(它会在最后重建缓存;在已部署的环境中运行时,请执行 drupal-deploy-safety 技能中的完整收尾步骤)
  • 运行 drush upgrade_status:analyze module_name
  • 访问相关页面,测试模块功能
  • 检查日志中的 PHP 错误/警告
  • 使用描述性消息提交更改

故障排除

补丁无法应用
# 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
# 5. Either way: composer patches-relock && composer reinstall drupal/module_name
版本冲突
# Error: "drupal/module_name requires drupal/core ^9"
# Add to drupal-lenient allowed-list
补丁已应用
# Error: "patch ... has already been applied"
# Module maintainer merged the patch - remove from composer.json, then
composer patches-relock
composer reinstall drupal/module_name
数据库更新失败
# Error during drush updb
# 1. Check error message carefully
# 2. Last resort only: pm:uninstall DELETES the module's config and stored data.
#    Back up the database first, and never do this on a module holding data you need.
drush pm:uninstall module_name
composer require drupal/module_name --with-all-dependencies
drush pm:enable module_name
drush updb -y

最佳实践

  1. 模块更新时始终使用 --with-all-dependencies
  2. composer 更新后始终运行 drush updb
  3. 更新后立即测试(访问页面、检查日志)
  4. 将补丁整理在 patches/ 目录中
  5. 使用描述性名称和注释记录补丁
  6. 创建自定义补丁之前先检查 issue queue
  7. 使用 upgrade_status 验证 D11 兼容性
  8. 原子化提交:每次提交只更新一个模块
  9. 使用包含补丁引用的描述性提交消息
  10. 尽量精简 drupal-lenient 列表(仅在必要时添加)

生产部署

对于部署已提交 Git 状态的主机(服务器上不运行 composer install),请在不包含开发依赖包的情况下构建并提交 vendor:

composer update drupal/module_name --with-all-dependencies   # the change itself
composer install --no-dev -o                                   # then build the vendor you commit
git add composer.json composer.lock vendor/                    # vendor/autoload.php + vendor/composer/ together
  • 切勿提交之后运行普通 composer install 留下的开发版自动加载器。
  • 将 vendor/autoload.php、vendor/composer/ 和各个包目录一起暂存;部分暂存会导致全站范围的 ComposerAutoloaderInit... not found 致命错误。关于固定 config.autoloader-suffix、三个后缀文件和提交前检查,请参阅 drupal-deploy-safety 技能(§8)。
  • 推送只会更改代码。在目标环境中,运行部署收尾流程(循环执行 updatedb → cache:rebuild → config:import → cache:rebuild → deploy:hook,然后验证),不要只运行 drush cr;请参阅 drupal-deploy-safety 技能(§2)。各主机的远程 Drush 命令形式请参阅 drupal-config-mgmt 技能。

开发和贡献 Contrib 模块

符号链接开发工作流以及 drupal.org 问题分支 / 合并请求工作流,详见 references/contributing-upstream.md。包含完整步骤的更新示例(已知补丁、D11 修复、破坏性大版本升级)详见 references/update-patterns.md。

参考资料

文件适用场景
references/d11-upgrade-workflow.md检查模块是否支持 Drupal 11,或运行 upgrade_status 扫描并修复扫描结果
references/d11-common-deprecations.md替换特定的弃用常量、函数、类常量或 Twig 过滤器
references/drupal-lenient.md判断模块是否应加入或可以从 drupal-lenient 允许列表中移除
references/finding-and-creating-patches.md遇到 Drupal 错误并查找现有补丁,或从干净克隆中编写新的本地补丁
references/drupal-patches-workflow.md任何更深入的 composer-patches 问题:插件命令、远程 / 本地 / MR 差异补丁、在已打补丁的模块上叠加补丁
references/issue-queue-rss-feeds.md以编程方式查询 drupal.org 问题队列(RSS 筛选器、curl/WebFetch 解析)
references/update-patterns.md按照完整步骤处理已打补丁的更新、D11 contrib 修复,或可回滚的大版本升级
references/contributing-upstream.md通过符号链接在本地开发 contrib 模块,或通过问题分支和合并请求贡献修复
examples/可运行的 shell 示例,演示上述相同场景

参考链接

评论

支持 Markdown