March 31, 2023

Patching Drupal contrib modules for Drupal 10 compatibility with Composer

I've started to adopt Drupal 10 on most of my personal projects (this blog included) and along the way I've hit issues where a contrib modules doesn't have an official Drupal 10 compatible release but there is a patch for Drupal 10 compatibility in the modules issue queue. I only use Composer to maintain my Drupal projects so how do you patch a module when Composer won't download it (because it's not compatible) is the first place. Here's a quick tutorial to show how I've been getting around this!

Before we begin a little disclaimer, unless you are a developer it's best to only use official support releases that state they're compatible with your version of Drupal. That said if you're a developer & you understand the potential risk you can forget this!


"The problem" with Composer (in this context)

Using Composer to maintain your Drupal codebase is really awesome, it strictly ensures you only get code in your codebase that will (in theory) function on your codebase, exactly what we want!

The "problem" occurs when you want to go outside of this and force downloading of code that isn't necessarily compatible with your codebase such as patching a contrib module to bring it up to Drupal 10 compatibility.

Running standard composer require PROJECT commands won't work if the module you want to patch doesn't have Works with Drupal: * || ^10 on a release on it's project page, this is Composer doing it's job like you normally want it to do:

An example of "The problem"

On this given personal project I use the feeds module (which I love!!) to gather JSON data into football fixtures & stats, I'm in the process of upgrading to Drupal 10 and I needed to install the Feeds HTTP Auth Fetcher module in order to make authenticated API calls to get JSON data.

The module had a stable release so I gladly went into Terminal and ran ddev composer require 'drupal/feeds_http_auth_fetcher:^1.0' only to find:

Could not find a matching version of package drupal/feeds_http_auth_fetcher. Check the package spelling, your version constraint and that the package is available in a  
   stability which matches your minimum-stability (stable).  

Back to the module page and I saw my mistake, there's no Drupal 10 compatible release!!! Just Drupal 8 & 9........

Drupal 10 compatibility on a Drupal module project
Drupal 10 compatibility on a Drupal module project

So over to the modules issue queue I went and searched for the standard automated Drupal 10 compatibility issue which is normally title "Automated Drupal 10 compatibility fixes", bingo! The automated bots had done their job and the module could be easily brought up to Drupal 10 compatibility with one simple patch (a single line), time to get around "the problem".

You can use this same approach on all contrib modules & themes just be aware not all modules will have automated patches available and not all the patches will be as simple as the one in this example!


Make Composer download a module from source

As the heading above suggests the way we get around "The problem" is by downloading the modules source code directly from it's source (ie. git) rather than from it's official package, this means we bypass Composers version checking and checkout the source code regardless of whether Composer thinks it's compatible or not.

To do this it's relatively simple. Open up your composer.json file and find the line "repositories": (this is where Composer looks for information about the code you download) and then manually add information about the Feeds HTTP Auth Fetcher modules source code so we can download it.

        {
            "type": "package",
            "package": {
              "name": "drupal-gitlab/feeds_http_auth_fetcher",
              "version": "1.0.3",
              "type": "drupal-module",
              "source": {
                "url": "https://git.drupalcode.org/project/feeds_http_auth_fetcher.git",
                "type": "git",
                "reference": "1.0.x"
              }
            }
        }

The most important bits are:

  • "name": "drupal-gitlab/feeds_http_auth_fetcher" - you can define whatever you want but you'll need to remember this so I use the pattern drupal-gitlab/MODULENAME (GitLab being where Drupal.org code is stored)
  • "type": "drupal-module" - tells Composer what type of code this is and if you use "installer-paths" that'll determine where the code is downloaded to, if it was a theme for example you'd use drupal-theme
  • "source" - this is where the source code actually is, for Drupal modules & themes they live on GibLab as "git" repositories. The URL pattern is always https://git.drupalcode.org/project/MODULENAME.git.
    "reference" should be the branch or commit hash you want, for this module there was only a 1.0.x branch.
  • "version" - my understanding is this allows you to define a given version of the code when you use composer require in my example it seemed to what with whatever I wrote here.

To test this we can now run the following which should now download the module from it's git source:

ddev composer require drupal-gitlab/feeds_http_auth_fetcher

And look for the following in your Composer output:

- Installing drupal-gitlab/feeds_http_auth_fetcher (1.0.3): Cloning 8.x-1.x
feeds_http_auth_fetcher downloaded locally
feeds_http_auth_fetcher downloaded locally

Now to patch the module

First thing we need if you haven't already got it is a Composer plugin called "composer-patches", follow the instructions there to install it on your Composer Drupal project.

Over to the Automated Drupal 10 compatibility fixes issue and grab the remote URL of the patch we want: https://www.drupal.org/files/issues/2022-07-18/feeds_http_auth_fetcher.1.0.x-dev.rector.patch

Now we add the patch information to the "extras" -> "patches", this is a simple format:

"extras": {
  "patches": {
    "PACKAGENAME-1": {
      "DESCRIPTION-OF-PATCH": "PATH-TO-PATCH-FILE-(LOCAL-OR-REMOTE)"
    },
    "PACKAGENAME-2": {
      "DESCRIPTION-OF-PATCH": "PATH-TO-PATCH-FILE-(LOCAL-OR-REMOTE)"
    }
  }
}

And for our example this became:

"extras": {
  "patches": {
    "drupal-gitlab/feeds_http_auth_fetcher": {
      "Automated Drupal 10 compatibility fixes": "https://www.drupal.org/files/issues/2022-07-18/feeds_http_auth_fetcher.1.0.x-dev.rector.patch"
    }
  }
}

Over in Terminal run ddev composer install and if everything works you'll see lines similar to this in your Composer output:

  - Installing drupal-gitlab/feeds_http_auth_fetcher (1.0.3): Cloning 1.0.x from cache
  - Applying patches for drupal-gitlab/feeds_http_auth_fetcher
    https://www.drupal.org/files/issues/2022-07-18/feeds_http_auth_fetcher.1.0.x-dev.rector.patch (Automated Drupal 10 compatibility fixes)

And that's it, your module should now show as Drupal 10 compatible and can be installed:

Module is now available to install
Module is now available to install