From: jenkins-bot Date: Sat, 13 Apr 2019 19:54:04 +0000 (+0000) Subject: Merge "registration: Allow to require environment abilities" X-Git-Tag: 1.34.0-rc.0~2011 X-Git-Url: https://git.heureux-cyclage.org/?p=lhc%2Fweb%2Fwiklou.git;a=commitdiff_plain;h=c81654bcdd821e7b59279be99119942b2a9c4559;hp=297d27debabdedc807c3c7b0aba8084945df45eb Merge "registration: Allow to require environment abilities" --- diff --git a/docs/extension.schema.v1.json b/docs/extension.schema.v1.json index 8cd4e711c0..36e2fe21ca 100644 --- a/docs/extension.schema.v1.json +++ b/docs/extension.schema.v1.json @@ -70,6 +70,11 @@ "php": { "type": "string", "description": "Version constraint string against PHP." + }, + "ability-shell": { + "type": "boolean", + "default": false, + "description": "Whether this extension requires shell access." } }, "patternProperties": { diff --git a/docs/extension.schema.v2.json b/docs/extension.schema.v2.json index 1d64095a46..ed903f889d 100644 --- a/docs/extension.schema.v2.json +++ b/docs/extension.schema.v2.json @@ -77,6 +77,11 @@ "php": { "type": "string", "description": "Version constraint string against PHP." + }, + "ability-shell": { + "type": "boolean", + "default": false, + "description": "Whether this extension requires shell access." } }, "patternProperties": { diff --git a/includes/registration/ExtensionDependencyError.php b/includes/registration/ExtensionDependencyError.php index c27cd2c18c..5329572a18 100644 --- a/includes/registration/ExtensionDependencyError.php +++ b/includes/registration/ExtensionDependencyError.php @@ -58,6 +58,11 @@ class ExtensionDependencyError extends Exception { */ public $missingPhpExtensions = []; + /** + * @var string[] + */ + public $missingAbilities = []; + /** * @param array $errors Each error has a 'msg' and 'type' key at minimum */ @@ -75,6 +80,9 @@ class ExtensionDependencyError extends Exception { case 'missing-phpExtension': $this->missingPhpExtensions[] = $info['missing']; break; + case 'missing-ability': + $this->missingAbilities[] = $info['missing']; + break; case 'missing-skins': $this->missingSkins[] = $info['missing']; break; diff --git a/includes/registration/ExtensionRegistry.php b/includes/registration/ExtensionRegistry.php index e3df499987..2607e5ab29 100644 --- a/includes/registration/ExtensionRegistry.php +++ b/includes/registration/ExtensionRegistry.php @@ -2,6 +2,8 @@ use Composer\Semver\Semver; use Wikimedia\ScopedCallback; +use MediaWiki\Shell\Shell; +use MediaWiki\ShellDisabledError; /** * ExtensionRegistry class @@ -144,7 +146,8 @@ class ExtensionRegistry { // A few more things to vary the cache on $versions = [ 'registration' => self::CACHE_VERSION, - 'mediawiki' => $wgVersion + 'mediawiki' => $wgVersion, + 'abilities' => $this->getAbilities(), ]; // We use a try/catch because we don't want to fail here @@ -207,6 +210,38 @@ class ExtensionRegistry { $this->finished = true; } + /** + * Get the list of abilities and their values + * @return bool[] + */ + private function getAbilities() { + return [ + 'shell' => !Shell::isDisabled(), + ]; + } + + /** + * Queries information about the software environment and constructs an appropiate version checker + * + * @return VersionChecker + */ + private function buildVersionChecker() { + global $wgVersion; + // array to optionally specify more verbose error messages for + // missing abilities + $abilityErrors = [ + 'shell' => ( new ShellDisabledError() )->getMessage(), + ]; + + return new VersionChecker( + $wgVersion, + PHP_MAJOR_VERSION . '.' . PHP_MINOR_VERSION . '.' . PHP_RELEASE_VERSION, + get_loaded_extensions(), + $this->getAbilities(), + $abilityErrors + ); + } + /** * Process a queue of extensions and return their extracted data * @@ -216,16 +251,11 @@ class ExtensionRegistry { * @throws ExtensionDependencyError */ public function readFromQueue( array $queue ) { - global $wgVersion; $autoloadClasses = []; $autoloadNamespaces = []; $autoloaderPaths = []; $processor = new ExtensionProcessor(); - $versionChecker = new VersionChecker( - $wgVersion, - PHP_MAJOR_VERSION . '.' . PHP_MINOR_VERSION . '.' . PHP_RELEASE_VERSION, - get_loaded_extensions() - ); + $versionChecker = $this->buildVersionChecker(); $extDependencies = []; $incompatible = []; $warnings = false; diff --git a/includes/registration/VersionChecker.php b/includes/registration/VersionChecker.php index 586729d057..a5d1fa1fcf 100644 --- a/includes/registration/VersionChecker.php +++ b/includes/registration/VersionChecker.php @@ -45,6 +45,16 @@ class VersionChecker { */ private $phpExtensions = []; + /** + * @var bool[] List of provided abilities + */ + private $abilities = []; + + /** + * @var string[] List of provided ability errors + */ + private $abilityErrors = []; + /** * @var array Loaded extensions */ @@ -59,12 +69,19 @@ class VersionChecker { * @param string $coreVersion Current version of core * @param string $phpVersion Current PHP version * @param string[] $phpExtensions List of installed PHP extensions + * @param bool[] $abilities List of provided abilities + * @param string[] $abilityErrors Error messages for the abilities */ - public function __construct( $coreVersion, $phpVersion, array $phpExtensions ) { + public function __construct( + $coreVersion, $phpVersion, array $phpExtensions, + array $abilities = [], array $abilityErrors = [] + ) { $this->versionParser = new VersionParser(); $this->setCoreVersion( $coreVersion ); $this->setPhpVersion( $phpVersion ); $this->phpExtensions = $phpExtensions; + $this->abilities = $abilities; + $this->abilityErrors = $abilityErrors; } /** @@ -121,7 +138,8 @@ class VersionChecker { * 'MediaWiki' => '>= 1.25.0', * 'platform': { * 'php': '>= 7.0.0', - * 'ext-foo': '*' + * 'ext-foo': '*', + * 'ability-bar': true * }, * 'extensions' => { * 'FooBaz' => '>= 1.25.0' @@ -193,6 +211,37 @@ class VersionChecker { 'missing' => $phpExtension, ]; } + } elseif ( substr( $dependency, 0, 8 ) === 'ability-' ) { + // Other abilities the environment might provide. + $ability = substr( $dependency, 8 ); + if ( !isset( $this->abilities[$ability] ) ) { + throw new UnexpectedValueException( 'Dependency type ' + . $dependency . ' unknown in ' . $extension ); + } + if ( !is_bool( $constraint ) ) { + throw new UnexpectedValueException( 'Only booleans are ' + . 'allowed to to indicate the presence of abilities ' + . 'in ' . $extension ); + } + + if ( $constraint === true && + $this->abilities[$ability] !== true + ) { + // add custom error message for missing ability if specified + $customMessage = ''; + if ( isset( $this->abilityErrors[$ability] ) ) { + $customMessage = ': ' . $this->abilityErrors[$ability]; + } + + $errors[] = [ + 'msg' => + "{$extension} requires \"{$ability}\" ability" + . $customMessage + , + 'type' => 'missing-ability', + 'missing' => $ability, + ]; + } } else { // add other platform dependencies here throw new UnexpectedValueException( 'Dependency type ' . $dependency . diff --git a/tests/phpunit/includes/registration/VersionCheckerTest.php b/tests/phpunit/includes/registration/VersionCheckerTest.php index 6b92444442..e824e3f02c 100644 --- a/tests/phpunit/includes/registration/VersionCheckerTest.php +++ b/tests/phpunit/includes/registration/VersionCheckerTest.php @@ -101,7 +101,21 @@ class VersionCheckerTest extends PHPUnit\Framework\TestCase { * @dataProvider provideType */ public function testType( $given, $expected ) { - $checker = new VersionChecker( '1.0.0', '7.0.0', [ 'phpLoadedExtension' ] ); + $checker = new VersionChecker( + '1.0.0', + '7.0.0', + [ 'phpLoadedExtension' ], + [ + 'presentAbility' => true, + 'presentAbilityWithMessage' => true, + 'missingAbility' => false, + 'missingAbilityWithMessage' => false, + ], + [ + 'presentAbilityWithMessage' => 'Present.', + 'missingAbilityWithMessage' => 'Missing.', + ] + ); $checker->setLoadedExtensionsAndSkins( [ 'FakeDependency' => [ 'version' => '1.0.0', @@ -218,6 +232,83 @@ class VersionCheckerTest extends PHPUnit\Framework\TestCase { ], ], ], + [ + [ + 'platform' => [ + 'ability-presentAbility' => true, + ], + ], + [], + ], + [ + [ + 'platform' => [ + 'ability-presentAbilityWithMessage' => true, + ], + ], + [], + ], + [ + [ + 'platform' => [ + 'ability-presentAbility' => false, + ], + ], + [], + ], + [ + [ + 'platform' => [ + 'ability-presentAbilityWithMessage' => false, + ], + ], + [], + ], + [ + [ + 'platform' => [ + 'ability-missingAbility' => true, + ], + ], + [ + [ + 'missing' => 'missingAbility', + 'type' => 'missing-ability', + 'msg' => 'FakeExtension requires "missingAbility" ability', + ], + ], + ], + [ + [ + 'platform' => [ + 'ability-missingAbilityWithMessage' => true, + ], + ], + [ + [ + 'missing' => 'missingAbilityWithMessage', + 'type' => 'missing-ability', + // phpcs:ignore Generic.Files.LineLength.TooLong + 'msg' => 'FakeExtension requires "missingAbilityWithMessage" ability: Missing.', + ], + ], + ], + [ + [ + 'platform' => [ + 'ability-missingAbility' => false, + ], + ], + [], + ], + [ + [ + 'platform' => [ + 'ability-missingAbilityWithMessage' => false, + ], + ], + [], + ], ]; } @@ -282,6 +373,26 @@ class VersionCheckerTest extends PHPUnit\Framework\TestCase { ], 'phpLoadedExtension', ], + [ + [ + 'FakeExtension' => [ + 'platform' => [ + 'ability-invalidAbility' => true, + ], + ], + ], + 'ability-invalidAbility', + ], + [ + [ + 'FakeExtension' => [ + 'platform' => [ + 'presentAbility' => true, + ], + ], + ], + 'presentAbility', + ], [ [ 'FakeExtension' => [ @@ -308,7 +419,15 @@ class VersionCheckerTest extends PHPUnit\Framework\TestCase { * @dataProvider provideInvalidDependency */ public function testInvalidDependency( $depencency, $type ) { - $checker = new VersionChecker( '1.0.0', '7.0.0', [ 'phpLoadedExtension' ] ); + $checker = new VersionChecker( + '1.0.0', + '7.0.0', + [ 'phpLoadedExtension' ], + [ + 'presentAbility' => true, + 'missingAbility' => false, + ] + ); $this->setExpectedException( UnexpectedValueException::class, "Dependency type $type unknown in FakeExtension" @@ -330,4 +449,31 @@ class VersionCheckerTest extends PHPUnit\Framework\TestCase { ], ] ); } + + /** + * @dataProvider provideInvalidAbilityType + */ + public function testInvalidAbilityType( $value ) { + $checker = new VersionChecker( '1.0.0', '7.0.0', [], [ 'presentAbility' => true ] ); + $this->setExpectedException( + UnexpectedValueException::class, + 'Only booleans are allowed to to indicate the presence of abilities in FakeExtension' + ); + $checker->checkArray( [ + 'FakeExtension' => [ + 'platform' => [ + 'ability-presentAbility' => $value, + ], + ], + ] ); + } + + public function provideInvalidAbilityType() { + return [ + [ null ], + [ 1 ], + [ '1' ], + ]; + } + }