Skip to main content

Apply Drupal 9 compatibility patches with Composer

An article from ComputerMinds - Building with Drupal in the UK since 2005
12th Jan 2021

James Williams

Senior Developer
Hey, you seem to look at this article a lot! Why not Bookmark this article so you can find it easily in the future?

Update! Since this article was written, a new 'lenient' composer endpoint has been created on Drupal.org to support using modules with Drupal 9 that haven't been marked as compatible with it yet. See the documentation, which boils down to adding a new common entry under 'repositories' in your composer.json, above the usual https://packages.drupal.org/8 one. The rest of this article is still useful for understanding how patching fits into composer's workflow.

The vast majority of community-contributed Drupal 8 modules now have releases that are compatible with Drupal 9, but what can you do if you need to use a module that doesn’t? Well, you’re likely to find a compatibility patch in the project's issue queue. But the tool most of us use with composer to apply patches, cweagans/composer-patches, is a plugin that can only make its changes after composer reads a package's metadata about its compatibility (and dependencies). So for contrib modules that haven't yet committed their patches, attempting to apply the patch in the usual way doesn't help. For example, before commerce_migrate was compatible with Drupal 9 (update: it is now!), when I tried applying the necessary patch this way, composer just gave me an enormous and confusing error:

Your requirements could not be resolved to an installable set of packages.

  Problem 1
    - Conclusion: don't install drupal/commerce_migrate 2.0.0-rc3
    - Conclusion: don't install drupal/commerce_migrate 2.0.0-rc2
    - Conclusion: remove drupal/core 9.0.9
    - Installation request for drupal/commerce_migrate ^2.0@RC -> satisfiable by drupal/commerce_migrate[2.0.0-rc1, 2.0.0-rc2, 2.0.0-rc3].
    - Conclusion: don't install drupal/core 9.0.9
    - drupal/commerce_migrate 2.0.0-rc1 requires drupal/core ^8.7 -> satisfiable by drupal/core[8.7.0, 8.7.0-alpha1, 8.7.0-alpha2, 8.7.0-beta1, 8.7.0-beta2, 8.7.0-rc1, 8.7.1, 8.7.10, 8.7.11, 8.7.12, 8.7.13, 8.7.14, 8.7.2, 8.7.3, 8.7.4, 8.7.5, 8.7.6, 8.7.7, 8.7.8, 8.7.9, 8.8.0, 8.8.0-alpha1, 8.8.0-beta1, 8.8.0-rc1, 8.8.1, 8.8.10, 8.8.11, 8.8.12, 8.8.2, 8.8.3, 8.8.4, 8.8.5, 8.8.6, 8.8.7, 8.8.8, 8.8.9, 8.9.0, 8.9.0-beta1, 8.9.0-beta2, 8.9.0-beta3, 8.9.0-rc1, 8.9.1, 8.9.10, 8.9.11, 8.9.2, 8.9.3, 8.9.4, 8.9.5, 8.9.6, 8.9.7, 8.9.8, 8.9.9].
    - Can only install one of: drupal/core[8.7.0, 9.0.9].
    - Can only install one of: drupal/core[8.7.0-alpha1, 9.0.9].
...
    - Installation request for drupal/core (locked at 9.0.9) -> satisfiable by drupal/core[9.0.9].


Installation failed, reverting ./composer.json to its original content.

The error in situations like this goes on and on, including scary messages like 'Conclusion: remove drupal/core', and sometimes about all sorts of seemingly unrelated packages! Ultimately, composer is trying to bend over backwards to use versions of Drupal and its dependencies that would work with the module, but it won't find any. Or if it does, you might find yourself downgraded to an old version of Drupal, which you certainly don't want!

So what's the solution?

We need to override the compatibility metadata for the contrib module.

Drupal sites use https://packages.drupal.org/8 as the metadata provider, which you'll find listed in your project's root composer.json file under a repositories section. This section instructs composer where to get the metadata from ... so if you add your own 'package' repository to be used first, composer will happily use that, ignoring what drupal.org tells it:

"repositories": [
    {
      "type": "package",
      "package": {
        "name": "drupal/commerce_migrate",
        "type": "drupal-module",
        "version": "dev-8.x-2.x",
        "source": {
          "type": "git",
          "url": "https://git.drupalcode.org/project/commerce_migrate.git",
          "reference": "9ac26262b3443d20e69cea69652abc2dee39fee5"
        }
      }
    },
    {
      "type": "composer",
      "url": "https://packages.drupal.org/8"
    }
  ],

In this example, for the commerce_migrate module, we specify the latest commit (at the time of writing) from its development branch (8.x-2.x), as that's what the patch is intended to get ultimately applied to. That branch also matches our requirement in our composer.json file: "drupal/commerce_migrate": "dev-8.x-2.x". (Note that composer needs the dev- prefix when using git branches.)

So this section redefines the module's dev-8.x-2.x version for composer. As we require that version, we get the snapshot of the codebase at that specific commit - skipping the metadata that drupal.org would have added which would restrict its compatibility.

Now, the patch can be added in the usual patches section, and then running composer update drupal/commerce_migrate will apply it successfully!

This technique can also be used to override other package metadata for composer, such as a change in dependencies. But beware - it means you're no longer using the real releases for the package, and in this example, locks to a specific commit. So you will need to keep an eye out for when the patch gets committed to the module, and for any other work in its codebase that should be incorporated over time. It's a little like taking a snapshot of the project, and applying just the specific changes you need - at the cost of losing out on any goodness that the maintainers may add over time. This approach does avoid forking the codebase away at least. As this replaces the module’s metadata entirely, it’s worth checking for any dependencies or other compatibility metadata that its own (potentially now patched) composer.json file might have, and copying them to your project’s root composer.json file.

Given how quickly the community & its leaders have rallied to make modules D9-ready, I wouldn't be surprised if this trick can be removed in a few months' time, as maintainers continue to commit these (often trivial) patches. Any projects that don't, may even get covered automatically some day - plus, if their maintainers aren't doing this, they're also unlikely to be adding much of note in the meantime anyway, so it's pretty safe. I would just keep an eye out for any new releases - especially security ones - to those projects.

Get notified

I suggest finding the issue that currently tracks making the module compatible with Drupal 9. This may be linked to from the project page, or it's usually one of the more recent/active ones in its issue queue. If you're logged into drupal.org (and you should be - sign up if you haven't!), then you can press a 'Follow' link that has a nice green star next to it, to get email notifications of updates. The 'View all releases' link from a project page takes you to a page that has an RSS feed, which can be used to subscribe to notifications of new releases too. That's well worth doing for a number of reasons! If you didn't know it, you can also subscribe to notifications of security releases - head to https://www.drupal.org/security where you'll find instructions at the top of the sidebar (which is below the list of posts if you're on a mobile).

 

Thanks to heddn, dpi and larowlan for this solution. Photo by Josh Carter on Unsplash.

Hi, thanks for reading

ComputerMinds are the UK’s Drupal specialists with offices in Bristol and Coventry. We offer a range of Drupal services including Consultancy, Development, Training and Support. Whatever your Drupal problem, we can help.