I wrote a blog post a couple of years ago about patching dependencies using Composer. This remains a critical part of my personal (and recommended) development AND devops workflow. However, every once in a very rare while I run into a problem where a patch doesn’t apply. And then I start scratching my head (as one does when a common practice suddenly fails them). Even more irksome is that typically this is a silent failure… meaning that I don’t get the patch I want, but I also don’t get any fatal errors that might indicate why.
This post will dig into the problem and offer a couple of solutions!
Background on Composer Patches
I think there are two critical pieces of knowledge for you to have regarding patching with composer, and I have one best practice to add in just for good luck.
Firstly, Composer itself does not support patching. However, this absolutely critical feature is added via the cweagans/composer-patches plugin (and it works like a dream). I include it on every project as part of the base composer.json file even if I’m not patching anything (because I know sooner or later, I will have to patch something).
Secondly, patching via composer is essentially the same as using git to patch. So, if you’ve ever run the git apply command, or generated a git patch, then you should be very familiar with what’s going on behind the scenes!
Finally, I strongly recommend never storing packages locally in your codebase. The composer patches plugin works fine with local patches, but storing them on an issue queue on Drupal.org or Github helps keep you honest. You shouldn’t be doing feature development, you don’t want to maintain patches forever, and keeping patches locked in your codebase is a surefire way to do both of these things.
When Patching Fails
Normally when a patch fails to apply with composer, it fails exactly the same as it might with git. Here’s a recent failure I got on a Drupal project related to an old Google Analytics module patch:
Gathering patches for dependencies. This might take a minute. - Installing drupal/google_analytics (4.0.0): Extracting archive - Applying patches for drupal/google_analytics https://www.drupal.org/files/issues/2021-11-25/google_analytics-3246597-7.patch (https://www.drupal.org/project/google_analytics/issues/3246597) https://www.drupal.org/files/issues/2021-11-25/google_analytics-3246597-7.patch (3246597 - Add dependency on drupal:path_alias) Could not apply patch! Skipping. The error was: Cannot apply patch https://www.drupal.org/files/issues/2021-11-25/google_analytics-3246597-7.patch
This is great, by the way. When a patch fails you want it to fail spectacularly. And in this case, it did. As it turns out, I no longer need this patch. Great! I just needed to remove it from the codebase and update my composer.lock file. In other cases, maybe you need a more recent patch. Also great! Super easy, go back to the issue queue and grab the newer version of the patch and re-apply.
What happens though when the patch doesn’t err out, but it also doesn’t apply. This is a much more concerning and confusing situation. Based on your output, it should have applied, but when you look at the code in question the patch didn’t get applied.
Unfortunately, there’s not a single answer to this problem. But in my experience there are only two scenarios where this might apply.
File Renaming
The first silent failure scenario comes from a really specific type of patch: one where the patch renames a file. There is an open issue on the composer-patches project related to this and it’s one that causes major consternation (so I’m trying to save you the frustration).
TLDR if you are trying to rename (or even delete) a file during a patch, check to make sure you have gpatch installed. You can test this on a Mac with homebrew:
$ brew info gpatch | grep "gpatch:" gpatch: stable 2.7.5 (bottled)
If you don’t have gpatch, try installing it and re-patching. This has instantly fixed the problem for me in the past!
Missing composer-patches / Dev Dependency on composer-patches
Another more recent discovery for this scenario was that a patch wasn’t applying at all. This one is a little more (potentially) niche of a problem… but I think it’s an easy enough mistake to make we should flag it here.
Remember, you cannot patch with composer without composer-patches being installed. So, first confirm that it’s in your composer.json. The next question, is if you are doing something that might strip composer-patches. I helped a customer this week that struggled with exactly this scenario. They had installed composer-patches (good) but they had it as a require-dev dependency instead of as a require dependency. So, when the deployment was occurring (and the composer command was run for production) it was excluding dev dependencies. So composer-patches was NOT included. So patches did NOT apply.
composer install --no-dev --no-interaction --optimize-autoloader
I strongly recommended adding composer-patches as a normal dependency and not a dev one!
Again, this instantly fixed the problem.
In Conclusion
Silent failures are so so hard to debug and troubleshoot because… there’s just nothing. Thankfully in the 5+ years I’ve been using composer-patches I have only rarely run into these issues. While annoying, they are predictable. Hopefully these situations help point you in the right direction if you too experience silent composer patch failures!
Dependency trees can be massive, and with the upcoming Drupal 10 release PHP 8.1 could have a significant impact on your project.