config_preview_deploy-1.0.0-alpha3/src/ProductionConfigDeployer.php
src/ProductionConfigDeployer.php
<?php
declare(strict_types=1);
namespace Drupal\config_preview_deploy;
use Drupal\Core\Config\Checkpoint\CheckpointListInterface;
use Drupal\Core\Config\Checkpoint\CheckpointStorage;
use Drupal\Core\Config\StorageInterface;
use Drupal\Core\KeyValueStore\KeyValueFactoryInterface;
use Drupal\Core\Logger\LoggerChannelFactoryInterface;
use Drupal\Core\Logger\LoggerChannelInterface;
use Drupal\Core\StringTranslation\StringTranslationTrait;
use Symfony\Component\HttpFoundation\RequestStack;
/**
* Applies configuration diffs to the active configuration on production.
*/
class ProductionConfigDeployer {
use StringTranslationTrait;
/**
* The checkpoint storage.
*/
protected CheckpointStorage $checkpointStorage;
/**
* Logger channel.
*/
protected LoggerChannelInterface $logger;
/**
* The config diff service.
*/
protected ConfigDiff $configDiff;
/**
* The request stack.
*/
protected RequestStack $requestStack;
/**
* Constructs a ConfigDeployer object.
*/
public function __construct(
StorageInterface $configStorage,
KeyValueFactoryInterface $keyValueFactory,
CheckpointListInterface $checkpoints,
LoggerChannelFactoryInterface $loggerFactory,
ConfigDiff $configDiff,
RequestStack $requestStack,
) {
$this->checkpointStorage = new CheckpointStorage($configStorage, $checkpoints, $keyValueFactory);
$this->logger = $loggerFactory->get('config_preview_deploy');
$this->configDiff = $configDiff;
$this->requestStack = $requestStack;
}
/**
* Applies a configuration diff to production.
*
* Note: Authentication should be validated by the caller before invoking
* this method.
*
* @param string $diff
* The unified diff to apply.
* @param string $environment
* The environment that is deploying.
*
* @return array
* Deployment results with checkpoint ID and applied changes.
*
* @throws \RuntimeException
* When deployment fails.
*/
public function deployDiff(string $diff, string $environment): array {
// Create checkpoint before applying changes.
$checkpointLabel = $this->t('Import from @env - @date', [
'@env' => $environment,
'@date' => date('Y-m-d H:i:s'),
])->render();
$checkpoint = $this->checkpointStorage->checkpoint($checkpointLabel);
$this->logger->notice('Created checkpoint @id before import from @env', [
'@id' => $checkpoint->id,
'@env' => $environment,
]);
try {
// Apply the diff using ConfigDiff service.
$changes = $this->configDiff->validateAndApplyDiff($diff);
$request = $this->requestStack->getCurrentRequest();
$this->logger->notice('Configuration deployed from @environment with checkpoint @checkpoint on @host', [
'@environment' => $environment,
'@checkpoint' => $checkpoint->id,
'@host' => $request->getHost(),
]);
return [
'success' => TRUE,
'checkpoint_id' => $checkpoint->id,
'changes' => $changes,
'environment' => $environment,
];
}
catch (\Exception $e) {
$this->logger->error('Configuration deployment failed from @environment: @error', [
'@environment' => $environment,
'@error' => $e->getMessage(),
]);
throw new \RuntimeException($this->t('Configuration deployment failed: @error', ['@error' => $e->getMessage()])->render());
}
}
/**
* Creates a checkpoint before applying configuration.
*
* @param string $label
* The checkpoint label.
*
* @return string
* The checkpoint ID.
*/
public function createCheckpoint(string $label): string {
$checkpoint = $this->checkpointStorage->checkpoint($label);
$this->logger->notice('Created checkpoint @id with label "@label"', [
'@id' => $checkpoint->id,
'@label' => $label,
]);
return $checkpoint->id;
}
}
