Merge "registration: Allow extensions to specify which MW core versions they require"
authorjenkins-bot <jenkins-bot@gerrit.wikimedia.org>
Tue, 22 Sep 2015 04:57:47 +0000 (04:57 +0000)
committerGerrit Code Review <gerrit@wikimedia.org>
Tue, 22 Sep 2015 04:57:47 +0000 (04:57 +0000)
autoload.php
composer.json
docs/extension.schema.json
includes/GlobalFunctions.php
includes/registration/CoreVersionChecker.php [new file with mode: 0644]
includes/registration/ExtensionProcessor.php
includes/registration/ExtensionRegistry.php
includes/registration/Processor.php
tests/phpunit/includes/registration/CoreVersionCheckerTest.php [new file with mode: 0644]

index 03728e9..bdcbe5a 100644 (file)
@@ -274,6 +274,7 @@ $wgAutoloadLocalClasses = array(
        'CopyJobQueue' => __DIR__ . '/maintenance/copyJobQueue.php',
        'CoreParserFunctions' => __DIR__ . '/includes/parser/CoreParserFunctions.php',
        'CoreTagHooks' => __DIR__ . '/includes/parser/CoreTagHooks.php',
+       'CoreVersionChecker' => __DIR__ . '/includes/registration/CoreVersionChecker.php',
        'CreateAndPromote' => __DIR__ . '/maintenance/createAndPromote.php',
        'CreateFileOp' => __DIR__ . '/includes/filebackend/FileOp.php',
        'CreditsAction' => __DIR__ . '/includes/actions/CreditsAction.php',
index e698862..cce664a 100644 (file)
@@ -16,6 +16,7 @@
                "wiki": "https://www.mediawiki.org/"
        },
        "require": {
+               "composer/semver": "0.1.0",
                "cssjanus/cssjanus": "1.1.1",
                "ext-iconv": "*",
                "liuggio/statsd-php-client": "1.0.16",
index d11635d..218a19c 100644 (file)
                                "Unlicense"
                        ]
                },
+               "requires": {
+                       "type": "object",
+                       "description": "Indicates what versions of MediaWiki core are required. This syntax may be extended in the future, for example to check dependencies between other extensions.",
+                       "properties": {
+                               "MediaWiki": {
+                                       "type": "string",
+                                       "description": "Version constraint string against MediaWiki core."
+                               }
+                       }
+               },
                "ResourceFileModulePaths": {
                        "type": "object",
                        "description": "Default paths to use for all ResourceLoader file modules",
index f2e37d5..58927dc 100644 (file)
@@ -3216,6 +3216,7 @@ function wfUsePHP( $req_ver ) {
  *
  * @see perldoc -f use
  *
+ * @deprecated since 1.26, use the "requires' property of extension.json
  * @param string|int|float $req_ver The version to check, can be a string, an integer, or a float
  * @throws MWException
  */
diff --git a/includes/registration/CoreVersionChecker.php b/includes/registration/CoreVersionChecker.php
new file mode 100644 (file)
index 0000000..a7a926c
--- /dev/null
@@ -0,0 +1,68 @@
+<?php
+
+/**
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+use Composer\Semver\VersionParser;
+use Composer\Semver\Constraint\VersionConstraint;
+
+/**
+ * @since 1.26
+ */
+class CoreVersionChecker {
+
+       /**
+        * @var VersionConstraint|bool representing $wgVersion
+        */
+       private $coreVersion = false;
+
+       /**
+        * @var VersionParser
+        */
+       private $versionParser;
+
+       /**
+        * @param string $coreVersion Current version of core
+        */
+       public function __construct( $coreVersion ) {
+               $this->versionParser = new VersionParser();
+               try {
+                       $this->coreVersion = new VersionConstraint(
+                               '==',
+                               $this->versionParser->normalize( $coreVersion )
+                       );
+               } catch ( UnexpectedValueException $e ) {
+                       // Non-parsable version, don't fatal.
+               }
+       }
+
+       /**
+        * Check that the provided constraint is compatible with the current version of core
+        *
+        * @param string $constraint Something like ">= 1.26"
+        * @return bool
+        */
+       public function check( $constraint ) {
+               if ( $this->coreVersion === false ) {
+                       // Couldn't parse the core version, so we can't check anything
+                       return true;
+               }
+
+               return $this->versionParser->parseConstraints( $constraint )
+                       ->matches( $this->coreVersion );
+       }
+}
index ca84a51..a286f6b 100644 (file)
@@ -192,6 +192,16 @@ class ExtensionProcessor implements Processor {
                );
        }
 
+       public function getRequirements( array $info ) {
+               $requirements = array();
+               $key = ExtensionRegistry::MEDIAWIKI_CORE;
+               if ( isset( $info['requires'][$key] ) ) {
+                       $requirements[$key] = $info['requires'][$key];
+               }
+
+               return $requirements;
+       }
+
        protected function extractHooks( array $info ) {
                if ( isset( $info['Hooks'] ) ) {
                        foreach ( $info['Hooks'] as $name => $value ) {
index 787a4b0..23e29d8 100644 (file)
  */
 class ExtensionRegistry {
 
+       /**
+        * "requires" key that applies to MediaWiki core/$wgVersion
+        */
+       const MEDIAWIKI_CORE = 'MediaWiki';
+
        /**
         * Version of the highest supported manifest version
         */
@@ -156,8 +161,11 @@ class ExtensionRegistry {
         * @throws Exception
         */
        public function readFromQueue( array $queue ) {
+               global $wgVersion;
                $autoloadClasses = array();
                $processor = new ExtensionProcessor();
+               $incompatible = array();
+               $coreVersionParser = new CoreVersionChecker( $wgVersion );
                foreach ( $queue as $path => $mtime ) {
                        $json = file_get_contents( $path );
                        if ( $json === false ) {
@@ -179,8 +187,27 @@ class ExtensionRegistry {
                        // Set up the autoloader now so custom processors will work
                        $GLOBALS['wgAutoloadClasses'] += $autoload;
                        $autoloadClasses += $autoload;
+                       // Check any constraints against MediaWiki core
+                       $requires = $processor->getRequirements( $info );
+                       if ( isset( $requires[self::MEDIAWIKI_CORE] )
+                               && !$coreVersionParser->check( $requires[self::MEDIAWIKI_CORE] )
+                       ) {
+                               // Doesn't match, mark it as incompatible.
+                               $incompatible[] = "{$info['name']} is not compatible with the current "
+                                       . "MediaWiki core (version {$wgVersion}), it requires: ". $requires[self::MEDIAWIKI_CORE]
+                                       . '.';
+                               continue;
+                       }
+                       // Compatible, read and extract info
                        $processor->extractInfo( $path, $info, $version );
                }
+               if ( $incompatible ) {
+                       if ( count( $incompatible ) === 1 ) {
+                               throw new Exception( $incompatible[0] );
+                       } else {
+                               throw new Exception( implode( "\n", $incompatible ) );
+                       }
+               }
                $data = $processor->getExtractedInfo();
                // Need to set this so we can += to it later
                $data['globals']['wgAutoloadClasses'] = array();
index e1aaca7..e5669d2 100644 (file)
@@ -30,4 +30,14 @@ interface Processor {
         *              'attributes' - registration info which isn't a global variable
         */
        public function getExtractedInfo();
+
+       /**
+        * Get the requirements for the provided info
+        *
+        * @since 1.26
+        * @param array $info
+        * @return array Where keys are the name to have a constraint on,
+        *              like 'MediaWiki'. Values are a constraint string like "1.26.1".
+        */
+       public function getRequirements( array $info );
 }
diff --git a/tests/phpunit/includes/registration/CoreVersionCheckerTest.php b/tests/phpunit/includes/registration/CoreVersionCheckerTest.php
new file mode 100644 (file)
index 0000000..bc154b3
--- /dev/null
@@ -0,0 +1,38 @@
+<?php
+
+/**
+ * @covers CoreVersionChecker
+ */
+class CoreVersionCheckerTest extends PHPUnit_Framework_TestCase {
+       /**
+        * @dataProvider provideCheck
+        */
+       public function testCheck( $coreVersion, $constraint, $expected ) {
+               $checker = new CoreVersionChecker( $coreVersion );
+               $this->assertEquals( $expected, $checker->check( $constraint ) );
+       }
+
+       public static function provideCheck() {
+               return array(
+                       // array( $wgVersion, constraint, expected )
+                       array( '1.25alpha', '>= 1.26', false ),
+                       array( '1.25.0', '>= 1.26', false ),
+                       array( '1.26alpha', '>= 1.26', true ),
+                       array( '1.26alpha', '>= 1.26.0', true ),
+                       array( '1.26alpha', '>= 1.26.0-stable', false ),
+                       array( '1.26.0', '>= 1.26.0-stable', true ),
+                       array( '1.26.1', '>= 1.26.0-stable', true ),
+                       array( '1.27.1', '>= 1.26.0-stable', true ),
+                       array( '1.26alpha', '>= 1.26.1', false ),
+                       array( '1.26alpha', '>= 1.26alpha', true ),
+                       array( '1.26alpha', '>= 1.25', true ),
+                       array( '1.26.0-alpha.14', '>= 1.26.0-alpha.15', false ),
+                       array( '1.26.0-alpha.14', '>= 1.26.0-alpha.10', true ),
+                       array( '1.26.1', '>= 1.26.2, <=1.26.0', false ),
+                       array( '1.26.1', '^1.26.2', false ),
+                       // Accept anything for un-parsable version strings
+                       array( '1.26mwf14', '== 1.25alpha', true ),
+                       array( 'totallyinvalid', '== 1.0', true ),
+               );
+       }
+}