devel_wizard-2.x-dev/src/Spell/SpellTraitPackageManager.php

src/Spell/SpellTraitPackageManager.php
<?php

declare(strict_types=1);

namespace Drupal\devel_wizard\Spell;

use Composer\InstalledVersions as ComposerInstalledVersions;
use Drupal\Core\Config\ConfigFactoryInterface;
use Drupal\Core\Extension\Exception\UnknownExtensionException;
use Drupal\Core\Extension\ModuleExtensionList;
use Drupal\Core\Extension\ModuleInstallerInterface;
use Drupal\Core\Messenger\MessengerInterface;
use Drupal\devel_wizard\ShellProcessFactoryInterface;
use Symfony\Component\Filesystem\Path;

trait SpellTraitPackageManager {

  protected ConfigFactoryInterface $configFactory;

  protected ShellProcessFactoryInterface $shellProcessFactory;

  protected ModuleInstallerInterface $moduleInstaller;

  protected ModuleExtensionList $moduleList;

  /**
   * @return \Drupal\devel_wizard\Spell\SpellDefinition
   */
  abstract public function getPluginDefinition();

  /**
   * @param string $mode
   *   Allowed values: "prod", "dev".
   * @param array<string, string> $packages
   *   Key: package name.
   *   Value: version constraint.
   */
  protected function installComposerPackages(string $mode, array $packages): array {
    $packages = array_filter(
      $packages,
      function ($versionConstraint): bool {
        return $versionConstraint !== NULL;
      },
    );

    $missingPackages = array_diff_key(
      $packages,
      array_flip(ComposerInstalledVersions::getInstalledPackages()),
    );

    if (!$missingPackages) {
      return [];
    }

    $settings = $this->configFactory->get('devel_wizard.settings');

    $phpBinDir = $settings->get('php.bin_dir');
    $phpExecutable = $settings->get('php.php_executable') ?: 'php';
    if ($phpBinDir) {
      $phpExecutable = Path::join($phpBinDir, $phpExecutable);
    }

    $command = [
      $phpExecutable,
      $settings->get('composer.composer_executable'),
      'require',
    ];

    if ($mode === 'dev') {
      $command[] = '--dev';
    }

    foreach ($missingPackages as $package => $version) {
      $command[] = rtrim("$package:$version", ':');
    }

    // @todo Detect project root.
    $projectRoot = Path::makeAbsolute('..', getcwd());
    $envVars = getenv();
    // @todo Better ${PATH} handling.
    // Without proper PATH Composer is not able to find the Git executable.
    // Without Git executable composer is not able to manage packages correctly.
    $envVars['PATH'] = explode(\PATH_SEPARATOR, $envVars['PATH'] ?? '');
    $envVars['PATH'] = implode(
      \PATH_SEPARATOR,
      array_unique(array_filter(array_merge(
        [
          "{$envVars['HOME']}/bin",
          $settings->get('php.bin_dir'),
          '/usr/bin',
          '/bin',
        ],
        $envVars['PATH'],
      ))),
    );
    $process = $this->shellProcessFactory->createInstance($command, $projectRoot, $envVars);
    $process->run();

    $args = [
      '@spell' => $this->getPluginDefinition()->id(),
      '@projectRoot' => $projectRoot,
      '@cwd' => getcwd(),
      '@wd' => $process->getWorkingDirectory(),
      '@command' => $process->getCommandLine(),
      '@exitCode' => $process->getExitCode(),
      '@stdOutput' => $process->getOutput(),
      '@stdError' => $process->getErrorOutput(),
    ];

    if ($process->getExitCode()) {
      throw new \RuntimeException(
        strtr(
          '@spell - shell command failed:<hr />project root: @projectRoot<hr />cwd: @cwd<hr />wd: @wd<hr />command: @command<hr />exit code: @exitCode<hr />stdOutput: <pre>@stdOutput</pre><hr />stdError: <pre>@stdError</pre>',
          $args,
        ),
      );
    }

    return $missingPackages;
  }

  protected function ensureModuleInstalled(string $moduleName): static {
    $installedModules = $this->moduleList->getAllInstalledInfo();
    if (isset($installedModules[$moduleName])) {
      $args = [
        '@spell' => $this->getPluginDefinition()->id(),
        '%module' => $moduleName,
      ];
      $this->batchContext['sandbox']['messages'][] = [
        'type' => MessengerInterface::TYPE_STATUS,
        'message' => $this->t('@spell - module %module already installed', $args),
      ];

      return $this;
    }

    $this->installModules([$moduleName => TRUE]);

    return $this;
  }

  protected function installModules(array $moduleNames): bool {
    $moduleNames = array_keys($moduleNames, TRUE, FALSE);
    if (!$moduleNames) {
      return TRUE;
    }

    try {
      $this->moduleInstaller->install($moduleNames, TRUE);
      return TRUE;
    }
    catch (\Exception $e) {
      $this->messenger()->addError($e->getMessage());
    }

    return FALSE;
  }

  /**
   * Use this function only for newly created modules.
   */
  protected function getModulePath(string $moduleName): string {
    try {
      return $this->moduleList->getPath($moduleName);
    }
    catch (UnknownExtensionException $e) {
      // Nothing to do.
    }

    $candidates = [
      Path::join('modules', 'custom', $moduleName),
      Path::join('modules', 'contrib', $moduleName),
    ];

    foreach ($candidates as $candidate) {
      if ($this->fs->exists("$candidate/$moduleName.info.yml")) {
        return $candidate;
      }
    }

    throw new UnknownExtensionException("The module '$moduleName' does not exist.");
  }

  protected function doItComposerRequire(): static {
    $this->installComposerPackages('prod', $this->getRequiredPackagesProd());
    $this->installComposerPackages('dev', $this->getRequiredPackagesDev());

    return $this;
  }

  protected function doItModuleInstall(): static {
    $moduleNames = array_keys($this->getRequiredModules(), TRUE, TRUE);
    foreach ($moduleNames as $moduleName) {
      $this->ensureModuleInstalled($moduleName);
    }

    return $this;
  }

}

Главная | Обратная связь

drupal hosting | друпал хостинг | it patrol .inc