Refactor checkComposerLockUpToDate.php logic out to reuseable class

Introduce LockFileChecker that used to check whether
composer-installed dependencies (no-dev) are up-to-date.

Bug: T283389
Change-Id: I0d56f235604d5c856bae5d170230f8c7ca0729c6
This commit is contained in:
RazeSoldier 2021-11-07 20:58:38 +08:00 committed by Reedy
parent 5dca00526a
commit 20e77793b8
13 changed files with 242 additions and 52 deletions

View file

@ -996,6 +996,7 @@ $wgAutoloadLocalClasses = [
'MediaWiki\\CommentFormatter\\StringCommentIterator' => __DIR__ . '/includes/CommentFormatter/StringCommentIterator.php',
'MediaWiki\\CommentStore\\CommentStore' => __DIR__ . '/includes/CommentStore/CommentStore.php',
'MediaWiki\\CommentStore\\CommentStoreComment' => __DIR__ . '/includes/CommentStore/CommentStoreComment.php',
'MediaWiki\\Composer\\LockFileChecker' => __DIR__ . '/includes/composer/LockFileChecker.php',
'MediaWiki\\Config\\Config' => __DIR__ . '/includes/config/Config.php',
'MediaWiki\\Config\\ConfigException' => __DIR__ . '/includes/config/ConfigException.php',
'MediaWiki\\Config\\ConfigFactory' => __DIR__ . '/includes/config/ConfigFactory.php',

View file

@ -46,6 +46,7 @@ class AutoLoader {
'MediaWiki\\Block\\' => __DIR__ . '/block/',
'MediaWiki\\Cache\\' => __DIR__ . '/cache/',
'MediaWiki\\ChangeTags\\' => __DIR__ . '/changetags/',
'MediaWiki\\Composer\\' => __DIR__ . '/composer/',
'MediaWiki\\Config\\' => __DIR__ . '/config/',
'MediaWiki\\Content\\' => __DIR__ . '/content/',
'MediaWiki\\DB\\' => __DIR__ . '/db/',

View file

@ -0,0 +1,104 @@
<?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
*
* @file
*/
namespace MediaWiki\Composer;
use Composer\Semver\Semver;
use ComposerJson;
use ComposerLock;
use Status;
/**
* Used to check whether composer-installed dependencies (no-dev) are up-to-date
* @since 1.42
*/
class LockFileChecker {
/** @var ComposerJson */
private $composerJson;
/** @var ComposerJson */
private $composerLock;
/**
* @param ComposerJson $composerJson
* @param ComposerLock $composerLock
*/
public function __construct( ComposerJson $composerJson, ComposerLock $composerLock ) {
$this->composerJson = $composerJson;
$this->composerLock = $composerLock;
}
/**
* This method will return a {@link Status} instance,
* you can use {@link Status::isGood()} to simply determine that
* the composer-installed dependencies are up-to-date.
* @return Status
*/
public function check(): Status {
$status = Status::newGood();
$requiredButOld = [];
$requiredButMissing = [];
$installed = $this->composerLock->getInstalledDependencies();
foreach ( $this->composerJson->getRequiredDependencies() as $name => $version ) {
// Not installed at all.
if ( !isset( $installed[$name] ) ) {
$requiredButMissing[] = [
'name' => $name,
'wantedVersion' => $version
];
continue;
}
// Installed; need to check it's the right version
if ( !SemVer::satisfies( $installed[$name]['version'], $version ) ) {
$requiredButOld[] = [
'name' => $name,
'wantedVersion' => $version,
'suppliedVersion' => $installed[$name]['version']
];
}
// We're happy; loop to the next dependency.
}
if ( count( $requiredButOld ) === 0 && count( $requiredButMissing ) === 0 ) {
// We couldn't find any out-of-date or missing dependencies, so assume everything is ok!
return $status;
}
foreach ( $requiredButOld as [
"name" => $name,
"suppliedVersion" => $suppliedVersion,
"wantedVersion" => $wantedVersion
] ) {
$status->error( 'composer-deps-outdated', $name, $suppliedVersion, $wantedVersion );
}
foreach ( $requiredButMissing as [
"name" => $name,
"wantedVersion" => $wantedVersion
] ) {
$status->error( 'composer-deps-notinstalled', $name, $wantedVersion );
}
return $status;
}
}

View file

@ -4644,5 +4644,7 @@
"benefit-1-description": "Watchlists that allow you to keep track of pages that youre interested in.",
"benefit-2-description": "Permanent list of contributions youve made to the project.",
"benefit-3-description": "Preferences that allow you to customize your experience.",
"temp-user-unable-to-acquire": "Unable to acquire a temporary account username. Please try again."
"temp-user-unable-to-acquire": "Unable to acquire a temporary account username. Please try again.",
"composer-deps-outdated": "$1: $2 installed, $3 required.",
"composer-deps-notinstalled": "$1: not installed, $2 required."
}

View file

@ -4901,5 +4901,7 @@
"benefit-1-description": "The text for the first benefit shown on signup for temporary users.",
"benefit-2-description": "The text for the second benefit shown on signup for temporary users.",
"benefit-3-description": "The text for the third benefit shown on signup for temporary users.",
"temp-user-unable-to-acquire": "The error message shown when the server was unable to acquire a temporary account username for the user."
"temp-user-unable-to-acquire": "The error message shown when the server was unable to acquire a temporary account username for the user.",
"composer-deps-outdated": "Error in check composer-installed dependencies. Parameters:\n* $1 - Dependency name.\n* $2 - Installed version\n* $3 - Required version.",
"composer-deps-notinstalled": "Error message if a composer dependency is not install. Parameters:\n* $1 - Dependency name.\n* $2 - Required version."
}

View file

@ -2,7 +2,7 @@
require_once __DIR__ . '/Maintenance.php';
use Composer\Semver\Semver;
use MediaWiki\Composer\LockFileChecker;
/**
* Checks whether your composer-installed dependencies are up to date
@ -35,58 +35,23 @@ class CheckComposerLockUpToDate extends Maintenance {
$lock = new ComposerLock( $lockLocation );
$json = new ComposerJson( $jsonLocation );
$requiredButOld = [];
$requiredButMissing = [];
// Check all the dependencies to see if any are old
$installed = $lock->getInstalledDependencies();
foreach ( $json->getRequiredDependencies() as $name => $version ) {
// Not installed at all.
if ( !isset( $installed[$name] ) ) {
$requiredButMissing[] = [
'name' => $name,
'wantedVersion' => $version
];
continue;
}
// Installed; need to check it's the right version
if ( !SemVer::satisfies( $installed[$name]['version'], $version ) ) {
$requiredButOld[] = [
'name' => $name,
'wantedVersion' => $version,
'suppliedVersion' => $installed[$name]['version']
];
}
// We're happy; loop to the next dependency.
}
if ( count( $requiredButOld ) === 0 && count( $requiredButMissing ) === 0 ) {
// We couldn't find any out-of-date or missing dependencies, so assume everything is ok!
$checker = new LockFileChecker( $json, $lock );
$result = $checker->check();
if ( $result->isGood() ) {
// We couldn't find any out-of-date dependencies, so assume everything is ok!
$this->output( "Your composer.lock file is up to date with current dependencies!\n" );
return;
}
foreach ( $requiredButOld as [
"name" => $name,
"suppliedVersion" => $suppliedVersion,
"wantedVersion" => $wantedVersion
] ) {
$this->error( "$name: $suppliedVersion installed, $wantedVersion required.\n" );
}
foreach ( $requiredButMissing as [
"name" => $name,
"wantedVersion" => $wantedVersion
] ) {
$this->error( "$name: not installed, $wantedVersion required.\n" );
}
$this->fatalError(
'Error: your composer.lock file is not up to date. ' .
} else {
foreach ( $result->getErrors() as $error ) {
$this->error(
wfMessage( $error['message'], ...$error['params'] )->inLanguage( 'en' )->plain() . "\n"
);
}
$this->fatalError(
'Error: your composer.lock file is not up to date. ' .
'Run "composer update --no-dev" to install newer dependencies'
);
);
}
}
}

View file

@ -0,0 +1,60 @@
<?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
*
* @file
*/
use MediaWiki\Composer\LockFileChecker;
/**
* @covers \MediaWiki\Composer\LockFileChecker
*/
class LockFileCheckerTest extends MediaWikiIntegrationTestCase {
public function testOk() {
$json = new ComposerJson( __DIR__ . '/composer-testcase1.json' );
$lock = new ComposerLock( __DIR__ . '/composer-testcase1.lock' );
$checker = new LockFileChecker( $json, $lock );
$status = $checker->check();
$this->assertTrue( $status->isGood() );
}
public function testOutdated() {
$json = new ComposerJson( __DIR__ . '/composer-testcase2.json' );
$lock = new ComposerLock( __DIR__ . '/composer-testcase2.lock' );
$checker = new LockFileChecker( $json, $lock );
$status = $checker->check();
$this->assertFalse( $status->isGood() );
$this->assertSame( 'wikimedia/relpath: 2.9.9 installed, 3.0.0 required.', $status->getMessage()->plain() );
}
public function testNotInstalled() {
$json = new ComposerJson( __DIR__ . '/composer-testcase3.json' );
$lock = new ComposerLock( __DIR__ . '/composer-testcase3.lock' );
$checker = new LockFileChecker( $json, $lock );
$status = $checker->check();
$this->assertFalse( $status->isGood() );
$errors = $status->getErrors();
$msgs = [];
foreach ( $errors as $error ) {
$msgs[] = wfMessage( $error['message'], ...$error['params'] )->plain();
}
$this->assertArrayEquals( [
'wikimedia/relpath: 2.9.9 installed, 3.0.0 required.',
'wikimedia/at-ease: not installed, 2.1.0 required.',
], $msgs );
}
}

View file

@ -0,0 +1,6 @@
{
"require": {
"wikimedia/at-ease": "2.1.0",
"wikimedia/relpath": "3.0.0"
}
}

View file

@ -0,0 +1,14 @@
{
"packages": [
{
"name": "wikimedia/at-ease",
"version": "v2.1.0",
"type": "library"
},
{
"name": "wikimedia/relpath",
"version": "3.0.0",
"type": "library"
}
]
}

View file

@ -0,0 +1,6 @@
{
"require": {
"wikimedia/at-ease": "2.1.0",
"wikimedia/relpath": "3.0.0"
}
}

View file

@ -0,0 +1,14 @@
{
"packages": [
{
"name": "wikimedia/at-ease",
"version": "v2.1.0",
"type": "library"
},
{
"name": "wikimedia/relpath",
"version": "2.9.9",
"type": "library"
}
]
}

View file

@ -0,0 +1,6 @@
{
"require": {
"wikimedia/at-ease": "2.1.0",
"wikimedia/relpath": "3.0.0"
}
}

View file

@ -0,0 +1,9 @@
{
"packages": [
{
"name": "wikimedia/relpath",
"version": "2.9.9",
"type": "library"
}
]
}