Verified Commit 74325fd8 authored by Alex Pott's avatar Alex Pott
Browse files

Issue #3427558 by srishtiiee, alexpott, phenaproxima, thejimbirch: Output...

Issue #3427558 by srishtiiee, alexpott, phenaproxima, thejimbirch: Output information from the recipe application process

(cherry picked from commit 300b8e0fb982675b53f635715c4c06b9d96747be)
parent 067d4a41
Loading
Loading
Loading
Loading
+53 −2
Original line number Diff line number Diff line
@@ -4,6 +4,7 @@

namespace Drupal\Core\Recipe;

use Drupal\Component\Render\PlainTextOutput;
use Drupal\Core\Config\Checkpoint\Checkpoint;
use Drupal\Core\Config\ConfigImporter;
use Drupal\Core\Config\ConfigImporterException;
@@ -83,8 +84,45 @@ protected function execute(InputInterface $input, OutputInterface $output): int
    $backup_checkpoint = $checkpoint_storage
      ->checkpoint("Backup before the '$recipe->name' recipe.");
    try {
      RecipeRunner::processRecipe($recipe);
      $io->success(sprintf('%s applied successfully', $recipe->name));
      $steps = RecipeRunner::toBatchOperations($recipe);
      $progress_bar = $io->createProgressBar();
      $progress_bar->setFormat("%current%/%max% [%bar%]\n%message%\n");
      $progress_bar->setMessage($this->toPlainString(t('Applying recipe')));
      $progress_bar->start(count($steps));

      /** @var array{message?: \Stringable|string, results: array{module?: string[], theme?: string[], content?: string[], recipe?: string[]}} $context */
      $context = ['results' => []];
      foreach ($steps as $step) {
        call_user_func_array($step[0], array_merge($step[1], [&$context]));
        if (isset($context['message'])) {
          $progress_bar->setMessage($this->toPlainString($context['message']));
        }
        unset($context['message']);
        $progress_bar->advance();
      }
      if ($io->isVerbose()) {
        if (!empty($context['results']['module'])) {
          $io->section($this->toPlainString(t('Modules installed')));
          $modules = array_map(fn ($module) => \Drupal::service('extension.list.module')->getName($module), $context['results']['module']);
          sort($modules, SORT_NATURAL);
          $io->listing($modules);
        }
        if (!empty($context['results']['theme'])) {
          $io->section($this->toPlainString(t('Themes installed')));
          $themes = array_map(fn ($theme) => \Drupal::service('extension.list.theme')->getName($theme), $context['results']['theme']);
          sort($themes, SORT_NATURAL);
          $io->listing($themes);
        }
        if (!empty($context['results']['content'])) {
          $io->section($this->toPlainString(t('Content created for recipes')));
          $io->listing($context['results']['content']);
        }
        if (!empty($context['results']['recipe'])) {
          $io->section($this->toPlainString(t('Recipes applied')));
          $io->listing($context['results']['recipe']);
        }
      }
      $io->success($this->toPlainString(t('%recipe applied successfully', ['%recipe' => $recipe->name])));
      return 0;
    }
    catch (\Throwable $e) {
@@ -98,6 +136,19 @@ protected function execute(InputInterface $input, OutputInterface $output): int
    }
  }

  /**
   * Converts a stringable like TranslatableMarkup to a plain text string.
   *
   * @param \Stringable|string $text
   *   The string to convert.
   *
   * @return string
   *   The plain text string.
   */
  private function toPlainString(\Stringable|string $text): string {
    return PlainTextOutput::renderFromHtml((string) $text);
  }

  /**
   * Rolls config back to a particular checkpoint.
   *
+5 −0
Original line number Diff line number Diff line
@@ -51,6 +51,7 @@ public static function triggerEvent(Recipe $recipe, ?array &$context = NULL): vo
    $event = new RecipeAppliedEvent($recipe);
    \Drupal::service(EventDispatcherInterface::class)->dispatch($event);
    $context['message'] = t('Applied %recipe recipe.', ['%recipe' => $recipe->name]);
    $context['results']['recipe'][] = $recipe->name;
  }

  /**
@@ -248,6 +249,7 @@ public static function installModule(string $module, StorageInterface|Recipe $re
    \Drupal::service('module_installer')->install([$module]);
    \Drupal::service('config.installer')->setSyncing(FALSE);
    $context['message'] = t('Installed %module module.', ['%module' => \Drupal::service('extension.list.module')->getName($module)]);
    $context['results']['module'][] = $module;
  }

  /**
@@ -277,6 +279,7 @@ public static function installTheme(string $theme, StorageInterface|Recipe $reci
    \Drupal::service('theme_installer')->install([$theme]);
    \Drupal::service('config.installer')->setSyncing(FALSE);
    $context['message'] = t('Installed %theme theme.', ['%theme' => \Drupal::service('extension.list.theme')->getName($theme)]);
    $context['results']['theme'][] = $theme;
  }

  /**
@@ -290,6 +293,7 @@ public static function installTheme(string $theme, StorageInterface|Recipe $reci
  public static function installConfig(Recipe $recipe, ?array &$context = NULL): void {
    static::processConfiguration($recipe->config);
    $context['message'] = t('Installed configuration for %recipe recipe.', ['%recipe' => $recipe->name]);
    $context['results']['config'][] = $recipe->name;
  }

  /**
@@ -303,6 +307,7 @@ public static function installConfig(Recipe $recipe, ?array &$context = NULL): v
  public static function installContent(Recipe $recipe, ?array &$context = NULL): void {
    static::processContent($recipe->content);
    $context['message'] = t('Created content for %recipe recipe.', ['%recipe' => $recipe->name]);
    $context['results']['content'][] = $recipe->name;
  }

}
+3 −3
Original line number Diff line number Diff line
@@ -37,7 +37,7 @@ public function testRecipeCommand(): void {

    $process = $this->applyRecipe('core/tests/fixtures/recipes/install_node_with_config');
    $this->assertSame(0, $process->getExitCode());
    $this->assertSame('', $process->getErrorOutput());
    $this->assertStringContainsString("6/6 [▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓]\nApplied Install node with config recipe.", $process->getErrorOutput());
    $this->assertStringContainsString('Install node with config applied successfully', $process->getOutput());
    $this->assertTrue(\Drupal::moduleHandler()->moduleExists('node'), 'The node module is installed');
    $this->assertCheckpointsExist(["Backup before the 'Install node with config' recipe."]);
@@ -45,7 +45,7 @@ public function testRecipeCommand(): void {
    // Ensure recipes can be applied without affecting pre-existing checkpoints.
    $process = $this->applyRecipe('core/tests/fixtures/recipes/install_two_modules');
    $this->assertSame(0, $process->getExitCode());
    $this->assertSame('', $process->getErrorOutput());
    $this->assertStringContainsString("1/1 [▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓]\nApplied Install two modules recipe.", $process->getErrorOutput());
    $this->assertStringContainsString('Install two modules applied successfully', $process->getOutput());
    $this->assertTrue(\Drupal::moduleHandler()->moduleExists('node'), 'The node module is installed');
    $this->assertCheckpointsExist([
@@ -68,7 +68,7 @@ public function testRecipeCommand(): void {
    \Drupal::service('config.storage.checkpoint')->checkpoint('Test log message');
    $process = $this->applyRecipe('core/tests/fixtures/recipes/no_extensions');
    $this->assertSame(0, $process->getExitCode());
    $this->assertSame('', $process->getErrorOutput());
    $this->assertStringContainsString("1/1 [▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓]\nApplied No extensions recipe.", $process->getErrorOutput());
    $this->assertCheckpointsExist([
      "Backup before the 'Install node with config' recipe.",
      "Backup before the 'Install two modules' recipe.",
+1 −1
Original line number Diff line number Diff line
@@ -242,7 +242,7 @@ parameters:

		-
			message: "#^\\\\Drupal calls should be avoided in classes, use dependency injection instead$#"
			count: 1
			count: 3
			path: core/lib/Drupal/Core/Recipe/RecipeCommand.php

		-