I’ve written at length about how we deploy WordPress with Composer and Capistrano. This week I’m going to write about how I adapted that workflow for our MediaWiki environments.

We have a couple MediaWiki environments which we manage on behalf of the campus community. The first thing I discovered was that our preferred approach–installing core with a git submodule and extensions with Composer–wasn’t viable. Beginning with MediaWiki 1.26.2 the core product ships with its own Composer dependencies, and extensions may choose to define dependencies of their own. The MediaWiki team supports this model with the composer-merge-plugin, which can combine the core and extension composer.json files on installation. When I attempted to overlay our extensions-as-composer-dependencies model on that environment it broke, and hard.

Instead, I fell back on the older model of nested git submodules. The core code is managed in an integration repository, and each extension is added to that repository as a submodule. The Capistrano git submodule gem handles this situation cleanly. For the extension composer dependencies, I defined a composer.local.json file for each project and added it to the shared files on the web server. In this example, I’m adding support for the SyntaxHighlight extension:

    "extra": {
        "merge-plugin": {
            "include": [

On deployment, the composer gem executes this deployment as expected, with both the code and the extension dependencies installed into /vendor/.

MediaWiki needs a little bit of help with the symlinked structure so I tweaked each LocalSettings.php file as well:

define('MW_INSTALL_PATH', '/path/to/root/of/installation');

The downside to the submodule approach is that we either have the same codebase across all our environments, or we’re maintaining separate branches in the core repository for each project.  Previously, we went with the former approach but had a shell script which initialized different submodules for each installation, based on path. Given that we have a relatively low number of extensions, and none of them are intrusive, we kept the former option. We can control which extensions are enabled in LocalSettings.php anyway.