diff --git a/.env b/.env index 6fac3df6fedd79da39205f048781dcaf824c817f..6c0664620a171eb177e8ac3cd100ff8cc47f065f 100644 --- a/.env +++ b/.env @@ -2,3 +2,7 @@ PROJECT_NAME=config_pr PROJECT_BASE_URL=localhost PROJECT_PORT=8089 DEPENDENCIES=bitbucket/client:^4.6 m4tthumphrey/php-gitlab-api:^11 php-http/guzzle7-adapter:^1.0 knplabs/github-api:^3.13 +GITHUB_TOKEN=XXX +GITLAB_TOKEN=XXX +REPO_OWNER=marcelovani +REPO_NAME=config_pr_automation diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 79b7fa9b035baf5120c6e298565ba5abbc2a227c..f21bc643790e170752b0b6bcf3990e56661babb6 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -22,8 +22,6 @@ variables: OPT_IN_TEST_CURRENT: 1 OPT_IN_TEST_NEXT_MINOR: 1 OPT_IN_TEST_NEXT_MAJOR: 1 - GITHUB_REPO_URL: https://github.com/marcelovani/config_pr_automation.git - GITLAB_REPO_URL: https://gitlab.com/marcelovani/config_pr_automation.git phpcs: allow_failure: true phpstan: @@ -35,4 +33,4 @@ phpstan (next major): phpunit (next minor): allow_failure: false phpunit (next major): - allow_failure: false + allow_failure: true diff --git a/Makefile b/Makefile index 1f09e3f6239330c14a885d3ca37cb869e4c7e679..ddd3a4c6aa48c563e7f7b11a69224e920dccbfc7 100644 --- a/Makefile +++ b/Makefile @@ -57,6 +57,22 @@ reinstall-local: # Test local build test-local: + docker exec --env-file=./.env -it drupalci_${PROJECT_NAME} bash -c '\ + sudo -u www-data \ + REPO_OWNER=${REPO_OWNER} REPO_NAME=${REPO_NAME} GITHUB_TOKEN=${GITHUB_TOKEN} GITLAB_TOKEN=${GITLAB_TOKEN} \ + php web/core/scripts/run-tests.sh \ + --php /usr/local/bin/php \ + --verbose \ + --keep-results \ + --color \ + --concurrency "32" \ + --repeat "1" \ + --types "Simpletest,PHPUnit-Unit,PHPUnit-Kernel,PHPUnit-Functional" \ + --sqlite sites/default/files/.ht.sqlite \ + --url http://localhost \ + --directory "modules/contrib/${PROJECT_NAME}"' + +test-local-class: docker exec -it drupalci_${PROJECT_NAME} bash -c '\ sudo -u www-data php web/core/scripts/run-tests.sh \ --php /usr/local/bin/php \ @@ -68,7 +84,7 @@ test-local: --types "Simpletest,PHPUnit-Unit,PHPUnit-Kernel,PHPUnit-Functional" \ --sqlite sites/default/files/.ht.sqlite \ --url http://localhost \ - --directory "modules/contrib/${PROJECT_NAME}"' + --class "Drupal\Tests\config_pr\Functional\ConfigPrTest"' # Test in non-interactive mode test-8: diff --git a/config/schema/config_pr.schema.yml b/config/schema/config_pr.schema.yml new file mode 100644 index 0000000000000000000000000000000000000000..8bfff13b9d9f04f92cf5ebccc96af2cc28a9f3d1 --- /dev/null +++ b/config/schema/config_pr.schema.yml @@ -0,0 +1,223 @@ +# Schema for the configuration files of the Configuration Pull Request module. + +field.field.user.user.field_config_pr_auth_token: + type: config_entity + label: 'Config PR Auth Token field' + mapping: + id: + type: string + label: 'ID' + field_name: + type: string + label: 'Field name' + entity_type: + type: string + label: 'Entity type' + bundle: + type: string + label: 'Bundle' + label: + type: label + label: 'Label' + description: + type: text + label: 'Help text' + required: + type: boolean + label: 'Required field' + translatable: + type: boolean + label: 'Translatable' + default_value: + type: sequence + label: 'Default value' + sequence: + type: mapping + mapping: + value: + type: string + label: 'Value' + default_value_callback: + type: string + label: 'Default value callback' + settings: + type: field.field_settings.[%parent.field_type] + field_type: + type: string + label: 'Field type' + +field.storage.user.field_config_pr_auth_token: + type: config_entity + label: 'Config PR Auth Token field storage' + mapping: + id: + type: string + label: 'ID' + field_name: + type: string + label: 'Field name' + entity_type: + type: string + label: 'Entity type' + type: + type: string + label: 'Type' + settings: + type: mapping + label: 'Settings' + mapping: + max_length: + type: integer + label: 'Maximum length' + case_sensitive: + type: boolean + label: 'Case sensitive' + is_ascii: + type: boolean + label: 'Contains US ASCII characters only' + module: + type: string + label: 'Module' + locked: + type: boolean + label: 'Locked' + cardinality: + type: integer + label: 'Maximum number of values users can enter' + translatable: + type: boolean + label: 'Translatable' + indexes: + type: sequence + label: 'Indexes' + sequence: + type: sequence + label: 'Indexes' + sequence: + type: string + label: 'Index' + persist_with_no_fields: + type: boolean + label: 'Persist field storage with no fields' + custom_storage: + type: boolean + label: 'Enable custom storage' + +core.entity_form_display.user.user.default: + type: config_entity + label: 'User form display' + mapping: + id: + type: string + label: 'ID' + targetEntityType: + type: string + label: 'Target entity type' + bundle: + type: string + label: 'Bundle' + mode: + type: string + label: 'Mode' + content: + type: sequence + label: 'Content' + sequence: + type: mapping + label: 'Field settings' + mapping: + type: + type: string + label: 'Type' + weight: + type: integer + label: 'Weight' + region: + type: string + label: 'Region' + settings: + type: field.widget.settings.[%parent.type] + label: 'Settings' + third_party_settings: + type: sequence + label: 'Third party settings' + sequence: + type: mapping + label: 'Settings' + label: + type: string + label: 'Label' + hidden: + type: sequence + label: 'Hidden' + sequence: + type: boolean + label: 'Component hidden' + +field.widget.settings.string_textfield: + type: mapping + label: 'Text field settings' + mapping: + size: + type: integer + label: 'Size' + placeholder: + type: string + label: 'Placeholder' + +field.widget.settings.image_image: + type: mapping + label: 'Image field settings' + mapping: + progress_indicator: + type: string + label: 'Progress indicator' + preview_image_style: + type: string + label: 'Preview image style' + +config_pr.settings: + type: config_object + label: 'Configuration Pull Request settings' + mapping: + repo: + type: mapping + label: 'Repository settings' + mapping: + controller: + type: string + label: 'Repository controller' + repo_url: + type: string + label: 'Repository URL' + repo_owner: + type: string + label: 'Repository owner' + repo_name: + type: string + label: 'Repository name' + branch: + type: string + label: 'Branch name' + pr_title: + type: string + label: 'Pull request title' + pr_body: + type: string + label: 'Pull request body' + commit_messages: + type: mapping + label: 'Commit messages' + mapping: + create: + type: string + label: 'Create message' + update: + type: string + label: 'Update message' + rename: + type: string + label: 'Rename message' + delete: + type: string + label: 'Delete message' \ No newline at end of file diff --git a/config_pr.info.yml b/config_pr.info.yml index c4822265e9ddd0c22f2d5a8c70b9a5e4944358d1..9ec47c0718a0deb57cf6a540a63cd458f16cf364 100644 --- a/config_pr.info.yml +++ b/config_pr.info.yml @@ -6,3 +6,4 @@ core_version_requirement: ">=8" configure: config_pr.settings dependencies: - drupal:config + - drupal:field diff --git a/config_pr.services.yml b/config_pr.services.yml index 49640ae10cdc11520a364ce00ddb6d95c481fd62..61c8e777f022414b9d095cd64202e5dfb72b8653 100644 --- a/config_pr.services.yml +++ b/config_pr.services.yml @@ -1,6 +1,10 @@ services: config_pr.repo_controller_manager: class: Drupal\config_pr\RepoControllerManager - arguments: ['@messenger'] + arguments: ['@messenger', '@config.factory'] tags: - { name: service_collector, tag: config_pr.repo_controller, call: addController } + + config_pr.active_repo_controller: + class: Drupal\config_pr\RepoControllerInterface + factory: ['@config_pr.repo_controller_manager', 'getActiveController'] diff --git a/src/Form/ConfigPrForm.php b/src/Form/ConfigPrForm.php index 0cc27608914ce60b68c15ad5b343c2087efd776b..22668d4dd5e3f3732274ed8f983a10f59b33c609 100644 --- a/src/Form/ConfigPrForm.php +++ b/src/Form/ConfigPrForm.php @@ -11,7 +11,6 @@ use Drupal\Core\Config\StorageInterface; use Drupal\Core\Form\FormBase; use Drupal\Core\Form\FormStateInterface; use Drupal\Core\Link; -use Drupal\Core\Session\AccountInterface; use Drupal\Core\Site\Settings; use Drupal\Core\Url; use Drupal\user\Entity\User; @@ -26,8 +25,7 @@ class ConfigPrForm extends FormBase { /** * The repo controller. * - * @var \Drupal\config_pr\RepoControllerInterface - * The Repo controller interface. + * @var \Drupal\config_pr\RepoControllerInterface|null */ protected $repoController; @@ -70,18 +68,15 @@ class ConfigPrForm extends FormBase { * Configuration manager. * @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory * The factory for configuration objects. - * @param \Drupal\config_pr\RepoControllerInterface $repoController - * The repo controller. - * @param \Drupal\Core\Session\AccountInterface $current_user - * The current user. + * @param \Drupal\config_pr\RepoControllerInterface|null $repoController + * The repo controller, or NULL if not configured. */ public function __construct( StorageInterface $sync_storage, StorageInterface $active_storage, ConfigManagerInterface $config_manager, ConfigFactoryInterface $config_factory, - RepoControllerInterface $repoController, - AccountInterface $current_user, + ?RepoControllerInterface $repoController = NULL, ) { $this->syncStorage = $sync_storage; $this->activeStorage = $active_storage; @@ -94,17 +89,12 @@ class ConfigPrForm extends FormBase { * {@inheritdoc} */ public static function create(ContainerInterface $container) { - $repoController = $container->get('config.factory') - ->get('config_pr.settings') - ->get('repo.controller') ?? 'config_pr.repo_controller.github'; - return new static( $container->get('config.storage.sync'), $container->get('config.storage'), $container->get('config.manager'), $container->get('config.factory'), - $container->get($repoController), - $container->get('current_user') + $container->get('config_pr.active_repo_controller'), ); } @@ -143,6 +133,14 @@ class ConfigPrForm extends FormBase { * {@inheritdoc} */ public function buildForm(array $form, FormStateInterface $form_state) { + if (!$this->repoController) { + return [ + '#markup' => $this->t('Repository configuration is missing. Please visit the @settings_link first.', [ + '@settings_link' => Link::createFromRoute('configuration page', 'config_pr.settings')->toString(), + ]), + ]; + } + $repo_owner = $this->config('config_pr.settings')->get('repo.repo_owner'); $repo_name = $this->config('config_pr.settings')->get('repo.repo_name'); if (empty($repo_owner) || empty($repo_name)) { diff --git a/src/Form/ConfigPrSettingsForm.php b/src/Form/ConfigPrSettingsForm.php index 6e56a3cc5564ff222258a66956b74ac001033a37..3494d4ed968e3b083c10123cc23ad31f9399db1f 100644 --- a/src/Form/ConfigPrSettingsForm.php +++ b/src/Form/ConfigPrSettingsForm.php @@ -7,6 +7,7 @@ use Drupal\Core\Config\ConfigFactoryInterface; use Drupal\Core\Config\TypedConfigManagerInterface; use Drupal\Core\Form\ConfigFormBase; use Drupal\Core\Form\FormStateInterface; +use Drupal\Core\Link; use Symfony\Component\DependencyInjection\ContainerInterface; /** @@ -78,6 +79,15 @@ class ConfigPrSettingsForm extends ConfigFormBase { * The form. */ public function buildForm(array $form, FormStateInterface $form_state) { + $repo_controllers = $this->repoController->getControllers(); + if (!$repo_controllers) { + return [ + '#markup' => $this->t('No repo controllers available, please enable one of the sub-modules in the @settings_link.', [ + '@settings_link' => Link::createFromRoute('Modules page', 'system.modules_list')->toString(), + ]), + ]; + } + $form['repo'] = [ '#title' => $this->t('Repository'), '#type' => 'fieldset', @@ -86,14 +96,10 @@ class ConfigPrSettingsForm extends ConfigFormBase { '#type' => 'select', '#title' => $this->t('Repo provider'), '#description' => $this->t('Select controller.'), - '#options' => $this->repoController->getControllers(), + '#options' => $repo_controllers, '#default_value' => $this->config('config_pr.settings')->get('repo.controller') ?? 'config_pr.repo_controller.github', '#required' => TRUE, ]; - - // Try to get the information from the local repo. This only works with Git. - $repo_info = $this->repoController->getLocalRepoInfo(); - $form['repo']['repo_url'] = [ '#type' => 'textfield', '#title' => $this->t('Repository URL (Optional)'), @@ -101,6 +107,10 @@ class ConfigPrSettingsForm extends ConfigFormBase { '#default_value' => $this->config('config_pr.settings')->get('repo.repo_url') ?? '', '#required' => FALSE, ]; + + // Try to get the information from the local repo. This only works with Git. + $repo_info = $this->repoController->getLocalRepoInfo() ?? ['repo_owner' => '', 'repo_name' => '']; + $form['repo']['repo_owner'] = [ '#type' => 'textfield', '#title' => $this->t('Repo owner name'), diff --git a/src/RepoControllerFactory.php b/src/RepoControllerFactory.php new file mode 100644 index 0000000000000000000000000000000000000000..44f826398476bc8941d41ddfcf840cd17fc0569b --- /dev/null +++ b/src/RepoControllerFactory.php @@ -0,0 +1,60 @@ +<?php + +namespace Drupal\config_pr; + +use Drupal\Core\Config\ConfigFactoryInterface; +use Symfony\Component\DependencyInjection\ContainerInterface; + +/** + * Factory class for repository controllers. + */ +class RepoControllerFactory { + + /** + * The config factory. + * + * @var \Drupal\Core\Config\ConfigFactoryInterface + */ + protected $configFactory; + + /** + * The service container. + * + * @var \Symfony\Component\DependencyInjection\ContainerInterface + */ + protected $container; + + /** + * Constructs a new RepoControllerFactory. + * + * @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory + * The config factory. + * @param \Symfony\Component\DependencyInjection\ContainerInterface $container + * The service container. + */ + public function __construct( + ConfigFactoryInterface $config_factory, + ContainerInterface $container + ) { + $this->configFactory = $config_factory; + $this->container = $container; + } + + /** + * Gets the appropriate repository controller. + * + * @return \Drupal\config_pr\RepoControllerInterface + * The repository controller. + */ + public function getController(): RepoControllerInterface { + $controller_id = $this->configFactory->get('config_pr.settings')->get('repo.controller'); + + // Default to dummy controller if no configuration exists. + if (empty($controller_id)) { + $controller_id = 'config_pr.dummy_repo_controller'; + } + + return $this->container->get($controller_id); + } + +} diff --git a/src/RepoControllerManager.php b/src/RepoControllerManager.php index 997933d31de103c6a357220380c2734c91723a16..1b5155733c10754b489666b23eb7098eda43a3e3 100644 --- a/src/RepoControllerManager.php +++ b/src/RepoControllerManager.php @@ -2,6 +2,9 @@ namespace Drupal\config_pr; +use Drupal\Core\Config\ConfigFactoryInterface; +use Drupal\Core\Messenger\MessengerInterface; + /** * Class RepoControllerManager. * @@ -17,26 +20,69 @@ class RepoControllerManager implements RepoControllerManagerInterface { * * @var array */ - protected $controllers = []; + protected array $controllers = []; + + /** + * The config factory. + * + * @var \Drupal\Core\Config\ConfigFactoryInterface + */ + protected $configFactory; + + /** + * The messenger. + * + * @var \Drupal\Core\Messenger\MessengerInterface + */ + protected $messenger; + + /** + * Constructs a new RepoControllerManager. + * + * @param \Drupal\Core\Messenger\MessengerInterface $messenger + * The messenger. + * @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory + * The config factory. + */ + public function __construct( + MessengerInterface $messenger, + ConfigFactoryInterface $config_factory + ) { + $this->messenger = $messenger; + $this->configFactory = $config_factory; + } /** * {@inheritdoc} */ public function addController(RepoControllerInterface $controller) { - $this->controllers[] = $controller; + $id = $controller->getControllerId(); + $this->controllers[$id] = $controller; } /** * {@inheritdoc} */ - public function getControllers() { + public function getControllers(): array { + $controllers = []; foreach ($this->controllers as $controller) { - $controllers[$controller->getControllerId()] = $controller->getControllerName(); + $id = $controller->getControllerId(); + $controllers[$id] = $controller->getControllerName(); } - return $controllers; } + /** + * Gets the active controller if configured. + * + * @return \Drupal\config_pr\RepoControllerInterface|null + * The active controller, or NULL if not configured. + */ + public function getActiveController(): ?RepoControllerInterface { + $id = $this->configFactory->get('config_pr.settings')->get('repo.controller'); + return $id ? ($this->controllers[$id] ?? NULL) : NULL; + } + /** * {@inheritdoc} */ diff --git a/src/RepoControllers/DummyController.php b/src/RepoControllers/DummyController.php new file mode 100644 index 0000000000000000000000000000000000000000..bc4035acf942be084da6bc1eaf0f427f09926cce --- /dev/null +++ b/src/RepoControllers/DummyController.php @@ -0,0 +1,92 @@ +<?php + +namespace Drupal\config_pr\RepoControllers; + +use Drupal\config_pr\RepoControllerInterface; +use Drupal\config_pr\RepoControllerTrait; +use Drupal\Core\Messenger\MessengerTrait; +use Drupal\Core\StringTranslation\StringTranslationTrait; + +/** + * Class to define a dummy controller. + * + * @see \Drupal\config_pr\RepoControllerInterface + */ +class DummyController implements RepoControllerInterface { + + use MessengerTrait; + use RepoControllerTrait; + use StringTranslationTrait; + + /** + * Holds the controller name. + * + * @var string + * The controller name. + */ + protected $controllerName = 'Dummy'; + + /** + * Holds the controller Id. + * + * @var string + * The controller id. + */ + protected $controllerId = 'config_pr.dummy_repo_controller'; + + /** + * {@inheritdoc} + */ + public function authenticate(): bool {} + + /** + * @inheritDoc + */ + public function getProjectDetails(): bool {} + + /** + * @inheritDoc + */ + public function getBranches(): array {} + + /** + * @inheritDoc + */ + public function createBranch($branch_name): bool {} + + /** + * {@inheritdoc} + */ + public function getOpenPrs(): array {} + + /** + * {@inheritdoc} + */ + public function createPr($base, $branch, $title, $body): array|bool {} + + /** + * @inheritDoc + */ + public function getFileSha($path): string {} + + /** + * {@inheritdoc} + */ + public function createFile($path, $content, $commitMessage, $branchName): array|bool {} + + /** + * {@inheritdoc} + */ + public function fileExists($path): bool {} + + /** + * {@inheritdoc} + */ + public function updateFile($path, $content, $commitMessage, $branchName): array|bool {} + + /** + * {@inheritdoc} + */ + public function deleteFile($path, $commitMessage, $branchName): bool {} + +} diff --git a/tests/src/Functional/ConfigPrTest.php b/tests/src/Functional/ConfigPrTest.php new file mode 100644 index 0000000000000000000000000000000000000000..11c516853d67bdfa255fe45843fc1b0daa4038b1 --- /dev/null +++ b/tests/src/Functional/ConfigPrTest.php @@ -0,0 +1,161 @@ +<?php + +namespace Drupal\Tests\config_pr\Functional; + +use Drupal\Tests\BrowserTestBase; +use Drupal\Tests\config_pr\Functional\Utils; + +/** + * Tests the Configuration Pull Request module functionality. + * + * @group config_pr + */ +class ConfigPrTest extends BrowserTestBase { + + /** + * {@inheritdoc} + */ + protected static $modules = [ + 'config_pr', + 'config_pr_github', + 'config_pr_gitlab', + ]; + + /** + * {@inheritdoc} + */ + protected $defaultTheme = 'stark'; + + /** + * {@inheritdoc} + */ + protected $profile = 'standard'; + + /** + * A test user with administrative privileges. + * + * @var \Drupal\user\UserInterface + */ + protected $adminUser; + + /** + * Stores the repo owner. + * + * @var string + */ + protected $repo_owner; + + /** + * Stores the repo user. + * + * @var string + */ + protected $repo_name; + + /** + * {@inheritdoc} + */ + protected function setUp(): void { + parent::setUp(); + + $this->repo_owner = getenv('REPO_OWNER'); + $this->repo_name = getenv('REPO_NAME'); + + // Create and log in our privileged user. + $this->adminUser = $this->drupalCreateUser([ + 'administer configuration pull request', + 'issue configuration pull requests', + 'synchronize configuration', + ]); + $this->drupalLogin($this->adminUser); + + // Export initial config. + $configs_to_export = [ + 'system.site', + 'system.theme', + // Add other configurations you want to export + ]; + foreach ($configs_to_export as $config_name) { + Utils::exportConfig($config_name); + } + + // Get the config factory service. + $config_factory = \Drupal::service('config.factory'); + // Load the 'system.site' configuration. + $config = $config_factory->getEditable('system.site'); + // Update the site name to 'foo'. + $config->set('name', 'Test Site'); + // Save the configuration. + $config->save(); + } + + public function testConfigPrGithub(): void { + $this->runTests('github'); + } + + public function testConfigPrGitlab(): void { + $this->runTests('gitlab'); + } + + /** + * Tests the config pull request workflow. + */ + private function runTests(string $type): void { + $repo_url = 'https://' . $type . '.com/' . $this->repo_owner . '/' . $this->repo_name . '.git'; + + // Configure access token in user profile. + $this->drupalGet('user/' . $this->adminUser->id() . '/edit'); + $this->assertSession()->statusCodeEquals(200); + + $edit = [ + 'field_config_pr_auth_token[0][value]' => getenv(strtoupper($type) . '_TOKEN'), + ]; + $this->submitForm($edit, 'Save'); + $this->assertSession()->pageTextContains('The changes have been saved'); + + // Configure the repository settings. + $this->drupalGet('admin/config/development/configuration/pull_request'); + $this->assertSession()->statusCodeEquals(200); + $this->assertSession()->pageTextContains('Repository configuration is missing'); + $this->clickLink('configuration page'); + + $edit = [ + 'repo_controller' => 'config_pr_' . $type . '.repo_controller.' . $type, + 'repo_url' => $repo_url, + 'repo_owner' => $this->repo_owner, + 'repo_name' => $this->repo_name, + 'message_create' => 'New config @config_name.yml', + 'message_delete' => 'Del config @config_name.yml', + ]; + $this->submitForm($edit, 'Save configuration'); + $this->assertSession()->pageTextContains('The configuration options have been saved'); + + // Verify the configuration is saved correctly. + $this->assertSession()->fieldValueEquals('repo_controller', 'config_pr_' . $type . '.repo_controller.' . $type); + $this->assertSession()->fieldValueEquals('repo_url', $repo_url); + $this->assertSession()->fieldValueEquals('repo_owner', $this->repo_owner); + $this->assertSession()->fieldValueEquals('repo_name', $this->repo_name); + $this->assertSession()->fieldValueEquals('message_create', 'New config @config_name.yml'); + $this->assertSession()->fieldValueEquals('message_delete', 'Del config @config_name.yml'); + + // Navigate to Config pull request form. + $this->clickLink('Pull Requests page'); + + $this->assertSession()->pageTextContains('1 changed'); + $this->assertSession()->linkExists('View differences'); + $this->assertSession()->optionExists('source_branch', 'master'); + + // Select config. + $page = $this->getSession()->getPage(); + $page->find('css', '[name="select-system_site"]')->check(); + $this->assertSession()->checkboxChecked('select-system_site'); + + // Create pull request. + $edit = [ + 'pr_title' => 'Test PR', + 'pr_description' => 'Automated test PR', + ]; + $this->submitForm($edit, 'Create Pull Request'); + } + +} diff --git a/tests/src/Functional/Utils.php b/tests/src/Functional/Utils.php new file mode 100644 index 0000000000000000000000000000000000000000..0a8b516eb5c352b65edffe6c759e2331a1d816ac --- /dev/null +++ b/tests/src/Functional/Utils.php @@ -0,0 +1,41 @@ +<?php + +namespace Drupal\Tests\config_pr\Functional; + +use Drupal\Core\Config\FileStorage; +use Drupal\Core\Site\Settings; + +/** + * Helper class for Config PR tests. + */ +class Utils { + + /** + * Exports configuration to a specific directory. + * + * @param string $config_name + * The configuration name to export (e.g., 'system.site'). + */ + public static function exportConfig($config_name) { + // Set up the sync directory + $config_sync_directory = Settings::get('config_sync_directory', NULL); + + // Ensure the directory exists + if (!file_exists($config_sync_directory)) { + mkdir($config_sync_directory, 0777, TRUE); + } + + // Get the configuration from active storage + $config = \Drupal::config($config_name); + if (!$config) { + throw new \Exception("Configuration '$config_name' not found."); + } + + // Create a file storage for the target directory + $storage = new FileStorage($config_sync_directory); + + // Write the configuration to file + $storage->write($config_name, $config->get()); + } + +}