Skip to content
Snippets Groups Projects
Commit 9a08d182 authored by Ted Bowman's avatar Ted Bowman Committed by Adam G-H
Browse files

Issue #3275883 by tedbow, phenaproxima: Warn if cron updates are enabled and...

Issue #3275883 by tedbow, phenaproxima: Warn if cron updates are enabled and the site is on an unsupported branch
parent bc9bd02c
No related branches found
No related tags found
No related merge requests found
<?php
namespace Drupal\automatic_updates\Validator\VersionPolicy;
use Drupal\Core\Config\ConfigFactoryInterface;
use Drupal\Core\DependencyInjection\ContainerInjectionInterface;
use Drupal\Core\Extension\ExtensionVersion;
use Drupal\Core\StringTranslation\StringTranslationTrait;
use Drupal\Core\Url;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
* A policy rule that requires updating from a supported branch.
*
* @internal
* This is an internal part of Automatic Updates' version policy for
* Drupal core. It may be changed or removed at any time without warning.
* External code should not interact with this class.
*/
class SupportedBranchInstalled implements ContainerInjectionInterface {
use StringTranslationTrait;
/**
* The config factory service.
*
* @var \Drupal\Core\Config\ConfigFactoryInterface
*/
private $configFactory;
/**
* Constructs a SupportedBranchInstalled object.
*
* @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory
* The config factory service.
*/
public function __construct(ConfigFactoryInterface $config_factory) {
$this->configFactory = $config_factory;
}
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container) {
return new static(
$container->get('config.factory')
);
}
/**
* Checks if the installed version of Drupal is in a supported branch.
*
* @param string $installed_version
* The installed version of Drupal.
*
* @return \Drupal\Core\StringTranslation\TranslatableMarkup[]
* The error messages, if any.
*/
public function validate(string $installed_version): array {
$available_updates = update_get_available(TRUE);
$installed = ExtensionVersion::createFromVersionString($installed_version);
$installed_major = $installed->getMajorVersion();
$installed_minor = $installed->getMinorVersion();
$in_supported_major = FALSE;
$supported_branches = explode(',', $available_updates['drupal']['supported_branches']);
foreach ($supported_branches as $supported_branch) {
$supported_branch = ExtensionVersion::createFromSupportBranch($supported_branch);
// Check if this supported branch is in the same major version as what's
// installed, since that will influence our messaging.
if ($installed_major === $supported_branch->getMajorVersion()) {
$in_supported_major = TRUE;
// If the supported branch's major and minor versions are the same as
// the installed ones, this rule is fulfilled.
if ($installed_minor === $supported_branch->getMinorVersion()) {
return [];
}
}
}
// By this point, we know the installed version of Drupal is not in a
// supported branch, so we'll always show this message.
$messages = [
$this->t('The currently installed version of Drupal core, @installed_version, is not in a supported minor version. Your site will not be automatically updated during cron until it is updated to a supported minor version.', [
'@installed_version' => $installed_version,
]),
];
// If the installed version of Drupal is in a supported major branch, an
// attended update may be possible, depending on configuration.
$allow_minor_updates = $this->configFactory->get('automatic_updates.settings')
->get('allow_core_minor_updates');
if ($in_supported_major && $allow_minor_updates) {
$messages[] = $this->t('Use the <a href=":url">update form</a> to update to a supported version.', [
':url' => Url::fromRoute('automatic_updates.module_update')->toString(),
]);
}
else {
$messages[] = $this->t('See the <a href=":url">available updates page</a> for available updates.', [
':url' => Url::fromRoute('update.status')->toString(),
]);
}
return $messages;
}
}
......@@ -13,6 +13,7 @@ use Drupal\automatic_updates\Validator\VersionPolicy\MajorVersionMatch;
use Drupal\automatic_updates\Validator\VersionPolicy\MinorUpdatesEnabled;
use Drupal\automatic_updates\Validator\VersionPolicy\StableReleaseInstalled;
use Drupal\automatic_updates\Validator\VersionPolicy\ForbidDevSnapshot;
use Drupal\automatic_updates\Validator\VersionPolicy\SupportedBranchInstalled;
use Drupal\automatic_updates\Validator\VersionPolicy\TargetSecurityRelease;
use Drupal\automatic_updates\Validator\VersionPolicy\TargetVersionInstallable;
use Drupal\automatic_updates\Validator\VersionPolicy\TargetVersionStable;
......@@ -88,6 +89,8 @@ final class VersionPolicyValidator implements EventSubscriberInterface {
// If cron updates are enabled, the installed version must be stable;
// no alphas, betas, or RCs.
$rules[] = StableReleaseInstalled::class;
// It must also be in a supported branch.
$rules[] = SupportedBranchInstalled::class;
// If the target version is known, more rules apply.
if ($target_version) {
......@@ -145,10 +148,19 @@ final class VersionPolicyValidator implements EventSubscriberInterface {
$messages = $this->validateVersion($stage, $target_version);
if ($messages) {
$summary = $this->t('Updating from Drupal @installed_version to @target_version is not allowed.', [
'@installed_version' => $this->getInstalledVersion(),
'@target_version' => $target_version,
]);
$installed_version = $this->getInstalledVersion();
if ($target_version) {
$summary = $this->t('Updating from Drupal @installed_version to @target_version is not allowed.', [
'@installed_version' => $installed_version,
'@target_version' => $target_version,
]);
}
else {
$summary = $this->t('Updating from Drupal @installed_version is not allowed.', [
'@installed_version' => $installed_version,
]);
}
$event->addError($messages, $summary);
}
}
......
<?php
namespace Drupal\Tests\automatic_updates\Kernel\ReadinessValidation\VersionPolicy;
use Drupal\automatic_updates\Validator\VersionPolicy\SupportedBranchInstalled;
use Drupal\Tests\automatic_updates\Kernel\AutomaticUpdatesKernelTestBase;
/**
* @covers \Drupal\automatic_updates\Validator\VersionPolicy\SupportedBranchInstalled
*
* @group automatic_updates
*/
class SupportedBranchInstalledTest extends AutomaticUpdatesKernelTestBase {
/**
* {@inheritdoc}
*/
protected static $modules = ['automatic_updates'];
/**
* Data provider for ::testSupportedBranchInstalled().
*
* @return array[]
* Sets of arguments to pass to the test method.
*/
public function providerSupportedBranchInstalled(): array {
return [
'supported minor installed' => [
'9.8.0',
[FALSE, TRUE],
[],
],
// These two cases test a supported major version, but unsupported minor
// version.
'supported major installed, minor updates forbidden' => [
'9.6.1',
[FALSE],
[
'The currently installed version of Drupal core, 9.6.1, is not in a supported minor version. Your site will not be automatically updated during cron until it is updated to a supported minor version.',
'See the <a href="/admin/reports/updates">available updates page</a> for available updates.',
],
],
'supported major installed, minor updates allowed' => [
'9.6.1',
[TRUE],
[
'The currently installed version of Drupal core, 9.6.1, is not in a supported minor version. Your site will not be automatically updated during cron until it is updated to a supported minor version.',
'Use the <a href="/admin/modules/automatic-update">update form</a> to update to a supported version.',
],
],
'unsupported version installed' => [
'8.9.0',
[FALSE, TRUE],
[
'The currently installed version of Drupal core, 8.9.0, is not in a supported minor version. Your site will not be automatically updated during cron until it is updated to a supported minor version.',
'See the <a href="/admin/reports/updates">available updates page</a> for available updates.',
],
],
];
}
/**
* Tests that the installed version of Drupal must be in a supported branch.
*
* @param string $installed_version
* The installed version of Drupal core.
* @param bool[] $allow_minor_updates
* The values of the `allow_core_minor_updates` config setting that should
* be tested.
* @param string[] $expected_errors
* The expected error messages, if any.
*
* @dataProvider providerSupportedBranchInstalled
*/
public function testSupportedBranchInstalled(string $installed_version, array $allow_minor_updates, array $expected_errors): void {
$this->setCoreVersion($installed_version);
$this->setReleaseMetadata([
'drupal' => __DIR__ . '/../../../../fixtures/release-history/drupal.9.8.2.xml',
]);
$rule = SupportedBranchInstalled::create($this->container);
foreach ($allow_minor_updates as $setting) {
$this->config('automatic_updates.settings')
->set('allow_core_minor_updates', $setting)
->save();
$actual_errors = array_map('strval', $rule->validate($installed_version));
$this->assertSame($expected_errors, $actual_errors);
}
}
}
......@@ -120,6 +120,41 @@ class VersionPolicyValidatorTest extends AutomaticUpdatesKernelTestBase {
]),
],
],
// These three cases prove that updating from an unsupported minor version
// will raise a readiness error if unattended updates are enabled.
// Furthermore, if an error is raised, the messaging will vary depending
// on whether attended updates across minor versions are allowed. (Note
// that the target version will not be automatically detected because the
// release metadata used in these cases doesn't have any 9.7.x releases.)
'update from unsupported minor, cron disabled' => [
'9.7.1',
"$metadata_dir/drupal.9.8.1-security.xml",
[CronUpdater::DISABLED],
[],
],
'update from unsupported minor, cron enabled, minor updates forbidden' => [
'9.7.1',
"$metadata_dir/drupal.9.8.1-security.xml",
[CronUpdater::SECURITY, CronUpdater::ALL],
[
$this->createValidationResult('9.7.1', NULL, [
'The currently installed version of Drupal core, 9.7.1, is not in a supported minor version. Your site will not be automatically updated during cron until it is updated to a supported minor version.',
'See the <a href="/admin/reports/updates">available updates page</a> for available updates.',
]),
],
],
'update from unsupported minor, cron enabled, minor updates allowed' => [
'9.7.1',
"$metadata_dir/drupal.9.8.1-security.xml",
[CronUpdater::SECURITY, CronUpdater::ALL],
[
$this->createValidationResult('9.7.1', NULL, [
'The currently installed version of Drupal core, 9.7.1, is not in a supported minor version. Your site will not be automatically updated during cron until it is updated to a supported minor version.',
'Use the <a href="/admin/modules/automatic-update">update form</a> to update to a supported version.',
]),
],
TRUE,
],
];
}
......@@ -137,16 +172,20 @@ class VersionPolicyValidatorTest extends AutomaticUpdatesKernelTestBase {
* \Drupal\automatic_updates\CronUpdater::ALL.
* @param \Drupal\package_manager\ValidationResult[] $expected_results
* The expected validation results.
* @param bool $allow_minor_updates
* (optional) Whether or not attended updates across minor updates are
* allowed. Defaults to FALSE.
*
* @dataProvider providerReadinessCheck
*/
public function testReadinessCheck(string $installed_version, string $release_metadata, array $cron_modes, array $expected_results): void {
public function testReadinessCheck(string $installed_version, string $release_metadata, array $cron_modes, array $expected_results, bool $allow_minor_updates = FALSE): void {
$this->setCoreVersion($installed_version);
$this->setReleaseMetadata(['drupal' => $release_metadata]);
foreach ($cron_modes as $cron_mode) {
$this->config('automatic_updates.settings')
->set('cron', $cron_mode)
->set('allow_core_minor_updates', $allow_minor_updates)
->save();
$this->assertCheckerResultsFromManager($expected_results, TRUE);
......@@ -282,6 +321,17 @@ class VersionPolicyValidatorTest extends AutomaticUpdatesKernelTestBase {
[],
TRUE,
],
// If attended updates across minor versions are allowed, it's okay to
// update from an unsupported minor version.
'attended update from unsupported minor allowed' => [
['automatic_updates.updater'],
'9.7.9',
"$metadata_dir/drupal.9.8.1-security.xml",
[CronUpdater::SECURITY, CronUpdater::ALL],
['drupal' => '9.8.1'],
[],
TRUE,
],
// Unattended updates to unstable versions are not allowed.
'unattended update to unstable version' => [
['automatic_updates.cron_updater'],
......@@ -295,6 +345,37 @@ class VersionPolicyValidatorTest extends AutomaticUpdatesKernelTestBase {
]),
],
],
// Unattended updates from an unsupported minor are never allowed, but
// the messaging will vary depending on whether attended updates across
// minor versions are allowed.
'unattended update from unsupported minor, minor updates forbidden' => [
['automatic_updates.cron_updater'],
'9.7.9',
"$metadata_dir/drupal.9.8.1-security.xml",
[CronUpdater::SECURITY, CronUpdater::ALL],
['drupal' => '9.8.1'],
[
$this->createValidationResult('9.7.9', '9.8.1', [
'The currently installed version of Drupal core, 9.7.9, is not in a supported minor version. Your site will not be automatically updated during cron until it is updated to a supported minor version.',
'See the <a href="/admin/reports/updates">available updates page</a> for available updates.',
]),
],
FALSE,
],
'unattended update from unsupported minor, minor updates allowed' => [
['automatic_updates.cron_updater'],
'9.7.9',
"$metadata_dir/drupal.9.8.1-security.xml",
[CronUpdater::SECURITY, CronUpdater::ALL],
['drupal' => '9.8.1'],
[
$this->createValidationResult('9.7.9', '9.8.1', [
'The currently installed version of Drupal core, 9.7.9, is not in a supported minor version. Your site will not be automatically updated during cron until it is updated to a supported minor version.',
'Use the <a href="/admin/modules/automatic-update">update form</a> to update to a supported version.',
]),
],
TRUE,
],
];
}
......@@ -358,19 +439,26 @@ class VersionPolicyValidatorTest extends AutomaticUpdatesKernelTestBase {
*
* @param string $installed_version
* The installed version of Drupal core.
* @param string $target_version
* The target version of Drupal core.
* @param string|null $target_version
* The target version of Drupal core, or NULL if it's not known.
* @param string[] $messages
* The error messages that the result should contain.
*
* @return \Drupal\package_manager\ValidationResult
* A validation error object with the appropriate summary.
*/
private function createValidationResult(string $installed_version, string $target_version, array $messages): ValidationResult {
$summary = t('Updating from Drupal @installed_version to @target_version is not allowed.', [
'@installed_version' => $installed_version,
'@target_version' => $target_version,
]);
private function createValidationResult(string $installed_version, ?string $target_version, array $messages): ValidationResult {
if ($target_version) {
$summary = t('Updating from Drupal @installed_version to @target_version is not allowed.', [
'@installed_version' => $installed_version,
'@target_version' => $target_version,
]);
}
else {
$summary = t('Updating from Drupal @installed_version is not allowed.', [
'@installed_version' => $installed_version,
]);
}
return ValidationResult::createError($messages, $summary);
}
......
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