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

Issue #3239103 by tedbow, kunal.sachdev, phenaproxima: Add setting to...

Issue #3239103 by tedbow, kunal.sachdev, phenaproxima: Add setting to enable/disable updates on cron
parent 36ff1b6e
No related branches found
No related tags found
1 merge request!56Issue #3239103: Add setting to enable/disable updates on cron
......@@ -6,9 +6,12 @@
*/
use Drupal\automatic_updates\CronUpdater;
use Drupal\automatic_updates\UpdateRecommender;
use Drupal\automatic_updates\Validation\AdminReadinessMessages;
use Drupal\Core\Extension\ExtensionVersion;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Url;
use Drupal\update\ProjectSecurityData;
/**
* Implements hook_page_top().
......@@ -85,6 +88,51 @@ 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) {
$recommender = new UpdateRecommender();
$drupal_project = $recommender->getProjectInfo();
$version = ExtensionVersion::createFromVersionString($drupal_project['existing_version']);
$current_minor = $version->getMajorVersion() . '.' . $version->getMinorVersion();
$supported_until_version = $version->getMajorVersion() . '.'
. ((int) $version->getMinorVersion() + ProjectSecurityData::CORE_MINORS_WITH_SECURITY_COVERAGE)
. '.0';
$form['automatic_updates_cron'] = [
'#type' => 'radios',
'#title' => t('Automatically update Drupal core'),
'#options' => [
CronUpdater::DISABLED => t('Disabled'),
CronUpdater::ALL => t('All supported updates'),
CronUpdater::SECURITY => t('Security updates only'),
],
'#default_value' => \Drupal::config('automatic_updates.settings')->get('cron'),
'#description' => t(
'If enabled, Drupal core will be automatically updated when an update is available. Automatic updates are only supported for @current_minor.x versions of Drupal core. Drupal @current_minor will receive security updates until @supported_until_version is released.',
[
'@current_minor' => $current_minor,
'@supported_until_version' => $supported_until_version,
]
),
];
$form += [
'#submit' => ['::submitForm'],
];
$form['#submit'][] = '_automatic_updates_update_settings_form_submit';
}
/**
* Submit function for the 'update_settings' form.
*/
function _automatic_updates_update_settings_form_submit(array &$form, FormStateInterface $form_state) {
\Drupal::configFactory()
->getEditable('automatic_updates.settings')
->set('cron', $form_state->getValue('automatic_updates_cron'))
->save();
}
/**
* Implements hook_local_tasks_alter().
*/
......
cron: security
automatic_updates.settings:
type: config_object
label: 'Automatic Updates settings'
mapping:
cron:
type: string
label: 'Enable automatic updates during cron'
......@@ -2,6 +2,7 @@
namespace Drupal\automatic_updates;
use Drupal\Core\Config\ConfigFactoryInterface;
use Drupal\Core\DependencyInjection\ContainerInjectionInterface;
use Drupal\Core\Logger\LoggerChannelFactoryInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
......@@ -15,6 +16,27 @@ use Symfony\Component\DependencyInjection\ContainerInterface;
*/
class CronUpdater implements ContainerInjectionInterface {
/**
* All automatic updates are disabled.
*
* @var string
*/
public const DISABLED = 'disable';
/**
* Only perform automatic security updates.
*
* @var string
*/
public const SECURITY = 'security';
/**
* All automatic updates are enabled.
*
* @var string
*/
public const ALL = 'patch';
/**
* The updater service.
*
......@@ -22,6 +44,13 @@ class CronUpdater implements ContainerInjectionInterface {
*/
protected $updater;
/**
* The config factory service.
*
* @var \Drupal\Core\Config\ConfigFactoryInterface
*/
protected $configFactory;
/**
* The logger.
*
......@@ -34,11 +63,14 @@ class CronUpdater implements ContainerInjectionInterface {
*
* @param \Drupal\automatic_updates\Updater $updater
* The updater service.
* @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory
* The config factory service.
* @param \Drupal\Core\Logger\LoggerChannelFactoryInterface $logger_factory
* The logger channel factory.
*/
public function __construct(Updater $updater, LoggerChannelFactoryInterface $logger_factory) {
public function __construct(Updater $updater, ConfigFactoryInterface $config_factory, LoggerChannelFactoryInterface $logger_factory) {
$this->updater = $updater;
$this->configFactory = $config_factory;
$this->logger = $logger_factory->get('automatic_updates');
}
......@@ -48,6 +80,7 @@ class CronUpdater implements ContainerInjectionInterface {
public static function create(ContainerInterface $container) {
return new static(
$container->get('automatic_updates.updater'),
$container->get('config.factory'),
$container->get('logger.factory')
);
}
......@@ -56,6 +89,14 @@ class CronUpdater implements ContainerInjectionInterface {
* Handles updates during cron.
*/
public function handleCron(): void {
$level = $this->configFactory->get('automatic_updates.settings')
->get('cron');
// If automatic updates are disabled, bail out.
if ($level === static::DISABLED) {
return;
}
$recommender = new UpdateRecommender();
try {
$recommended_release = $recommender->getRecommendedRelease(TRUE);
......@@ -76,6 +117,12 @@ class CronUpdater implements ContainerInjectionInterface {
return;
}
// If automatic updates are only enabled for security releases, bail out if
// the recommended release is not a security release.
if ($level === static::SECURITY && !$recommended_release->isSecurityRelease()) {
return;
}
// @todo Use the queue to add update jobs allowing jobs to span multiple
// cron runs.
$recommended_version = $recommended_release->getVersion();
......
<?php
namespace Drupal\Tests\automatic_updates\Kernel;
use Drupal\automatic_updates\CronUpdater;
use Drupal\Core\Form\FormState;
use Drupal\update\UpdateSettingsForm;
/**
* @covers \Drupal\automatic_updates\CronUpdater
* @covers \automatic_updates_form_update_settings_alter
*
* @group automatic_updates
*/
class CronUpdaterTest extends AutomaticUpdatesKernelTestBase {
/**
* {@inheritdoc}
*/
protected static $modules = [
'automatic_updates',
'package_manager',
];
/**
* Data provider for ::testUpdaterCalled().
*
* @return array[]
* Sets of arguments to pass to the test method.
*/
public function providerUpdaterCalled(): array {
$fixture_dir = __DIR__ . '/../../fixtures/release-history';
return [
'disabled, normal release' => [
CronUpdater::DISABLED,
"$fixture_dir/drupal.9.8.1.xml",
FALSE,
],
'disabled, security release' => [
CronUpdater::DISABLED,
"$fixture_dir/drupal.9.8.1-security.xml",
FALSE,
],
'security only, security release' => [
CronUpdater::SECURITY,
"$fixture_dir/drupal.9.8.1-security.xml",
TRUE,
],
'security only, normal release' => [
CronUpdater::SECURITY,
"$fixture_dir/drupal.9.8.1.xml",
FALSE,
],
'enabled, normal release' => [
CronUpdater::ALL,
"$fixture_dir/drupal.9.8.1.xml",
TRUE,
],
'enabled, security release' => [
CronUpdater::ALL,
"$fixture_dir/drupal.9.8.1-security.xml",
TRUE,
],
];
}
/**
* Tests that the cron handler calls the updater as expected.
*
* @param string $setting
* Whether automatic updates should be enabled during cron. Possible values
* are 'disable', 'security', and 'patch'.
* @param string $release_data
* If automatic updates are enabled, the path of the fake release metadata
* that should be served when fetching information on available updates.
* @param bool $will_update
* Whether an update should be performed, given the previous two arguments.
*
* @dataProvider providerUpdaterCalled
*/
public function testUpdaterCalled(string $setting, string $release_data, bool $will_update): void {
// Our form alter does not refresh information on available updates, so
// ensure that the appropriate update data is loaded beforehand.
$this->setReleaseMetadata($release_data);
update_get_available(TRUE);
// Submit the configuration form programmatically, to prove our alterations
// work as expected.
$form_builder = $this->container->get('form_builder');
$form_state = new FormState();
$form = $form_builder->buildForm(UpdateSettingsForm::class, $form_state);
// Ensure that the version ranges in the setting's description, which are
// computed dynamically, look correct.
$this->assertStringContainsString('Automatic updates are only supported for 9.8.x versions of Drupal core. Drupal 9.8 will receive security updates until 9.10.0 is released.', $form['automatic_updates_cron']['#description']);
$form_state->setValue('automatic_updates_cron', $setting);
$form_builder->submitForm(UpdateSettingsForm::class, $form_state);
// Mock the updater so we can assert that its methods are called or bypassed
// depending on configuration.
$will_update = (int) $will_update;
$updater = $this->prophesize('\Drupal\automatic_updates\Updater');
$updater->begin(['drupal' => '9.8.1'])->shouldBeCalledTimes($will_update);
$updater->stage()->shouldBeCalledTimes($will_update);
$updater->commit()->shouldBeCalledTimes($will_update);
$updater->clean()->shouldBeCalledTimes($will_update);
$this->container->set('automatic_updates.updater', $updater->reveal());
$this->container->get('cron')->run();
}
}
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