diff --git a/src/Common/IO.php b/src/Common/IO.php new file mode 100644 index 0000000..074b26c --- /dev/null +++ b/src/Common/IO.php @@ -0,0 +1,135 @@ +writeln($text); + } + + /** + * Writes text to screen with big, loud decoration. + * + * @param string $text + * The text to write. + * @param int $length + * The length at which text should be wrapped. + * @param string $color + * The color of the text. + */ + protected function yell($text, $length = 40, $color = 'green') { + $format = "%s"; + $this->formattedOutput($text, $length, $format); + } + + /** + * Format text as a question. + * + * @param string $message + * The question text. + * + * @return string + * The formatted question text. + */ + protected function formatQuestion($message) { + return " $message "; + } + + /** + * Asks the user a multiple-choice question. + * + * @param string $question + * The question text. + * @param array $options + * An array of available options. + * @param mixed $default + * Default. + * + * @return string + * The chosen option. + */ + protected function askChoice($question, array $options, $default = NULL) { + return $this->doAsk(new ChoiceQuestion($this->formatQuestion($question), + $options, $default)); + } + + /** + * Asks a required question. + * + * @param string $message + * The question text. + * + * @return string + * The response. + */ + protected function askRequired($message) { + $question = new Question($this->formatQuestion($message)); + $question->setValidator(function ($answer) { + if (empty($answer)) { + throw new \RuntimeException( + 'You must enter a value!' + ); + } + + return $answer; + }); + return $this->doAsk($question); + } + + /** + * Writes an array to the screen as a formatted table. + * + * @param array $array + * The unformatted array. + * @param array $headers + * The headers for the array. Defaults to ['Property','Value']. + */ + protected function printArrayAsTable( + array $array, + array $headers = ['Property', 'Value'] + ) { + $table = new Table($this->output); + $table->setHeaders($headers) + ->setRows(ArrayManipulator::convertArrayToFlatTextArray($array)) + ->render(); + } + + /** + * Writes a particular configuration key's value to the log. + * + * @param array $array + * The configuration. + * @param string $prefix + * A prefix to add to each row in the configuration. + * @param int $verbosity + * The verbosity level at which to display the logged message. + */ + protected function logConfig(array $array, $prefix = '', $verbosity = OutputInterface::VERBOSITY_VERY_VERBOSE) { + if ($this->output()->getVerbosity() >= $verbosity) { + if ($prefix) { + $this->output()->writeln("Configuration for $prefix:"); + foreach ($array as $key => $value) { + $array["$prefix.$key"] = $value; + unset($array[$key]); + } + } + $this->printArrayAsTable($array); + } + } + +} diff --git a/src/Config/ConfigInitializer.php b/src/Config/ConfigInitializer.php index f38a115..1e9aaaf 100644 --- a/src/Config/ConfigInitializer.php +++ b/src/Config/ConfigInitializer.php @@ -4,6 +4,7 @@ use Acquia\Drupal\RecommendedSettings\Helpers\EnvironmentDetector; use Consolidation\Config\Config; +use Consolidation\Config\ConfigInterface; use Consolidation\Config\Loader\YamlConfigLoader; /** @@ -11,7 +12,7 @@ */ class ConfigInitializer { - const DEFAULT_CONFIG_FILE_PATH = "/config/build.yml"; + const CONFIG_FILE_PATH = "/config/build.yml"; /** * Config. @@ -39,25 +40,13 @@ class ConfigInitializer { * @param string $site * Drupal site uri. Ex: site1, site2 etc. */ - public function __construct(string $site = "default") { - $this->config = new Config(); + public function __construct(ConfigInterface $config) { + $this->config = $config; $this->loader = new YamlConfigLoader(); $this->processor = new YamlConfigProcessor(); - $this->setSite($site); $this->initialize(); } - /** - * Set site. - * - * @param string $site - * Given Site. - */ - public function setSite(string $site): void { - $this->site = $site; - $this->config->set('site', $site); - } - /** * Initialize. */ @@ -72,6 +61,7 @@ public function initialize(): ConfigInitializer { */ public function loadAllConfig(): ConfigInitializer { $this->loadDefaultConfig(); + $this->loadProjectConfig(); return $this; } @@ -81,7 +71,18 @@ public function loadAllConfig(): ConfigInitializer { protected function loadDefaultConfig(): ConfigInitializer { $this->addConfig($this->config->export()); $drsDirectory = dirname(__FILE__, 3); - $this->processor->extend($this->loader->load($drsDirectory . self::DEFAULT_CONFIG_FILE_PATH)); + $this->processor->extend($this->loader->load($drsDirectory . self::CONFIG_FILE_PATH)); + return $this; + } + + /** + * Load config. + * + * @return $this + * Config. + */ + public function loadProjectConfig(): ConfigInitializer { + $this->processor->extend($this->loader->load($this->config->get('repo.root') . self::CONFIG_FILE_PATH)); return $this; } @@ -102,6 +103,9 @@ public function addConfig(array $data): Config { * @throws \ReflectionException */ public function determineEnvironment(): string { + if ($this->config->get('environment')) { + return $this->config->get('environment'); + } if (EnvironmentDetector::isCiEnv()) { return 'ci'; } diff --git a/src/Config/DefaultConfig.php b/src/Config/DefaultConfig.php new file mode 100644 index 0000000..14b838d --- /dev/null +++ b/src/Config/DefaultConfig.php @@ -0,0 +1,30 @@ +set('repo.root', $repo_root); + $this->set('docroot', $drupal_root); + $this->set('composer.bin', $repo_root . '/vendor/bin'); + $this->set('site', 'default'); + $this->set('tmp.dir', sys_get_temp_dir()); + } + +} diff --git a/src/Config/DefaultDrushConfig.php b/src/Config/DefaultDrushConfig.php new file mode 100644 index 0000000..d4e4db0 --- /dev/null +++ b/src/Config/DefaultDrushConfig.php @@ -0,0 +1,36 @@ +get("options.uri") ?? "default"; + $config->set('repo.root', $config->get("runtime.project")); + $config->set('docroot', $config->get("options.root")); + $config->set('composer.bin', $config->get("drush.vendor-dir") . '/bin'); + $config->set('drush.uri', $config->get("options.uri")); + $config->set('site', $config->get("options.uri")); + if ($config->get("options.ansi")) { + $config->set('drush.ansi', $config->get("options.ansi")); + } + $config->set('drush.bin', $config->get("runtime.drush-script")); + $config->setDefault('drush.alias', "self"); + parent::__construct($config->export()); + } + +} diff --git a/src/Drush/Commands/BaseDrushCommands.php b/src/Drush/Commands/BaseDrushCommands.php index 35a58f0..3efb29f 100644 --- a/src/Drush/Commands/BaseDrushCommands.php +++ b/src/Drush/Commands/BaseDrushCommands.php @@ -4,8 +4,12 @@ namespace Acquia\Drupal\RecommendedSettings\Drush\Commands; +use Acquia\Drupal\RecommendedSettings\Common\IO; +use Acquia\Drupal\RecommendedSettings\Config\ConfigInitializer; +use Acquia\Drupal\RecommendedSettings\Config\DefaultDrushConfig; use Acquia\Drupal\RecommendedSettings\Exceptions\SettingsException; use Acquia\Drupal\RecommendedSettings\Robo\Config\ConfigAwareTrait; +use Acquia\Drupal\RecommendedSettings\Robo\Tasks\DrushTask; use Acquia\Drupal\RecommendedSettings\Robo\Tasks\LoadTasks; use Consolidation\AnnotatedCommand\AnnotationData; use Consolidation\AnnotatedCommand\Hooks\HookManager; @@ -20,6 +24,7 @@ use Robo\Contract\IOAwareInterface; use Robo\LoadAllTasks; use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Process\Process; /** @@ -31,19 +36,17 @@ class BaseDrushCommands extends DrushCommands implements ConfigAwareInterface, L use LoadAllTasks; use ConfigAwareTrait; use LoadTasks; + use IO; /** * {@inheritdoc} */ #[CLI\Hook(type: HookManager::INITIALIZE)] public function init(InputInterface $input, AnnotationData $annotationData): void { - if ($this->getConfig()->get("options.uri")) { - $this->getConfig()->setDefault('drush.uri', $this->getConfig()->get("options.uri")); - } - if ($this->getConfig()->get("options.ansi")) { - $this->getConfig()->setDefault('drush.ansi', $this->getConfig()->get("options.ansi")); - } - $this->getConfig()->setDefault('drush.bin', $this->getConfig()->get("runtime.drush-script")); + $config = new DefaultDrushConfig($this->getConfig()); + $configInitializer = new ConfigInitializer($config); + $config = $configInitializer->loadAllConfig()->processConfig(); + $this->setConfig($config); } /** @@ -72,7 +75,7 @@ protected function invokeCommands(array $commands) { * Invokes a single Drush command. * * @param string $command_name - * The name of the command, e.g., 'tests:behat:run'. + * The name of the command, e.g., 'status'. * @param array $args * An array of arguments to pass to the command. * @@ -80,25 +83,24 @@ protected function invokeCommands(array $commands) { */ protected function invokeCommand(string $command_name, array $args = []): void { $options = $this->input()->getOptions(); + $drushOptions = []; foreach ($options as $key => $value) { - if ($value === NULL || $key === "define") { + if ($value === NULL || $key === "define" || $value == FALSE) { unset($options[$key]); } } $process = Drush::drush(Drush::aliasManager()->getSelf(), $command_name, $args, $options); - $this->output->writeln(" > " . $process->getCommandLine() . ""); - $process->run($process->showRealtime()->hideStderr()); - $errorOutput = $process->getErrorOutput(); - if ($errorOutput) { - if (!$process->isSuccessful()) { - $errorOutput = preg_replace("/^\s+|\s+$/", "", $errorOutput); - throw new SettingsException($errorOutput); + $this->output->writeln(" > " . $command_name. ""); + $output = $this->output(); + $process->setTty(Process::isTtySupported()); + $process->run(static function ($type, $buffer) use ($output, $process) { + if (Process::ERR === $type) { + $output->getErrorOutput()->write($buffer, false, OutputInterface::OUTPUT_NORMAL); } else { - $errorOutput = str_replace("[success]", "[success]", $errorOutput); - $this->io()->write($errorOutput); + $output->write($buffer, false, OutputInterface::OUTPUT_NORMAL); } - } + }); } } diff --git a/src/Settings.php b/src/Settings.php index 73d14fd..8becaea 100644 --- a/src/Settings.php +++ b/src/Settings.php @@ -3,9 +3,11 @@ namespace Acquia\Drupal\RecommendedSettings; use Acquia\Drupal\RecommendedSettings\Config\ConfigInitializer; +use Acquia\Drupal\RecommendedSettings\Config\DefaultConfig; use Acquia\Drupal\RecommendedSettings\Config\SettingsConfig; use Acquia\Drupal\RecommendedSettings\Exceptions\SettingsException; use Acquia\Drupal\RecommendedSettings\Helpers\Filesystem; +use Consolidation\Config\Config; /** * Core class of the plugin. @@ -53,10 +55,10 @@ class Settings { /** * Constructs the plugin object. */ - public function __construct(string $drupalRoot, string $site = "default") { + public function __construct(string $drupal_root, string $site = "default") { + $this->config = new DefaultConfig($drupal_root); $this->fileSystem = new Filesystem(); - $this->drupalRoot = $drupalRoot; - $this->site = $site; + $this->config->set("site", $site); } /** @@ -72,7 +74,7 @@ public static function getPluginPath(): string { protected function copyGlobalSettings(): bool { return $this->fileSystem->copyFiles( self::getPluginPath() . "/settings/global", - $this->drupalRoot . "/sites/settings" + $this->config->get("docroot") . "/sites/settings" ); } @@ -82,7 +84,7 @@ protected function copyGlobalSettings(): bool { protected function copySiteSettings(): bool { return $this->fileSystem->copyFiles( self::getPluginPath() . "/settings/site", - $this->drupalRoot . "/sites/" . $this->site . "/settings" + $this->config->get("docroot") . "/sites/" . $this->config->get("site") . "/settings" ); } @@ -96,45 +98,48 @@ protected function copySiteSettings(): bool { */ public function generate(array $overrideData = []): void { try { - $site = $this->site; + // Replace variables in local.settings.php file. + $config = new ConfigInitializer($this->config); + $config = $config->loadAllConfig(); + if ($overrideData) { + $config->addConfig($overrideData); + } + $config = $config->processConfig(); + + $site = $config->get("site"); + $docroot = $config->get("docroot"); $this->copyGlobalSettings(); $this->copySiteSettings(); // Create settings.php file from default.settings.php. $this->fileSystem->copyFile( - $this->drupalRoot . "/sites/default/default.settings.php", - $this->drupalRoot . "/sites/$site/settings.php" + $docroot . "/sites/default/default.settings.php", + $docroot . "/sites/$site/settings.php" ); // Append `require acquia-recommended.settings.php` code block in // site specific settings.php file (if it does not exist). $this->appendIfMatchesCollect( - $this->drupalRoot . "/sites/$site/settings.php", + $docroot . "/sites/$site/settings.php", '#vendor/acquia/drupal-recommended-settings/settings/acquia-recommended.settings.php#', PHP_EOL . 'require DRUPAL_ROOT . "/../vendor/acquia/drupal-recommended-settings/settings/acquia-recommended.settings.php";' . PHP_EOL ); $this->appendIfMatchesCollect( - $this->drupalRoot . "/sites/$site/settings.php", + $docroot . "/sites/$site/settings.php", '#Do not include additional settings here#', $this->settingsWarning . PHP_EOL ); // Create local.settings.php file from default.local.settings.php. $this->fileSystem->copyFile( - $this->drupalRoot . "/sites/$site/settings/default.local.settings.php", - $this->drupalRoot . "/sites/$site/settings/local.settings.php" + $docroot . "/sites/$site/settings/default.local.settings.php", + $docroot . "/sites/$site/settings/local.settings.php" ); - // Replace variables in local.settings.php file. - $config = new ConfigInitializer(); - $config = $config->loadAllConfig(); - if ($overrideData) { - $config->addConfig($overrideData); - } - $settings = new SettingsConfig($config->processConfig()->export()); - $settings->replaceFileVariables($this->drupalRoot . "/sites/$site/settings/local.settings.php"); + $settings = new SettingsConfig($config->export()); + $settings->replaceFileVariables($docroot . "/sites/$site/settings/local.settings.php"); // The config directory for given site must exists, otherwise Drupal will // add database credentials to settings.php. - $this->fileSystem->ensureDirectoryExists($this->drupalRoot . "/../config/$site"); + $this->fileSystem->ensureDirectoryExists($docroot . "/../config/$site"); } catch (\Exception $e) { throw new SettingsException($e);