Most developers in the CiviCRM community are probably familiar with Composer. If not, Composer is a dependency manager for PHP. That's just a fancy way of saying that composer allows codebases like CiviCRM to pull in code (dependencies) from other projects. For example, CiviCRM requires the league/csv
package via composer, as it doesn't make sense for CiviCRM to maintain its own CSV parsing code when another good quality package already exists.
Traditionally, CiviCRM has been installed by downloading the code, including its dependencies, from https://civicrm.org/download, meaning CiviCRM administrators have not needed to use Composer directly. However, in recent months it's become more common to install CiviCRM through Composer itself, with this being the recommended install method for Drupal 10.
At BrightMinded, we've recently been trying to go a step further by installing CiviCRM extensions via Composer. Unfortunately, we've found many extensions are not set up to support this workflow, which is what this blog aims to address.
Why would we want to install extensions via Composer?
There are various reasons why we think managing CiviCRM extensions via Composer is valuable:
- Security best practice dictates that web-server file systems should be read-only unless strictly necessary. As such many organisations disable the extension browser on staging and production environments,
- Having CiviCRM core, extensions and plugins for the host CMS all managed through Composer gives us a holistic overview of the installed dependencies from a single view, and means we only need a single tool when we want to apply updates (rather than needing to switch between Composer and the CiviCRM
cv
tool) - Using Composer allows for tools like Renovate or Dependabot to notify us when new extension releases are available, by automatically opening pull requests with the update applied.
Composer installers: the problem
When you install a package through composer, its code usually goes into the vendor
folder, where it would be picked up through a code autoloader. However, this doesn't work for CiviCRM extensions, which need to live within CiviCRM's configured extensions folder.
Composer has a solution for this through their composer/installers
package. This package allows the developer or sysadmin to control where packages of specific types will go:
{
"extra": {
"installer-paths": {
"web/sites/default/files/civicrm/ext/{$name}/": ["civicrm-ext"]
}
}
}
This configuration tells all CiviCRM extensions installed via composer to be installed to web/sites/default/files/civicrm/ext/{$name}/
. This path would be different depending on your CMS, and the directory in which you've installed CiviCRM.
However, this configuration only works if the extension contains "type": "civicrm-ext"
in its own composer.json
file. This "civicrm-ext" can't be changed, as it has to match the exact wording from the composer/installers
project. If the extension does not have this civicrm-ext
type, the extension will be installed into the vendor
folder where it won't be recognised up by CiviCRM.
Some, but not all, of the top CiviCRM extensions already support being installed via Git in this way. This list shows the current top extensions from civicrm.org/extensions with a tick ✔ for extensions that correctly specify "type": "civicrm-ext"
:
- Mosaico
- ✔ Extended Reports
- ✔ CiviRules
- Payment Shared
- API csv Import GUI
- ✔ E-mail API
- Contact Layout Editor
- iATS Payments by Deluxe
- ✔ Firewall
- Fuzion Tokens
- ✔ Stripe Payment Processor
- ✔ CiviCRM Log Viewer
- ✔ Sweetalert
- Angular Profile Utilities
Extension authors: Here's how to add composer installation support
Before I continue I just want to acknowledge that most CiviCRM extensions are developed by the community, often with limited or no funding; Whilst I'd love to see better support for installing extensions via composer, it's not my aim to criticise any extension developer who doesn't prioritise allowing composer installation of their extension.
To support installing an extension via Composer, there are two things to check:
First, is there a file named composer.json
in the root of the project? If not, a composer.json
should be added with the following structure:
{
"name": "civicrm/civirules",
"type": "civicrm-ext"
}
For extensions hosted on https://lab.civicrm.org/extensions/ the name
should use "civicrm/
" followed by the name of the extension.
If your extension already has a composer.json
file, you just need to ensure it contains "type": "civicrm-ext"
.
What next?
Arguably the above step could and should be automated. There is already a proposal to automatically generate composer.json
files as part of Civix: https://github.com/totten/civix/issues/234.
I've seen various issues on various tickets around adding "type": "civicrm-ext"
, but it's difficult to know what level of interest there is in installing extensions via Composer across the community. It'd be great to hear in the comments if anyone has had success with this approach, or if it's something that others are exploring or would find useful.
Comments
Thanks for starting this conversation! We run Civi without write permissions to the source for security reasons, so we also really would like to see composer install the extensions.
To get things working, I wrote [a pending pull request](https://github.com/civicrm/composer-downloads-plugin/pull/5) against [composer-download-plugins](https://github.com/civicrm/composer-downloads-plugin). It's not the same thing as having composer install the extensions, but at least it allows us to build a complete platform with composer. It hasn't been merged, but we have been using it for a few years without problems.
I have not been great about adding composer.json files to my extensions because it seemed like such an uphill struggle, but I will change my ways :).
jamie