Skip to content
Snippets Groups Projects
Commit 139cc2bf authored by Ted Bowman's avatar Ted Bowman
Browse files

Issue #3271240 by tedbow, phenaproxima: Allow getting update information for...

Issue #3271240 by tedbow, phenaproxima: Allow getting update information for any project not just Drupal core
parent b2679cc1
No related branches found
No related tags found
No related merge requests found
......@@ -148,7 +148,7 @@ function automatic_updates_form_update_manager_update_form_alter(&$form, FormSta
* Implements hook_form_FORM_ID_alter() for 'update_settings' form.
*/
function automatic_updates_form_update_settings_alter(array &$form, FormStateInterface $form_state, string $form_id) {
$project_info = new ProjectInfo();
$project_info = new ProjectInfo('drupal');
$version = ExtensionVersion::createFromVersionString($project_info->getInstalledVersion());
$current_minor = $version->getMajorVersion() . '.' . $version->getMinorVersion();
// @todo In https://www.drupal.org/node/2998285 use the update XML to
......
......@@ -86,7 +86,7 @@ class CronUpdater extends Updater {
* The version to which to update.
*/
private function performUpdate(string $update_version): void {
$installed_version = (new ProjectInfo())->getInstalledVersion();
$installed_version = (new ProjectInfo('drupal'))->getInstalledVersion();
if (empty($installed_version)) {
$this->logger->error('Unable to determine the current version of Drupal core.');
return;
......
......@@ -131,7 +131,7 @@ class UpdaterForm extends FormBase {
'#theme' => 'update_last_check',
'#last' => $this->state->get('update.last_check', 0),
];
$project_info = new ProjectInfo();
$project_info = new ProjectInfo('drupal');
try {
// @todo Until https://www.drupal.org/i/3264849 is fixed, we can only show
......@@ -176,7 +176,7 @@ class UpdaterForm extends FormBase {
],
];
$project = $project_info->getProjectInfo();
$project = $project_info->getProjectInfo('drupal');
if (empty($project['title']) || empty($project['link'])) {
throw new \UnexpectedValueException('Expected project data to have a title and link.');
}
......
......@@ -10,51 +10,72 @@ use Drupal\update\UpdateManagerInterface;
/**
* Defines a class for retrieving project information from Update module.
*
* @todo Allow passing a project name to handle more than Drupal core in
* https://www.drupal.org/i/3271240.
* @internal
* External code should use the Update API directly.
*/
class ProjectInfo {
/**
* Returns up-to-date project information for Drupal core.
* The project name.
*
* @var string
*/
protected $name;
/**
* Constructs a ProjectInfo object.
*
* @param string $name
* The project name.
*/
public function __construct(string $name) {
$this->name = $name;
}
/**
* Returns up-to-date project information.
*
* @param bool $refresh
* (optional) Whether to fetch the latest information about available
* updates from drupal.org. This can be an expensive operation, so defaults
* to FALSE.
*
* @return array
* The retrieved project information for Drupal core.
* @return array|null
* The retrieved project information.
*
* @throws \RuntimeException
* If data about available updates cannot be retrieved.
*/
public function getProjectInfo(bool $refresh = FALSE): array {
public function getProjectInfo(bool $refresh = FALSE): ?array {
$available_updates = update_get_available($refresh);
$project_data = update_calculate_project_data($available_updates);
return $project_data['drupal'];
return $project_data[$this->name] ?? NULL;
}
/**
* Gets all releases of Drupal core to which the site can update.
* Gets all project releases to which the site can update.
*
* @param bool $refresh
* (optional) Whether to fetch the latest information about available
* updates from drupal.org. This can be an expensive operation, so defaults
* to FALSE.
*
* @return \Drupal\automatic_updates_9_3_shim\ProjectRelease[]
* An array of possible update releases with release versions as keys. The
* releases are in descending order by version number (i.e., higher versions
* are listed first).
* @return \Drupal\automatic_updates_9_3_shim\ProjectRelease[]|null
* If the project information is available, an array of releases that can be
* installed, keyed by version number; otherwise NULL. The releases are in
* descending order by version number (i.e., higher versions are listed
* first).
*
* @throws \RuntimeException
* Thrown if $refresh is TRUE and there are no available releases.
*
* @todo Remove or simplify this function in https://www.drupal.org/i/3252190.
*/
public function getInstallableReleases(bool $refresh = FALSE): array {
public function getInstallableReleases(bool $refresh = FALSE): ?array {
$project = $this->getProjectInfo($refresh);
if (!$project) {
return NULL;
}
$installed_version = $this->getInstalledVersion();
// If we refreshed and we were able to get available releases we should
// always have at least have the current release stored.
......@@ -67,7 +88,7 @@ class ProjectInfo {
}
elseif (empty($project['recommended'])) {
// If we don't know what to recommend they update to, time to freak out.
throw new \LogicException('Drupal core is out of date, but the recommended version could not be determined.');
throw new \LogicException("The '{$this->name}' project is out of date, but the recommended version could not be determined.");
}
$installable_releases = [];
if (Comparator::greaterThan($project['recommended'], $installed_version)) {
......@@ -95,12 +116,15 @@ class ProjectInfo {
* updates from drupal.org. This can be an expensive operation, so defaults
* to FALSE.
*
* @return string
* The installed project version as known to the Update module.
* @return string|null
* The installed project version as known to the Update module or NULL if
* the project information is not available.
*/
public function getInstalledVersion(bool $refresh = FALSE): string {
$project_data = $this->getProjectInfo($refresh);
return $project_data['existing_version'];
public function getInstalledVersion(bool $refresh = FALSE): ?string {
if ($project_data = $this->getProjectInfo($refresh)) {
return $project_data['existing_version'];
}
return NULL;
}
}
......@@ -36,7 +36,7 @@ class ReleaseChooser {
*/
public function __construct(UpdateVersionValidator $version_validator) {
$this->versionValidator = $version_validator;
$this->projectInfo = new ProjectInfo();
$this->projectInfo = new ProjectInfo('drupal');
}
/**
......
......@@ -113,7 +113,7 @@ class ReadinessValidationManager implements EventSubscriberInterface {
}
$event = new ReadinessCheckEvent($stage);
// Version validators will need up-to-date project info.
(new ProjectInfo())->getProjectInfo(TRUE);
(new ProjectInfo('drupal'))->getProjectInfo(TRUE);
$this->eventDispatcher->dispatch($event);
$results = $event->getResults();
$this->keyValueExpirable->setWithExpire(
......
......@@ -78,7 +78,7 @@ final class CronUpdateVersionValidator extends UpdateVersionValidator {
// update release is a security release.
$level = $this->configFactory->get('automatic_updates.settings')->get('cron');
if ($level === CronUpdater::SECURITY) {
$releases = (new ProjectInfo())->getInstallableReleases();
$releases = (new ProjectInfo('drupal'))->getInstallableReleases();
// @todo Remove this check and add validation to
// \Drupal\automatic_updates\Validator\UpdateVersionValidator::getValidationResult()
// to ensure the update release is always secure and supported in
......
......@@ -56,7 +56,7 @@ class UpdateVersionValidator implements EventSubscriberInterface {
* The running core version as known to the Update module.
*/
protected function getCoreVersion(): string {
return (new ProjectInfo())->getInstalledVersion();
return (new ProjectInfo('drupal'))->getInstalledVersion();
}
/**
......@@ -119,7 +119,7 @@ class UpdateVersionValidator implements EventSubscriberInterface {
// @todo Remove this code in https://www.drupal.org/i/3272326 when we add
// add a validator that will warn if cron updates will no longer work
// because the site is more than 1 patch release behind.
$project_info = new ProjectInfo();
$project_info = new ProjectInfo('drupal');
if ($possible_releases = $project_info->getInstallableReleases()) {
$possible_release = array_pop($possible_releases);
return $possible_release->getVersion();
......
......@@ -122,20 +122,24 @@ class ProjectInfoTest extends UnitTestCase {
'8.2.4' => $release_objects['8.2.4'],
],
],
[
NULL,
NULL,
],
];
}
/**
* @covers ::getInstallableReleases
*
* @param array $project_data
* @param array|null $project_data
* The project data to return from ::getProjectInfo().
* @param \Drupal\automatic_updates_9_3_shim\ProjectRelease[] $expected_releases
* @param \Drupal\automatic_updates_9_3_shim\ProjectRelease[]|null $expected_releases
* The expected releases.
*
* @dataProvider providerGetInstallableReleases
*/
public function testGetInstallableReleases(array $project_data, array $expected_releases): void {
public function testGetInstallableReleases(?array $project_data, ?array $expected_releases): void {
$project_info = $this->getMockedProjectInfo($project_data);
$this->assertEqualsCanonicalizing($expected_releases, $project_info->getInstallableReleases());
}
......@@ -159,7 +163,7 @@ class ProjectInfoTest extends UnitTestCase {
];
$project_info = $this->getMockedProjectInfo($project_data);
$this->expectException('LogicException');
$this->expectExceptionMessage('Drupal core is out of date, but the recommended version could not be determined.');
$this->expectExceptionMessage("The 'drupal' project is out of date, but the recommended version could not be determined.");
$project_info->getInstallableReleases();
}
......@@ -169,20 +173,23 @@ class ProjectInfoTest extends UnitTestCase {
public function testGetInstalledVersion(): void {
$project_info = $this->getMockedProjectInfo(['existing_version' => '1.2.3']);
$this->assertSame('1.2.3', $project_info->getInstalledVersion());
$project_info = $this->getMockedProjectInfo(NULL);
$this->assertSame(NULL, $project_info->getInstalledVersion());
}
/**
* Mocks a ProjectInfo object.
*
* @param array $project_data
* @param array|null $project_data
* The project info that should be returned by the mock's ::getProjectInfo()
* method.
*
* @return \Drupal\automatic_updates\ProjectInfo
* The mocked object.
*/
private function getMockedProjectInfo(array $project_data): ProjectInfo {
private function getMockedProjectInfo(?array $project_data): ProjectInfo {
$project_info = $this->getMockBuilder(ProjectInfo::class)
->setConstructorArgs(['drupal'])
->onlyMethods(['getProjectInfo'])
->getMock();
$project_info->expects($this->any())
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment