Requests for comment/Improving extension management

This RfC proposes a system for easier extension management through web interfaces, tarball generators and distributors, and dependency management.

Pain points for developers

 * Handling dependencies
 * Depending upon another extension is typically manual and hacky with to avoid load order dependencies.
 * Versioning
 * Versioning is mainly an internal thing, not really exposed anywhere, and not really used by sysadmins. Or is it? As a dev, I typically ignore them unless someone asks me to bump it.
 * Compatibility with core
 * We have REL_ branches, but people usually don't backport every change they make that is compatible
 * Some extension maintainers keep compatibility with older versions of core, but that isn't documented anywhere in a machine readable format for tools like ExtensionDistributor.
 * wfUseMW is hacky as well

Pain points for sysadmins

 * Just because an extension has a REL1_xx branch doesn't mean it actually is compatible with said version
 * Upgrading tons of extensions at once (during a major version bump) is not super friendly and requires a lot of manual steps

Considerations

 * Security: This system should function in an environment where webservers cannot write executable PHP files
 * Ease of use: Shouldn't require a CLI interface, nearly everything should be doable from the web
 * But...for people who like CLI/git, we should still support that workflow, probably with a maintenance script.

Dependencies upon MediaWiki core
Extensions can currently specify that they depend upon a specific version of core by using, however that is extremely hacky, and only allows for specifying a lower bound of support. We automatically create REL1_XX branches whenever core is branched, except that there is no actual verification that the extension works with that core version unless the maintainer or a user does so.

Instead of that, I propose that we store the versions that the extension supports in its extension.json file:

Supports is an array of versions that the maintainer has explicitly stated work and are supported. Experimental is used for those are that are in active development and not yet recommended for use. Broken means that it is not compatible with those versions. When an extension drops support for a certain release, it would create the REL1_XX branch at the last commit that did support that release.

The extension distributor would be updated to read from extension.json to see which versions to display.

Dependencies upon libraries
We have standardized upon using composer as the dependency manager for library dependencies. Extensions would list their depdencies upon external libraries through composer.json files.

For users using the web interface and without CLI access, we would have a web proxy to composer that would take an input of the required dependencies and output a tarball of the vendor directory they need. I've written a quick prototype to demonstrate how this might work.

CLI users would be able to use the composer-merge-plugin to manage library dependencies.

Dependencies upon extensions
There are very few actual dependencies upon other extensions. Many times it's simply dependencies upon common code, that really should be refactored out into a separate library. A good example of this is AbuseFilter depending upon AntiSpoof. AbuseFilter really only needs AntiSpoof's normalization functionality, not the entire extension. That common code should be refactored out into a separate library which both AbuseFilter and AntiSpoof depend upon.

However, there are some cases where extensions do actually depend upon each other. These constraints can be added in the extension.json file:

Extensions that use versioning are encouraged to adopt semantic versioning, and extensions that depend upon those can use version identifiers like "1.*". For extensions that don't use versioning, "*" will get the latest version of the extension that supports your MediaWiki core version. Advanced constructs like greater than, less than, or specifying multiple constraints with a comma would not be supported.

This will be processed by the registry at compile time, and exceptions will be thrown if dependencies are missing or incompatible.

Implementation
A special page would exist to list installed extensions (both disabled and enabled), and would allow enabling and disabling of those extensions. It would also show whether those extensions have updates available.

Sysadmins would be able to update an extension, which would download an updated tarball from extension distributor, unzip it, and install it. The composer.json would be read if it exists and a new vendor directory would be fetched from the composer proxy if necessary.

For sysadmins who are more familiar with CLI and would prefer to use that, the same functionality would be available via a maintenance script.

Why not use composer?
Regarding Requests for comment/Extension management with Composer:

As mentioned earlier, composer is what we use to manage dependencies upon libraries. It's important to note that extensions are not libraries. Libraries typically just require an autoloader, whereas extensions need an autoloader, but also need to register hooks, special pages, API modules, and much more.


 * composer is CLI-only. For shared hosting users or users who don't like CLI, we need some kind of web interface.
 * Note that https://github.com/CurosMJ/NoConsoleComposer exists, also see stackoverflow
 * composer combines the installation process (downloading tarball and unzipping it to the right place) and the enable process (require_once/wfLoadExtension) into one step. This is especially problematic for wiki-farm setups where extensions may be enabled on one wiki but not on others (see A way to disable extensions)
 * We could just have composer download and install the extension, and *not* autoload the entry point.
 * composer is still an alpha product, and not packaged by any major distros. This means users need to download a phar file and trust it.
 * If we add composer/composer as a dependency to mediawiki/core (that would be shipped with the tarball), we wouldn't have to require the binary and could directly interact with the composer API.