drupalorg-1.0.x-dev/src/Drush/Commands/DrushCommands.php

src/Drush/Commands/DrushCommands.php
<?php

namespace Drupal\drupalorg\Drush\Commands;

use Drupal\Component\Datetime\TimeInterface;
use Drupal\Core\Cache\MemoryCache\MemoryCache;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\Site\Settings;
use Drupal\drupalorg\ProjectService;
use Drupal\drupalorg\Traits\GitLabClientTrait;
use Drupal\drupalorg\Utilities\ActiveInstalls;
use Drupal\drupalorg\Utilities\ComposerNamespace;
use Drupal\drupalorg\Utilities\CoreCompatibility;
use Drupal\node\Entity\Node;
use Drupal\node\NodeInterface;
use Drush\Commands\AutowireTrait;
use Drush\Commands\DrushCommands as BaseDrushCommands;
use Drush\Attributes as CLI;
use Drush\Drush;
use Gitlab\ResultPager;
use Symfony\Component\DependencyInjection\Attribute\Autowire;

/**
 * A drush command file.
 *
 * @package Drupal\drupalorg\Commands
 */
final class DrushCommands extends BaseDrushCommands {

  use AutowireTrait;
  use GitLabClientTrait;

  /**
   * {@inheritdoc}
   */
  public function __construct(
    #[Autowire(service: 'entity.memory_cache')]
    protected MemoryCache $memoryCache,
    #[Autowire(service: 'drupalorg.project_service')]
    protected ProjectService $projectService,
    protected EntityTypeManagerInterface $entityTypeManager,
    protected TimeInterface $time,
  ) {
    parent::__construct();
  }

  /**
   * Calculate the active installs for modules based on the usage table.
   */
  #[CLI\Command(name: 'drupalorg:calculate-active-installs')]
  public function calculateActiveInstalls() {
    ActiveInstalls::calculateNewActiveInstalls(NULL, Drush::verbose());
  }

  /**
   * Calculate the composer namespaces for modules based on update status info.
   *
   * @see https://www.drupal.org/drupalorg/docs/apis/update-status-xml
   */
  #[CLI\Command(name: 'drupalorg:calculate-composer-namespaces')]
  public function calculateComposerNamespaces() {
    // This is setting a default "drupal/<machine_name>" if not found. Due
    // to D7 and below modules. Otherwise, it'll keep trying those every time.
    // The below line could be part of an update hook.
    ComposerNamespace::calculateNamespace(TRUE);
  }

  /**
   * Calculate the composer compatibility for modules based on their releases.
   */
  #[CLI\Command(name: 'drupalorg:calculate-composer-compatibilities')]
  public function calculateComposerCompatibilities() {
    $last_run = \Drupal::state()->get('drupalorg.composer_compatibilities');
    $request_time = \Drupal::time()->getRequestTime();
    CoreCompatibility::calculateNewCoreCompatibilities($last_run);
    \Drupal::state()->set('drupalorg.composer_compatibilities', $request_time);
  }

  /**
   * Check for disabled GitLab system hooks.
   */
  #[CLI\Command(name: 'drupalorg:check-gitlab-system-hooks')]
  public function checkGitlabSystemHooks(): int {
    $return = self::EXIT_SUCCESS;
    foreach ($this->getGitLabClient()->systemHooks()->all() as $hook) {
      $this->logger->notice('{url} is {alert_status}, disabled until {disabled_until}', [
        'url' => $hook['url'],
        'alert_status' => $hook['alert_status'],
        'disabled_until' => $hook['disabled_until'],
      ]);
      if ($hook['alert_status'] !== 'executable') {
        $this->logger->error('{url} system hook is not executable! API data: {data}', [
          'url' => $hook['url'],
          'data' => json_encode($hook),
        ]);
        $return = self::EXIT_FAILURE_WITH_CLARITY;
      }
    }

    if ($return === self::EXIT_SUCCESS) {
      $this->logger->notice('GitLab system hooks are all enabled!');
    }

    return $return;
  }

  /**
   * Fetch GitLab avatars and set them as logos where available.
   */
  #[CLI\Command(name: 'drupalorg:fetch-gitlab-avatars')]
  public function fetchGitlabAvatars(): void {
    try {
      $client = $this->getGitLabClient();

      $project_nids = \Drupal::entityQuery('node')
        ->accessCheck(FALSE)
        ->condition('type', ProjectService::PROJECT_TYPES, 'IN')
        ->notExists('field_logo_url')
        ->execute();
      foreach (array_chunk($project_nids, 100) as $nids) {
        foreach (Node::loadMultiple($nids) as $project_node) {
          $repository = $this->projectService->getProjectRepositoryInformation($project_node);
          if (empty($repository['gitlab_project_id'])) {
            continue;
          }

          $this->logger->notice('Checking project logo for {project}', [
            'project' => $project_node->get('field_project_machine_name')->value,
          ]);

          $gitlab_project = $client->projects()->show($repository['gitlab_project_id']);
          $this->projectService->updateLogo($project_node, $gitlab_project['avatar_url'] ?? '');
        }
        $this->memoryCache->deleteAll();
      }
    }
    catch (\Throwable $e) {
      $this->logger->error('Could not connect to GitLab or fetch avatars. Code {code} at {at}. Message: {message}', [
        'code' => $e->getCode(),
        'at' => $e->getFile() . ':' . $e->getLine(),
        'message' => $e->getMessage(),
      ]);
    }
  }

  /**
   * Populate milestones for security team.
   */
  #[CLI\Command(name: 'drupalorg:security-populate-milestones')]
  public function securityPopulateMilestones() {
    $gitlab_client = $this->getGitLabClient();
    $milestones = array_column((new ResultPager($gitlab_client))->fetchAll($gitlab_client->groupsMilestones(), 'all', ['security']), 'id', 'due_date');

    $wednesdays = new \DatePeriod(new \DateTimeImmutable('Wednesday'), new \DateInterval('P7D'), 16);
    foreach ($wednesdays as $wednesday) {
      $date = $wednesday->format('Y-m-d');
      if (!isset($milestones[$date])) {
        $label = $date;
        $day_of_month = (int) $wednesday->format('j');
        if ($day_of_month > 14 && $day_of_month <= 21) {
          $label .= ' core';
        }
        $gitlab_client->groupsMilestones()->create('security', [
          'start_date' => $wednesday->sub(new \DateInterval('P6D'))->format('Y-m-d'),
          'due_date' => $date,
          'title' => $label,
        ]);
      }
    }
  }

  /**
   * Calculate organization rank.
   *
   * While the site is in transition, this only populates
   * field_org_rank_components, which the legacy site fetches to complete
   * ranking calculations.
   */
  #[CLI\Command(name: 'drupalorg:update-organization-rank')]
  public function updateOrganizationRank(): void {
    // Deltas for field_org_rank_components:
    // 0: weighted issue credits, 12 months
    // 1: association partnerships
    // 2: association memberships
    // 3: individual memberships
    // 4: case studies
    // 5: project(s) supported
    // 6: contributor roles
    // 7: contribution without issue credits
    // 8: contribution with 3 months of issue credit
    // 9: contribution with 12 months of issue credit
    // 10: Event contribution
    // 11: Weighted security advisories, 12 months.
    $weights = Settings::get('drupalorg_organization_rank_weights');

    // Batch load case studies. Index by organization nid and Drupal version.
    $case_studies = [];
    $case_study_nids = $this->entityTypeManager->getStorage('node')->getQuery()
      ->accessCheck(TRUE)
      ->condition('type', 'casestudy')
      ->condition('status', NodeInterface::PUBLISHED)
      ->execute();
    foreach (array_chunk($case_study_nids, 100) as $nids) {
      foreach (Node::loadMultiple($nids) as $node) {
        foreach ($node->get('field_case_organizations')->getValue() as $organization) {
          foreach ($node->get('field_drupal_version')->referencedEntities() as $version) {
            $case_studies[$organization['target_id']][$version->label()] = ($case_studies[$organization['target_id']][$version->label()] ?? 0) + 1;
          }
        }
      }
    }

    // Iterate over organizations.
    $organization_nids = $this->entityTypeManager->getStorage('node')->getQuery()
      ->accessCheck(FALSE)
      ->condition('type', 'organization')
      ->execute();
    foreach (Node::loadMultiple($organization_nids) as $organization_node) {
      $this->logger->info('Updating rank for {title} ({nid})', [
        'title' => $organization_node->getTitle(),
        'nid' => $organization_node->id(),
      ]);
      $new_rank = array_fill(0, 12, 0);
      $updated = FALSE;

      // Case studies.
      foreach ($weights['case study'] as $drupal_version => $weight) {
        $new_rank[4] += ($case_studies[$organization_node->id()][$drupal_version] ?? 0) * $weight;
      }

      // Update if needed.
      foreach ($new_rank as $delta => $value) {
        if ($organization_node->get('field_org_rank_components')->get($delta)?->getValue()['value'] != $value) {
          $organization_node->set('field_org_rank_components', $new_rank);
          $updated = TRUE;
          break;
        }
      }
      if ($updated) {
        $organization_node->setSyncing(TRUE);
        $organization_node->save();
      }
    }
  }

}

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

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