From 7a44c0dff4a5a4e1949e5d84a976bf267eb45439 Mon Sep 17 00:00:00 2001 From: Aron Novak Date: Thu, 21 Sep 2023 10:10:03 +0200 Subject: [PATCH 1/5] working release notes via packagist --- RoboFile.php | 4 +- composer.json | 1 + composer.lock | 33 ++- robo-components/ReleaseNotesTrait.php | 283 -------------------------- 4 files changed, 35 insertions(+), 286 deletions(-) delete mode 100644 robo-components/ReleaseNotesTrait.php diff --git a/RoboFile.php b/RoboFile.php index 6c95e68be..4c7ab7b6f 100644 --- a/RoboFile.php +++ b/RoboFile.php @@ -8,7 +8,7 @@ use RoboComponents\DeploymentTrait; use RoboComponents\ElasticSearchTrait; use RoboComponents\PhpcsTrait; -use RoboComponents\ReleaseNotesTrait; +use GizraRobo\ReleaseNotesTasks; use RoboComponents\ThemeTrait; use RoboComponents\TranslationManagement\ExportFromConfig; use RoboComponents\TranslationManagement\ImportToConfig; @@ -29,7 +29,7 @@ class RoboFile extends Tasks { use ImportToConfig; use ImportToUi; use PhpcsTrait; - use ReleaseNotesTrait; + use ReleaseNotesTasks; use ThemeTrait; /** diff --git a/composer.json b/composer.json index 50d473900..d7254bd9a 100644 --- a/composer.json +++ b/composer.json @@ -74,6 +74,7 @@ "drupal/upgrade_status": "^4.0", "drupal/username_enumeration_prevention": "^1.3", "drush/drush": "^11", + "gizra/robo-release-notes": "^0.1.0", "longwave/laminas-diactoros": "^2.14", "npm-asset/anchor-js": "^5.0", "npm-asset/select2": "^4.0", diff --git a/composer.lock b/composer.lock index 563daddbf..ab54ac96f 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "76a715497d8f7dc20c8ec00aae9bde9f", + "content-hash": "d6e3b976579322a2d8ce23f6dda2e4bf", "packages": [ { "name": "asm89/stack-cors", @@ -5750,6 +5750,37 @@ }, "time": "2022-12-07T11:28:53+00:00" }, + { + "name": "gizra/robo-release-notes", + "version": "0.1.1", + "source": { + "type": "git", + "url": "https://github.com/Gizra/robo-release-notes.git", + "reference": "e78220652c7e5318b6c8f8537fced142bd3bff9b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Gizra/robo-release-notes/zipball/e78220652c7e5318b6c8f8537fced142bd3bff9b", + "reference": "e78220652c7e5318b6c8f8537fced142bd3bff9b", + "shasum": "" + }, + "require": { + "consolidation/robo": "^4.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "GizraRobo\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "description": "Release notes Robo command as PHP trait", + "support": { + "issues": "https://github.com/Gizra/robo-release-notes/issues", + "source": "https://github.com/Gizra/robo-release-notes/tree/0.1.1" + }, + "time": "2023-09-21T08:08:42+00:00" + }, { "name": "grasmash/expander", "version": "3.0.0", diff --git a/robo-components/ReleaseNotesTrait.php b/robo-components/ReleaseNotesTrait.php deleted file mode 100644 index 25e9a7a6e..000000000 --- a/robo-components/ReleaseNotesTrait.php +++ /dev/null @@ -1,283 +0,0 @@ -taskExec("git tag | grep \"$tag\"") - ->printOutput(FALSE) - ->run() - ->getMessage(); - if (empty($result)) { - $this->say('The specified tag does not exist: ' . $tag); - } - } - - if (empty($result)) { - $latest_tag = $this->taskExec("git tag --sort=version:refname | tail -n1") - ->printOutput(FALSE) - ->run() - ->getMessage(); - if (empty($latest_tag)) { - throw new \Exception('There are no tags in this repository.'); - } - if ($this->confirm("Would you like to compare from the latest tag: $latest_tag?")) { - $tag = $latest_tag; - } - } - - // Detect organization / repository name from git remote. - $remote = $this->taskExec("git remote get-url origin") - ->printOutput(FALSE) - ->run() - ->getMessage(); - - if (!empty($remote)) { - $origin_parts = preg_split('/[:\/]/', str_replace('.git', '', $remote)); - if (!empty($origin_parts[1]) && !empty($origin_parts[2])) { - $github_org = $origin_parts[1]; - $github_project = $origin_parts[2]; - } - } - - if (!isset($github_org) || !isset($github_project)) { - $this->say('No GitHub project or GitHub organization found, so not trying to fetch details from GitHub API.'); - } - - // This is the heart of the release notes, the git history, we get all the - // commits since the specified last version and later on we parse - // the output. Optionally we enrich it with metadata from GitHub REST API. - $git_command = "git log --pretty=format:'%s¬¬|¬¬%b'"; - if (!empty($tag)) { - $git_command .= " $tag.."; - } - $log = $this->taskExec($git_command)->printOutput(FALSE)->run()->getMessage(); - $lines = explode("\n", $log); - - $this->say('Copy release notes below'); - - $this->printReleaseNotesTitle('Changelog'); - - $pull_requests_per_issue = []; - $no_issue_lines = []; - $contributors = []; - $issue_titles = []; - $additions = 0; - $deletions = 0; - $changed_files = 0; - - foreach ($lines as $line) { - $log_messages = explode("¬¬|¬¬", $line); - $pr_matches = []; - - // Here we need to handle two cases. - // Simple Merges: - // Merge pull request #1234 from Gizra/drupal-starter/1234 - // Squash & Merges, so there are messages like: - // Explanation (#1234) - preg_match_all('/Merge pull request #([0-9]+)/', $line, $pr_matches); - - if (count($log_messages) < 2) { - // No log message at all, not meaningful for changelog. - continue; - } - if (!isset($pr_matches[1][0])) { - // Could not detect PR number or it"s a Squash and Merge. - $pr_matches = []; - preg_match_all('!\(#([0-9]+)\)!', $line, $pr_matches); - if (!isset($pr_matches[0][0])) { - continue; - } - } - - $log_messages[1] = trim(str_replace('* ', '', $log_messages[1])); - - $pr_number = $pr_matches[1][0]; - if (!empty($github_org) && !empty($github_project)) { - /** @var \stdClass $pr_details */ - $pr_details = $this->githubApiGet("repos/$github_org/$github_project/pulls/$pr_number"); - if (!empty($pr_details->user)) { - $contributors[] = '@' . $pr_details->user->login; - $additions += $pr_details->additions; - $deletions += $pr_details->deletions; - $changed_files += $pr_details->changed_files; - - if (empty($log_messages[1])) { - $log_messages[1] = $pr_details->title; - } - } - } - - if (empty($log_messages[1])) { - // Whitespace-only log message, not meaningful for changelog. - continue; - } - - // The issue number is a required part of the branch name, - // So usually we can grab it from the log too, but that's optional - // If we cannot detect it, we still print a less verbose changelog line. - $issue_matches = []; - preg_match_all('!from [a-zA-Z-_0-9]+/([0-9]+)!', $line, $issue_matches); - - if (isset($issue_matches[1][0])) { - $issue_number = $issue_matches[1][0]; - } - else { - // Retrieve the issue number from the PR description via GitHub API. - $pr = NULL; - if (!empty($github_project) && !empty($github_org)) { - $pr = $this->githubApiGet("repos/$github_org/$github_project/pulls/$pr_number"); - } - if (!isset($pr->body)) { - $no_issue_lines[] = "- $log_messages[1] (#$pr_number)"; - continue; - } - preg_match_all('!#([0-9]+)!', $pr->body, $issue_matches); - if (!isset($issue_matches[1][0])) { - $no_issue_lines[] = "- $log_messages[1] (#$pr_number)"; - continue; - } - $issue_number = $issue_matches[1][0]; - } - - if (!empty($issue_number)) { - if (!isset($issue_titles[$issue_number]) && !empty($github_org) && !empty($github_project)) { - /** @var \stdClass $issue_details */ - $issue_details = $this->githubApiGet("repos/$github_org/$github_project/issues/$issue_number"); - if (!empty($issue_details->title)) { - $issue_titles[$issue_number] = $issue_details->title; - $contributors[] = '@' . $issue_details->user->login; - } - } - - if (isset($issue_titles[$issue_number])) { - $issue_line = "- $issue_titles[$issue_number] (#$issue_number)"; - } - else { - $issue_line = "- Issue #$issue_number"; - } - if (!isset($pull_requests_per_issue[$issue_line])) { - $pull_requests_per_issue[$issue_line] = []; - } - $pull_requests_per_issue[$issue_line][] = " - $log_messages[1] (#{$pr_matches[1][0]})"; - } - else { - $no_issue_lines[] = "- $log_messages[1] (#$pr_number)"; - } - } - - foreach ($pull_requests_per_issue as $issue_line => $pr_lines) { - print $issue_line . "\n"; - foreach ($pr_lines as $pr_line) { - print $pr_line . "\n"; - } - } - - $this->printReleaseNotesSection('', $no_issue_lines); - - if (isset($github_org)) { - $contributors = array_count_values($contributors); - arsort($contributors); - $this->printReleaseNotesSection('Contributors', $contributors, TRUE); - - $this->printReleaseNotesSection('Code statistics', [ - "Lines added: $additions", - "Lines deleted: $deletions", - "Files changed: $changed_files", - ]); - } - } - - /** - * Print a section for the release notes. - * - * @param string $title - * Section title. - * @param array $lines - * Bullet points. - * @param bool $print_key - * Whether to print the key of the array. - */ - protected function printReleaseNotesSection(string $title, array $lines, bool $print_key = FALSE): void { - if (!empty($title)) { - $this->printReleaseNotesTitle($title); - } - foreach ($lines as $key => $line) { - if ($print_key) { - print "- $key ($line)\n"; - } - elseif (substr($line, 0, 1) == '-') { - print "$line\n"; - } - else { - print "- $line\n"; - } - } - } - - /** - * Print a title for the release notes. - * - * @param string $title - * Section title. - */ - protected function printReleaseNotesTitle(string $title): void { - echo "\n\n## $title\n"; - } - - /** - * Performs a GET request towards GitHub API using personal access token. - * - * @param string $path - * Resource/path to GET. - * - * @return mixed|null - * Decoded response or NULL. - * - * @throws \Exception - */ - protected function githubApiGet(string $path) { - $token = getenv('GITHUB_ACCESS_TOKEN'); - $username = getenv('GITHUB_USERNAME'); - if (empty($token)) { - throw new \Exception('Specify the personal access token in GITHUB_ACCESS_TOKEN environment variable before invoking the release notes generator in order to be able to fetch details of issues and pull requests'); - } - if (empty($username)) { - throw new \Exception('Specify the GitHub username in GITHUB_USERNAME environment variable before invoking the release notes generator in order to be able to fetch details of issues and pull requests'); - } - // We might not have a sane Drupal instance, let's not rely on Drupal API - // to generate release notes. - $ch = curl_init('https://api.github.com/' . $path); - curl_setopt($ch, CURLOPT_USERAGENT, 'Drupal Starter Release Notes Generator'); - curl_setopt($ch, CURLOPT_USERPWD, $username . ":" . $token); - curl_setopt($ch, CURLOPT_TIMEOUT, 30); - curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE); - curl_setopt($ch, CURLOPT_FOLLOWLOCATION, TRUE); - $result = curl_exec($ch); - $http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE); - curl_close($ch); - $result = empty($result) ? NULL : json_decode($result); - if (substr((string) $http_code, 0, 1) != 2) { - throw new \Exception("Failed to request the API:\n" . print_r($result, TRUE)); - } - return $result; - } - -} From b054e7fcbe31d504818b60b37a4185f5171f8a6a Mon Sep 17 00:00:00 2001 From: Aron Novak Date: Thu, 21 Sep 2023 10:21:13 +0200 Subject: [PATCH 2/5] phpcs --- RoboFile.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/RoboFile.php b/RoboFile.php index 4c7ab7b6f..aeeb556a5 100644 --- a/RoboFile.php +++ b/RoboFile.php @@ -3,12 +3,12 @@ declare(strict_types = 1); use Drupal\Core\DrupalKernel; +use GizraRobo\ReleaseNotesTasks; use Robo\Tasks; use RoboComponents\BootstrapTrait; use RoboComponents\DeploymentTrait; use RoboComponents\ElasticSearchTrait; use RoboComponents\PhpcsTrait; -use GizraRobo\ReleaseNotesTasks; use RoboComponents\ThemeTrait; use RoboComponents\TranslationManagement\ExportFromConfig; use RoboComponents\TranslationManagement\ImportToConfig; From 7777e433e95d8b4659cc0eaffec1dd0deb284f93 Mon Sep 17 00:00:00 2001 From: Aron Novak Date: Fri, 22 Sep 2023 06:11:36 +0200 Subject: [PATCH 3/5] phpcs as package --- RoboFile.php | 4 +-- composer.json | 1 + composer.lock | 33 ++++++++++++++++++- robo-components/PhpcsTrait.php | 60 ---------------------------------- 4 files changed, 35 insertions(+), 63 deletions(-) delete mode 100644 robo-components/PhpcsTrait.php diff --git a/RoboFile.php b/RoboFile.php index aeeb556a5..21e13a4e6 100644 --- a/RoboFile.php +++ b/RoboFile.php @@ -3,12 +3,12 @@ declare(strict_types = 1); use Drupal\Core\DrupalKernel; +use GizraRobo\PhpcsTasks; use GizraRobo\ReleaseNotesTasks; use Robo\Tasks; use RoboComponents\BootstrapTrait; use RoboComponents\DeploymentTrait; use RoboComponents\ElasticSearchTrait; -use RoboComponents\PhpcsTrait; use RoboComponents\ThemeTrait; use RoboComponents\TranslationManagement\ExportFromConfig; use RoboComponents\TranslationManagement\ImportToConfig; @@ -28,7 +28,7 @@ class RoboFile extends Tasks { use ExportFromConfig; use ImportToConfig; use ImportToUi; - use PhpcsTrait; + use PhpcsTasks; use ReleaseNotesTasks; use ThemeTrait; diff --git a/composer.json b/composer.json index d7254bd9a..169296402 100644 --- a/composer.json +++ b/composer.json @@ -74,6 +74,7 @@ "drupal/upgrade_status": "^4.0", "drupal/username_enumeration_prevention": "^1.3", "drush/drush": "^11", + "gizra/robo-phpcs": "^0.1.0", "gizra/robo-release-notes": "^0.1.0", "longwave/laminas-diactoros": "^2.14", "npm-asset/anchor-js": "^5.0", diff --git a/composer.lock b/composer.lock index ab54ac96f..d312d7be2 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "d6e3b976579322a2d8ce23f6dda2e4bf", + "content-hash": "e58b99a347df58c70481d4e644ec49ad", "packages": [ { "name": "asm89/stack-cors", @@ -5750,6 +5750,37 @@ }, "time": "2022-12-07T11:28:53+00:00" }, + { + "name": "gizra/robo-phpcs", + "version": "0.1.1", + "source": { + "type": "git", + "url": "https://github.com/Gizra/robo-phpcs.git", + "reference": "3e0a095526091c4454eba5b214de145d85557ae9" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Gizra/robo-phpcs/zipball/3e0a095526091c4454eba5b214de145d85557ae9", + "reference": "3e0a095526091c4454eba5b214de145d85557ae9", + "shasum": "" + }, + "require": { + "consolidation/robo": "^4.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "GizraRobo\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "description": "Coding standard check Robo command as PHP trait", + "support": { + "issues": "https://github.com/Gizra/robo-phpcs/issues", + "source": "https://github.com/Gizra/robo-phpcs/tree/0.1.1" + }, + "time": "2023-09-22T04:09:45+00:00" + }, { "name": "gizra/robo-release-notes", "version": "0.1.1", diff --git a/robo-components/PhpcsTrait.php b/robo-components/PhpcsTrait.php deleted file mode 100644 index 7e9fd8c1e..000000000 --- a/robo-components/PhpcsTrait.php +++ /dev/null @@ -1,60 +0,0 @@ -_exec("cd web && ../vendor/bin/$command $directory $arguments"); - if (empty($error_code) && !$result->wasSuccessful()) { - $error_code = $result->getExitCode(); - } - } - } - } - - if (!empty($error_code)) { - return new ResultData($error_code, 'PHPCS found some issues'); - } - return NULL; - } - -} From 948a21ebb56079e142f06727cdcf68b31b02bd9b Mon Sep 17 00:00:00 2001 From: Aron Novak Date: Fri, 22 Sep 2023 11:18:19 +0200 Subject: [PATCH 4/5] replace to composer packages --- RoboFile.php | 24 +- composer.json | 10 +- composer.lock | 454 +++++++---- robo-components/BootstrapTrait.php | 389 --------- robo-components/DeploymentTrait.php | 745 ------------------ robo-components/ElasticSearchTrait.php | 268 ------- robo-components/ThemeTrait.php | 216 ----- .../ExportFromConfig.php | 155 ---- .../TranslationManagement/ImportToConfig.php | 84 -- .../TranslationManagement/ImportToUi.php | 48 -- 10 files changed, 320 insertions(+), 2073 deletions(-) delete mode 100644 robo-components/BootstrapTrait.php delete mode 100644 robo-components/DeploymentTrait.php delete mode 100644 robo-components/ElasticSearchTrait.php delete mode 100644 robo-components/ThemeTrait.php delete mode 100644 robo-components/TranslationManagement/ExportFromConfig.php delete mode 100644 robo-components/TranslationManagement/ImportToConfig.php delete mode 100644 robo-components/TranslationManagement/ImportToUi.php diff --git a/RoboFile.php b/RoboFile.php index 21e13a4e6..25b7afb12 100644 --- a/RoboFile.php +++ b/RoboFile.php @@ -5,14 +5,12 @@ use Drupal\Core\DrupalKernel; use GizraRobo\PhpcsTasks; use GizraRobo\ReleaseNotesTasks; +use GizraRobo\BootstrapTasks; +use GizraRobo\DeploymentTasks; +use GizraRobo\ElasticSearchTasks; +use GizraRobo\ThemeTasks; +use GizraRobo\TranslationTasks; use Robo\Tasks; -use RoboComponents\BootstrapTrait; -use RoboComponents\DeploymentTrait; -use RoboComponents\ElasticSearchTrait; -use RoboComponents\ThemeTrait; -use RoboComponents\TranslationManagement\ExportFromConfig; -use RoboComponents\TranslationManagement\ImportToConfig; -use RoboComponents\TranslationManagement\ImportToUi; use Symfony\Component\HttpFoundation\Request; $GLOBALS['drupal_autoloader'] = require_once 'web/autoload.php'; @@ -22,15 +20,13 @@ */ class RoboFile extends Tasks { - use BootstrapTrait; - use DeploymentTrait; - use ElasticSearchTrait; - use ExportFromConfig; - use ImportToConfig; - use ImportToUi; + use BootstrapTasks; + use DeploymentTasks; + use ElasticSearchTasks; + use TranslationTasks; use PhpcsTasks; use ReleaseNotesTasks; - use ThemeTrait; + use ThemeTasks; /** * Defines a list of languages installed on the site. diff --git a/composer.json b/composer.json index 169296402..4cfac8e48 100644 --- a/composer.json +++ b/composer.json @@ -74,8 +74,13 @@ "drupal/upgrade_status": "^4.0", "drupal/username_enumeration_prevention": "^1.3", "drush/drush": "^11", + "gizra/robo-bootstrap": "^0.1.1", + "gizra/robo-deployment": "^0.1.0", + "gizra/robo-elasticsearch": "^0.1.0", "gizra/robo-phpcs": "^0.1.0", "gizra/robo-release-notes": "^0.1.0", + "gizra/robo-theme": "^0.1.0", + "gizra/robo-translation": "^0.1.0", "longwave/laminas-diactoros": "^2.14", "npm-asset/anchor-js": "^5.0", "npm-asset/select2": "^4.0", @@ -110,11 +115,6 @@ }, "minimum-stability": "dev", "prefer-stable": true, - "autoload": { - "psr-4": { - "RoboComponents\\": "./robo-components/" - } - }, "config": { "sort-packages": true, "allow-plugins": { diff --git a/composer.lock b/composer.lock index d312d7be2..897843173 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "e58b99a347df58c70481d4e644ec49ad", + "content-hash": "4a0b816de72869d4de07102961992655", "packages": [ { "name": "asm89/stack-cors", @@ -5750,6 +5750,247 @@ }, "time": "2022-12-07T11:28:53+00:00" }, + { + "name": "gettext/gettext", + "version": "v5.7.0", + "source": { + "type": "git", + "url": "https://github.com/php-gettext/Gettext.git", + "reference": "8657e580747bb3baacccdcebe69cac094661e404" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-gettext/Gettext/zipball/8657e580747bb3baacccdcebe69cac094661e404", + "reference": "8657e580747bb3baacccdcebe69cac094661e404", + "shasum": "" + }, + "require": { + "gettext/languages": "^2.3", + "php": "^7.2|^8.0" + }, + "require-dev": { + "brick/varexporter": "^0.3.5", + "friendsofphp/php-cs-fixer": "^3.2", + "oscarotero/php-cs-fixer-config": "^2.0", + "phpunit/phpunit": "^8.0|^9.0", + "squizlabs/php_codesniffer": "^3.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Gettext\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Oscar Otero", + "email": "oom@oscarotero.com", + "homepage": "http://oscarotero.com", + "role": "Developer" + } + ], + "description": "PHP gettext manager", + "homepage": "https://github.com/php-gettext/Gettext", + "keywords": [ + "JS", + "gettext", + "i18n", + "mo", + "po", + "translation" + ], + "support": { + "email": "oom@oscarotero.com", + "issues": "https://github.com/php-gettext/Gettext/issues", + "source": "https://github.com/php-gettext/Gettext/tree/v5.7.0" + }, + "funding": [ + { + "url": "https://paypal.me/oscarotero", + "type": "custom" + }, + { + "url": "https://github.com/oscarotero", + "type": "github" + }, + { + "url": "https://www.patreon.com/misteroom", + "type": "patreon" + } + ], + "time": "2022-07-27T19:54:55+00:00" + }, + { + "name": "gettext/languages", + "version": "2.10.0", + "source": { + "type": "git", + "url": "https://github.com/php-gettext/Languages.git", + "reference": "4d61d67fe83a2ad85959fe6133d6d9ba7dddd1ab" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-gettext/Languages/zipball/4d61d67fe83a2ad85959fe6133d6d9ba7dddd1ab", + "reference": "4d61d67fe83a2ad85959fe6133d6d9ba7dddd1ab", + "shasum": "" + }, + "require": { + "php": ">=5.3" + }, + "require-dev": { + "phpunit/phpunit": "^4.8 || ^5.7 || ^6.5 || ^7.5 || ^8.4" + }, + "bin": [ + "bin/export-plural-rules" + ], + "type": "library", + "autoload": { + "psr-4": { + "Gettext\\Languages\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Michele Locati", + "email": "mlocati@gmail.com", + "role": "Developer" + } + ], + "description": "gettext languages with plural rules", + "homepage": "https://github.com/php-gettext/Languages", + "keywords": [ + "cldr", + "i18n", + "internationalization", + "l10n", + "language", + "languages", + "localization", + "php", + "plural", + "plural rules", + "plurals", + "translate", + "translations", + "unicode" + ], + "support": { + "issues": "https://github.com/php-gettext/Languages/issues", + "source": "https://github.com/php-gettext/Languages/tree/2.10.0" + }, + "funding": [ + { + "url": "https://paypal.me/mlocati", + "type": "custom" + }, + { + "url": "https://github.com/mlocati", + "type": "github" + } + ], + "time": "2022-10-18T15:00:10+00:00" + }, + { + "name": "gizra/robo-bootstrap", + "version": "0.1.1", + "source": { + "type": "git", + "url": "https://github.com/Gizra/robo-bootstrap.git", + "reference": "709e214704f16a6d30c7ba8fed0b326f08a3a46a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Gizra/robo-bootstrap/zipball/709e214704f16a6d30c7ba8fed0b326f08a3a46a", + "reference": "709e214704f16a6d30c7ba8fed0b326f08a3a46a", + "shasum": "" + }, + "require": { + "consolidation/robo": "^4.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "GizraRobo\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "description": "Bootstraps a new Drupal project on Pantheon: Robo command as PHP trait", + "support": { + "issues": "https://github.com/Gizra/robo-bootstrap/issues", + "source": "https://github.com/Gizra/robo-bootstrap/tree/0.1.1" + }, + "time": "2023-09-22T07:56:27+00:00" + }, + { + "name": "gizra/robo-deployment", + "version": "0.1.0", + "source": { + "type": "git", + "url": "https://github.com/Gizra/robo-deployment.git", + "reference": "041d097590144c5f169129ce2ca1e7031ddba579" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Gizra/robo-deployment/zipball/041d097590144c5f169129ce2ca1e7031ddba579", + "reference": "041d097590144c5f169129ce2ca1e7031ddba579", + "shasum": "" + }, + "require": { + "consolidation/robo": "^4.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "GizraRobo\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "description": "Pantheon-specific deployment Robo commands as PHP trait", + "support": { + "issues": "https://github.com/Gizra/robo-deployment/issues", + "source": "https://github.com/Gizra/robo-deployment/tree/0.1.0" + }, + "time": "2023-09-22T09:06:25+00:00" + }, + { + "name": "gizra/robo-elasticsearch", + "version": "0.1.0", + "source": { + "type": "git", + "url": "https://github.com/Gizra/robo-elasticsearch.git", + "reference": "32783c43e508c198ebe5759224a54c176ba69be4" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Gizra/robo-elasticsearch/zipball/32783c43e508c198ebe5759224a54c176ba69be4", + "reference": "32783c43e508c198ebe5759224a54c176ba69be4", + "shasum": "" + }, + "require": { + "consolidation/robo": "^4.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "GizraRobo\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "description": "ElasticSearch provision for Drupal Robo command as PHP trait", + "support": { + "issues": "https://github.com/Gizra/robo-elasticsearch/issues", + "source": "https://github.com/Gizra/robo-elasticsearch/tree/0.1.0" + }, + "time": "2023-09-22T07:37:20+00:00" + }, { "name": "gizra/robo-phpcs", "version": "0.1.1", @@ -5812,6 +6053,69 @@ }, "time": "2023-09-21T08:08:42+00:00" }, + { + "name": "gizra/robo-theme", + "version": "0.1.0", + "source": { + "type": "git", + "url": "https://github.com/Gizra/robo-theme.git", + "reference": "1e38549b744f06218fbf20285e466f3e32bea7f2" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Gizra/robo-theme/zipball/1e38549b744f06218fbf20285e466f3e32bea7f2", + "reference": "1e38549b744f06218fbf20285e466f3e32bea7f2", + "shasum": "" + }, + "require": { + "consolidation/robo": "^4.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "GizraRobo\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "description": "Theme compilation Robo command as PHP trait", + "support": { + "issues": "https://github.com/Gizra/robo-theme/issues", + "source": "https://github.com/Gizra/robo-theme/tree/0.1.0" + }, + "time": "2023-09-22T07:10:57+00:00" + }, + { + "name": "gizra/robo-translation", + "version": "0.1.0", + "source": { + "type": "git", + "url": "https://github.com/Gizra/robo-translation.git", + "reference": "a4630db5617ebdcec01bf070b906ae49e6bf5f15" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Gizra/robo-translation/zipball/a4630db5617ebdcec01bf070b906ae49e6bf5f15", + "reference": "a4630db5617ebdcec01bf070b906ae49e6bf5f15", + "shasum": "" + }, + "require": { + "consolidation/robo": "^4.0", + "gettext/gettext": "^5.7" + }, + "type": "library", + "autoload": { + "psr-4": { + "GizraRobo\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "description": "PO-file based translation workflow Robo command as PHP trait", + "support": { + "issues": "https://github.com/Gizra/robo-translation/issues", + "source": "https://github.com/Gizra/robo-translation/tree/0.1.0" + }, + "time": "2023-09-22T08:53:41+00:00" + }, { "name": "grasmash/expander", "version": "3.0.0", @@ -12308,154 +12612,6 @@ "source": "https://git.drupalcode.org/project/potx" } }, - { - "name": "gettext/gettext", - "version": "v5.7.0", - "source": { - "type": "git", - "url": "https://github.com/php-gettext/Gettext.git", - "reference": "8657e580747bb3baacccdcebe69cac094661e404" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/php-gettext/Gettext/zipball/8657e580747bb3baacccdcebe69cac094661e404", - "reference": "8657e580747bb3baacccdcebe69cac094661e404", - "shasum": "" - }, - "require": { - "gettext/languages": "^2.3", - "php": "^7.2|^8.0" - }, - "require-dev": { - "brick/varexporter": "^0.3.5", - "friendsofphp/php-cs-fixer": "^3.2", - "oscarotero/php-cs-fixer-config": "^2.0", - "phpunit/phpunit": "^8.0|^9.0", - "squizlabs/php_codesniffer": "^3.0" - }, - "type": "library", - "autoload": { - "psr-4": { - "Gettext\\": "src" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Oscar Otero", - "email": "oom@oscarotero.com", - "homepage": "http://oscarotero.com", - "role": "Developer" - } - ], - "description": "PHP gettext manager", - "homepage": "https://github.com/php-gettext/Gettext", - "keywords": [ - "JS", - "gettext", - "i18n", - "mo", - "po", - "translation" - ], - "support": { - "email": "oom@oscarotero.com", - "issues": "https://github.com/php-gettext/Gettext/issues", - "source": "https://github.com/php-gettext/Gettext/tree/v5.7.0" - }, - "funding": [ - { - "url": "https://paypal.me/oscarotero", - "type": "custom" - }, - { - "url": "https://github.com/oscarotero", - "type": "github" - }, - { - "url": "https://www.patreon.com/misteroom", - "type": "patreon" - } - ], - "time": "2022-07-27T19:54:55+00:00" - }, - { - "name": "gettext/languages", - "version": "2.10.0", - "source": { - "type": "git", - "url": "https://github.com/php-gettext/Languages.git", - "reference": "4d61d67fe83a2ad85959fe6133d6d9ba7dddd1ab" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/php-gettext/Languages/zipball/4d61d67fe83a2ad85959fe6133d6d9ba7dddd1ab", - "reference": "4d61d67fe83a2ad85959fe6133d6d9ba7dddd1ab", - "shasum": "" - }, - "require": { - "php": ">=5.3" - }, - "require-dev": { - "phpunit/phpunit": "^4.8 || ^5.7 || ^6.5 || ^7.5 || ^8.4" - }, - "bin": [ - "bin/export-plural-rules" - ], - "type": "library", - "autoload": { - "psr-4": { - "Gettext\\Languages\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Michele Locati", - "email": "mlocati@gmail.com", - "role": "Developer" - } - ], - "description": "gettext languages with plural rules", - "homepage": "https://github.com/php-gettext/Languages", - "keywords": [ - "cldr", - "i18n", - "internationalization", - "l10n", - "language", - "languages", - "localization", - "php", - "plural", - "plural rules", - "plurals", - "translate", - "translations", - "unicode" - ], - "support": { - "issues": "https://github.com/php-gettext/Languages/issues", - "source": "https://github.com/php-gettext/Languages/tree/2.10.0" - }, - "funding": [ - { - "url": "https://paypal.me/mlocati", - "type": "custom" - }, - { - "url": "https://github.com/mlocati", - "type": "github" - } - ], - "time": "2022-10-18T15:00:10+00:00" - }, { "name": "instaclick/php-webdriver", "version": "1.4.16", diff --git a/robo-components/BootstrapTrait.php b/robo-components/BootstrapTrait.php deleted file mode 100644 index bbb0eedd6..000000000 --- a/robo-components/BootstrapTrait.php +++ /dev/null @@ -1,389 +0,0 @@ -verifyRequirements($project_name, $organization, $project_machine_name, $terminus_token, $github_token, $docker_mirror_url, $http_basic_auth_user, $http_basic_auth_password); - - $this->prepareGithubRepository($project_name, $organization, $project_machine_name, $github_repository_url, $docker_mirror_url); - - $this->createPantheonProject($terminus_token, $project_name, $project_machine_name, $organization); - - $this->deployPantheonInstallEnv('dev', $project_machine_name); - $this->deployPantheonInstallEnv('qa', $project_machine_name); - - $this->lockPantheonEnvironments($project_machine_name, $http_basic_auth_user, $http_basic_auth_password); - - $tfa_secret = $this->taskExec("openssl rand -base64 32") - ->printOutput(FALSE) - ->run() - ->getMessage(); - $this->taskExec('terminus self:plugin:install pantheon-systems/terminus-secrets-plugin')->run(); - $this->taskExec("terminus secrets:set $project_machine_name.qa tfa $tfa_secret")->run(); - $this->taskExec("terminus secrets:set $project_machine_name.dev tfa $tfa_secret")->run(); - - $this->say("Bootstrap completed successfully."); - $this->say("You might want to run the following commands to properly place the project:"); - $this->say("mv .bootstrap ../$project_machine_name"); - $this->say("mv .pantheon ../$project_machine_name/.pantheon"); - } - - /** - * Prepares the new GitHub repository for the project. - * - * @param string $project_name - * The project name. - * @param string $organization - * The GitHub organization. - * @param string $project_machine_name - * The project machine name in GH slug. - * @param string $github_repository_url - * The clone URL of the GitHub repository. - * @param string $docker_mirror_url - * The Docker mirror URL. Optional, but expect Travis failures if not set. - */ - protected function prepareGithubRepository(string $project_name, string $organization, string $project_machine_name, string $github_repository_url, string $docker_mirror_url = '') { - $temp_remote = 'bootstrap_' . time(); - $this->taskExec("git remote add $temp_remote $github_repository_url") - ->run(); - $this->taskExec("git push --force $temp_remote main") - ->run(); - $this->taskExec("git remote remove $temp_remote") - ->run(); - - $this->taskExec("git clone $github_repository_url .bootstrap") - ->run(); - - if (!file_exists('.bootstrap')) { - throw new \Exception("Failed to clone the GitHub repository. You might need to execute: `ddev auth ssh` beforehand."); - } - - if (!file_exists('.bootstrap/.ddev/config.yaml')) { - throw new \Exception("The GitHub repository is not in the expected format."); - } - - $this->taskReplaceInFile('.bootstrap/robo-components/DeploymentTrait.php') - ->from('Gizra/drupal-starter') - ->to("$organization/$project_machine_name") - ->run(); - - $this->taskReplaceInFile('.bootstrap/.ddev/config.yaml') - ->from('drupal-starter') - ->to($project_machine_name) - ->run(); - - $this->taskReplaceInFile('.bootstrap/.ddev/config.yaml') - ->from('8880') - ->to((string) rand(6000, 8000)) - ->run(); - - $this->taskReplaceInFile('.bootstrap/.ddev/config.yaml') - ->from('4443') - ->to((string) rand(3000, 5000)) - ->run(); - - $host_user = $this->taskExec("whoami") - ->printOutput(FALSE) - ->run() - ->getMessage(); - - $this->taskReplaceInFile('.bootstrap/README.md') - ->from('Drupal 9 Starter') - ->to($project_name) - ->run(); - - $this->taskReplaceInFile('.bootstrap/README.md') - ->from('Gizra') - ->to($organization) - ->run(); - - $this->taskReplaceInFile('.bootstrap/README.md') - ->from('drupal-starter') - ->to($project_machine_name) - ->run(); - - $this->taskReplaceInFile('.bootstrap/.ddev/providers/pantheon.yaml') - ->from('gizra-drupal-starter.qa') - ->to($project_name . '.qa') - ->run(); - - $this->taskReplaceInFile('.bootstrap/composer.json') - ->from('drupal-starter') - ->to(strtolower($project_machine_name)) - ->run(); - $this->taskReplaceInFile('.bootstrap/composer.json') - ->from('gizra') - ->to(strtolower($organization)) - ->run(); - - $this->taskReplaceInFile('.bootstrap/web/sites/default/settings.pantheon.php') - ->from('drupal_starter') - ->to(str_replace('-', '_', $project_machine_name)) - ->run(); - - $this->taskReplaceInFile('.bootstrap/.travis.template.yml') - ->from('DOCKER_MIRROR') - ->to($docker_mirror_url) - ->run(); - - $result = $this->taskExec("cd .bootstrap && composer update --lock") - ->run() - ->getExitCode(); - if ($result !== 0) { - throw new \Exception("Failed to run composer update in GH repository."); - } - - $this->taskReplaceInFile('.bootstrap/config/sync/system.site.yml') - ->from('Drupal Starter') - ->to($project_name) - ->run(); - - $result = $this->taskExec("cd .bootstrap && git add . && git commit -m 'Bootstrap project $project_name by $host_user' && git push origin main") - ->run() - ->getExitCode(); - - if ($result !== 0) { - throw new \Exception("Failed to push to GH repository the result of the transformation."); - } - } - - /** - * Creates and prepare the Pantheon project. - * - * @param string $terminus_token - * The Pantheon machine token. - * @param string $project_name - * The project name. - * @param string $project_machine_name - * The project machine name in GH slug. - * @param string $organization - * The GitHub/Pantheon organization. - */ - protected function createPantheonProject(string $terminus_token, string $project_name, string $project_machine_name, string $organization) { - $result = $this->taskExec("terminus auth:login --machine-token=\"$terminus_token\"") - ->run() - ->getExitCode(); - if ($result !== 0) { - throw new \Exception("Failed to login to Terminus."); - } - - $result = $this->taskExec("terminus site:create $project_machine_name \"$project_name\" \"bde48795-b16d-443f-af01-8b1790caa1af\" --org=\"$organization\"") - ->run() - ->getExitCode(); - - if ($result !== 0) { - throw new \Exception("Failed to create the Pantheon project."); - } - - $result = $this->taskExec("terminus connection:set $project_machine_name.dev git") - ->run() - ->getExitCode(); - - if ($result !== 0) { - throw new \Exception("Failed to set the Pantheon project connection mode to Git."); - } - - // Retrieve Git repository from Pantheon, then clone the artifact repository - // to .pantheon directory. - $pantheon_repository_url = $this->taskExec("terminus connection:info $project_machine_name.dev --field=git_url") - ->printOutput(FALSE) - ->run() - ->getMessage(); - - if (empty($pantheon_repository_url)) { - throw new \Exception("Failed to retrieve the Pantheon project Git repository URL."); - } - - $this->taskExec("git clone $pantheon_repository_url -b master .pantheon") - ->run(); - - // Ensure the dev dependencies are installed before compiling the theme in - // case this is a retry. - $this->taskExec('composer install')->run(); - - // Compile theme. - $this->themeCompile(); - - // Remove the dev dependencies before pushing up to Pantheon. - $this->taskExec("composer install --no-dev")->run(); - - $rsync_exclude_string = '--exclude=' . implode(' --exclude=', self::$deploySyncExcludes); - - $result = $this->_exec("rsync -az -q $rsync_exclude_string .bootstrap/ .pantheon")->getExitCode(); - if ($result !== 0) { - throw new \Exception('Failed to rsync .bootstrap to .pantheon'); - } - - // We need a working DDEV instance to compile the theme, that's why - // it is a bit awkward to assemble the Pantheon artifact repository - // from two GitHub repositories. - $result = $this->_exec("rsync -az -q $rsync_exclude_string " . self::$themeBase . "/ .pantheon/" . self::$themeBase)->getExitCode(); - if ($result !== 0) { - throw new \Exception('Failed to rsync theme to .pantheon'); - } - - $this->taskWriteToFile('.pantheon/.gitignore') - ->append(FALSE) - ->textFromFile('pantheon_template/gitignore-template') - ->run(); - $this->taskWriteToFile('.pantheon/pantheon.yml') - ->append(FALSE) - ->textFromFile('pantheon_template/pantheon.yml') - ->run(); - $this->taskWriteToFile('.pantheon/web/sites/default/settings.pantheon.php') - ->append(FALSE) - ->textFromFile('pantheon_template/settings.pantheon.php') - ->run(); - $this->taskWriteToFile('.pantheon/web/sites/default/settings.php') - ->append(FALSE) - ->textFromFile('.bootstrap/web/sites/default/settings.pantheon.php') - ->run(); - - $result = $this->taskExec("cd .pantheon && git add . && git commit -m 'Bootstrap project $project_name' && git push origin master") - ->run() - ->getExitCode(); - - if ($result !== 0) { - throw new \Exception('Failed to push to Pantheon.'); - } - - // Create QA environment on Pantheon. - $result = $this->taskExec("terminus multidev:create $project_machine_name.dev qa") - ->run() - ->getExitCode(); - - if ($result !== 0) { - throw new \Exception('Failed to create the Pantheon QA environment.'); - } - - $result = $this->taskExec("terminus connection:set $project_machine_name.qa git") - ->run() - ->getExitCode(); - - if ($result !== 0) { - throw new \Exception('Failed to set the Pantheon QA environment connection mode to Git.'); - } - } - - /** - * Lock all Pantheon environments for the given site. - * - * @param string $project_machine_name - * The machine name of the project. - * @param string $http_basic_auth_user - * The HTTP basic auth user. - * @param string $http_basic_auth_password - * The HTTP basic auth password. - */ - public function lockPantheonEnvironments(string $project_machine_name, string $http_basic_auth_user, string $http_basic_auth_password) { - if (empty($http_basic_auth_user) || empty($http_basic_auth_password)) { - $this->say("No HTTP basic auth credentials were provided. Pantheon environments will not be locked."); - return; - } - $pantheon_environments = $this->taskExec("terminus env:list $project_machine_name --field=ID --format=list") - ->printOutput(FALSE) - ->run() - ->getMessage(); - - $pantheon_environments = explode(PHP_EOL, $pantheon_environments); - foreach ($pantheon_environments as $pantheon_environment) { - $result = $this->taskExec("terminus env:wake $project_machine_name.$pantheon_environment") - ->run() - ->getExitCode(); - if ($result !== 0) { - $this->say("Failed to wake up the Pantheon $pantheon_environment environment."); - continue; - } - $result = $this->taskExec("terminus lock:enable $project_machine_name.$pantheon_environment $http_basic_auth_user $http_basic_auth_password") - ->run(); - if ($result !== 0) { - $this->say("Failed to lock the Pantheon $pantheon_environment environment."); - } - } - } - - /** - * Verify the input data / environment. - * - * @param string $project_name - * The project name. - * @param string $organization - * The GitHub organization. - * @param string $project_machine_name - * The project machine name in GH slug. - * @param string $terminus_token - * The Pantheon machine token. - * @param string $github_token - * The GitHub token. - * @param string $docker_mirror_url - * The Docker mirror URL. - * @param string $http_basic_auth_user - * The HTTP basic auth user. - * @param string $http_basic_auth_password - * The HTTP basic auth password. - */ - protected function verifyRequirements(string $project_name, string $organization, string $project_machine_name, string $terminus_token, string $github_token, string $docker_mirror_url, $http_basic_auth_user, $http_basic_auth_password) { - if (is_dir('.bootstrap')) { - throw new \Exception('The .bootstrap directory already exists. Please remove / move it and try again.'); - } - if (is_dir('.pantheon')) { - throw new \Exception('The .pantheon directory already exists. Please remove / move it and try again.'); - } - if (empty(trim($project_name))) { - throw new \Exception('The project name is empty.'); - } - if (empty(trim($organization))) { - throw new \Exception('The organization is empty.'); - } - if (empty(trim($project_machine_name))) { - throw new \Exception('The project machine name is empty.'); - } - if (str_contains($project_machine_name, ' ')) { - throw new \Exception('The project machine name contains spaces.'); - } - if (empty(trim($terminus_token))) { - throw new \Exception('The Pantheon machine token is empty.'); - } - if (empty(trim($github_token))) { - throw new \Exception('The GitHub token is empty.'); - } - if (empty(trim($docker_mirror_url))) { - throw new \Exception('The Docker mirror URL is empty.'); - } - if (!empty($docker_mirror_url) && !filter_var($docker_mirror_url, FILTER_VALIDATE_URL)) { - throw new \Exception('The Docker mirror URL is not a valid URL.'); - } - } - -} diff --git a/robo-components/DeploymentTrait.php b/robo-components/DeploymentTrait.php deleted file mode 100644 index 2db9f576f..000000000 --- a/robo-components/DeploymentTrait.php +++ /dev/null @@ -1,745 +0,0 @@ -taskExec('git status -s') - ->printOutput(FALSE) - ->run(); - - if ($result->getMessage()) { - $this->say($result->getMessage()); - throw new \Exception('The working directory is dirty. Please commit or stash the pending changes.'); - } - - $this->taskExec("git checkout $tag")->run(); - - // Full installation with dev dependencies as we need some of them for the - // build. - $this->taskExec("rm -rf vendor web/core web/libraries web/modules/contrib && composer install")->run(); - - if (empty($commit_message)) { - $commit_message = 'Release ' . $tag; - } - - // Set default exit code to 0 (success). - $exit = 0; - try { - $this->deployPantheon($branch_name, $commit_message); - } - catch (\Exception $e) { - $this->yell('The deployment failed', 22, 'red'); - $this->say($e->getMessage()); - // Set exit code to 1 (error). - $exit = 1; - } - // Check out the original branch regardless of success or failure. - $this->taskExec("git checkout -")->run(); - // Exit. - $this->taskExec("exit $exit")->run(); - } - - /** - * Deploy to Pantheon. - * - * @param string $branch_name - * The branch name to commit to. - * @param string|null $commit_message - * Supply a custom commit message for the pantheon repo. - * Falls back to: "Site update from [current_version]". - * - * @throws \Exception - */ - public function deployPantheon(string $branch_name = 'master', ?string $commit_message = NULL): void { - $pantheon_directory = '.pantheon'; - $deployment_version_path = $pantheon_directory . '/.deployment'; - - if (!file_exists($pantheon_directory) || !is_dir($pantheon_directory)) { - throw new \Exception('Clone the Pantheon artifact repository first into the .pantheon directory'); - } - - $result = $this - ->taskExec('git status -s') - ->printOutput(FALSE) - ->run(); - - if ($result->getMessage()) { - $this->say($result->getMessage()); - throw new \Exception('The Pantheon directory is dirty. Please commit any pending changes.'); - } - - $result = $this - ->taskExec("cd $pantheon_directory && git status -s") - ->printOutput(FALSE) - ->run(); - - if ($result->getMessage()) { - $this->say($result->getMessage()); - throw new \Exception('The Pantheon directory is dirty. Please commit any pending changes.'); - } - - // Validate pantheon.yml has web_docroot: true. - if (!file_exists($pantheon_directory . '/pantheon.yml')) { - throw new \Exception("pantheon.yml is missing from the Pantheon directory ($pantheon_directory)"); - } - - $yaml = Yaml::parseFile($pantheon_directory . '/pantheon.yml'); - if (empty($yaml['web_docroot'])) { - throw new \Exception("'web_docroot: true' is missing from pantheon.yml in Pantheon directory ($pantheon_directory)"); - } - - $this->_exec("cd $pantheon_directory && git checkout $branch_name"); - - // We deal with versions as commit hashes. - // The high-level goal is to prevent the auto-deploy process - // to overwrite the code with an older version if the Travis queue - // swaps the order of two jobs, so they are not executed in - // chronological order. - $currently_deployed_version = NULL; - if (file_exists($deployment_version_path)) { - $currently_deployed_version = trim(file_get_contents($deployment_version_path)); - } - - $result = $this - ->taskExec('git rev-parse HEAD') - ->printOutput(FALSE) - ->run(); - - $current_version = trim($result->getMessage()); - - if (!empty($currently_deployed_version)) { - $result = $this - ->taskExec('git cat-file -t ' . $currently_deployed_version) - ->printOutput(FALSE) - ->run(); - - if ($result->getMessage() !== 'commit') { - $this->yell(strtr('This current commit @current-commit cannot be deployed, since new commits have been created since, so we don\'t want to deploy an older version. Result was: @result', [ - '@current-commit' => $current_version, - '@result' => $result->getMessage(), - ])); - throw new \Exception('Aborting the process to avoid going back in time.'); - } - } - - // Ensure the dev dependencies are installed before compiling the theme in - // case this is a retry. - $this->taskExec('composer install')->run(); - - // Compile theme. - $this->themeCompile(); - - // Remove the dev dependencies before pushing up to Pantheon. - $this->taskExec("composer install --no-dev")->run(); - - $rsync_exclude_string = '--exclude=' . implode(' --exclude=', self::$deploySyncExcludes); - - // Copy all files and folders. - $result = $this->_exec("rsync -az -q --delete $rsync_exclude_string . $pantheon_directory")->getExitCode(); - if ($result !== 0) { - throw new \Exception('File sync failed'); - } - - // The settings.pantheon.php is managed by Pantheon, there can be updates, - // site-specific modifications belong to settings.php. - $this->_exec("cp web/sites/default/settings.pantheon.php $pantheon_directory/web/sites/default/settings.php"); - - // Flag the current version in the artifact repo. - file_put_contents($deployment_version_path, $current_version); - - // We don't want to change Pantheon's git ignore, as we do want to commit - // vendor and contrib directories. - // @todo Ignore it from rsync, but './.gitignore' didn't work. - $this->_exec("cd $pantheon_directory && git checkout .gitignore"); - - // Also we need to clean up gitignores that are deeper in the tree, - // those can be troublemakers too, it also purges various Git helper - // files that are irrelevant here. - $this->_exec("cd $pantheon_directory && (find . | grep \"\.git\" | grep -v \"^./.git\" | xargs rm -rf || true)"); - - $this->_exec("cd $pantheon_directory && git status"); - - $commit_and_deploy_confirm = $this->confirm('Commit changes and deploy?', TRUE); - if (!$commit_and_deploy_confirm) { - $this->say('Aborted commit and deploy, you can do it manually'); - - // The Pantheon repo is dirty, so check if we want to clean it up before - // exit. - $cleanup_pantheon_directory_confirm = $this->confirm("Revert any changes on $pantheon_directory directory (i.e. `git checkout .`)?"); - if (!$cleanup_pantheon_directory_confirm) { - // Keep folder as is. - return; - } - - // We repeat "git clean" twice, as sometimes it seems that a single one - // doesn't remove all directories. - $this->_exec("cd $pantheon_directory && git checkout . && git clean -fd && git clean -fd && git status"); - - return; - } - - if (empty($commit_message)) { - $tag = $this->taskExec("git tag --points-at HEAD") - ->printOutput(FALSE) - ->run() - ->getMessage(); - if (empty($tag)) { - $commit_message = 'Site update from ' . $current_version; - } - else { - $commit_message = "Site update from $tag ($current_version)"; - } - } - $commit_message = escapeshellarg($commit_message); - $result = $this->taskExec("cd $pantheon_directory && git pull && git add . && git commit -am $commit_message && git push") - ->printOutput(FALSE) - ->run(); - if (str_contains($result->getMessage(), 'nothing to commit, working tree clean')) { - $this->say('Nothing to commit, working tree clean'); - } - print $result->getMessage(); - - if ($result->getExitCode() !== 0) { - throw new \Exception('Pushing to the remote repository failed'); - } - - // Let's wait until the code is deployed to the environment. - // This "git push" above is as async operation, so prevent invoking - // for instance drush cim before the new changes are there. - usleep(self::$deploymentWaitTime); - $pantheon_env = $branch_name == 'master' ? 'dev' : $branch_name; - - $this->waitForCodeDeploy($pantheon_env); - $this->deployPantheonSync($pantheon_env, FALSE); - } - - /** - * Waits until no code sync is running. - * - * @param string $env - * The environment to wait for. - */ - protected function waitForCodeDeploy(string $env) { - $pantheon_info = $this->getPantheonNameAndEnv(); - $attempt = 0; - $code_sync_completed = FALSE; - do { - $attempt++; - $result = $this->taskExec("terminus workflow:list " . $pantheon_info['name'] . " --format=json --fields=env,status,workflow")->printOutput(FALSE)->run(); - if ($result->getExitCode() !== 0) { - $this->yell('Getting workflow list failed'); - continue; - } - $result = json_decode($result->getMessage(), TRUE); - $workflows = array_filter($result, function ($workflow) use ($env) { - // When there's a git push, there's a "Sync code" workflow that - // needs to be completed, before we can rely on the code being - // present. - return $workflow['env'] == $env && $workflow['status'] == 'running' && str_contains($workflow['workflow'], 'Sync ') !== FALSE; - }); - $this->say(print_r($workflows, TRUE)); - $code_sync_completed = empty($workflows); - usleep(self::$deploymentWaitTime); - } while (!$code_sync_completed && $attempt < self::$codeSyncWaitMaxRetries); - } - - /** - * Get the Pantheon name and environment. - * - * @return array - * Array keyed by `name` and `env`. - * - * @throws \Exception - */ - protected function getPantheonNameAndEnv() : array { - $yaml = Yaml::parseFile('./.ddev/providers/pantheon.yaml'); - if (empty($yaml['environment_variables']['project'])) { - throw new \Exception("`environment_variables.project` is missing from .ddev/providers/pantheon.yaml"); - } - - $project = explode('.', $yaml['environment_variables']['project'], 2); - if (count($project) !== 2) { - throw new \Exception("`environment_variables.project` should be in the format of `yourproject.dev`"); - } - - return [ - 'name' => $project[0], - 'env' => $project[1], - ]; - - } - - /** - * Deploy site from one env to the other on Pantheon. - * - * @param string $env - * The environment to update. - * @param bool $do_deploy - * Determine if 'terminus env:deploy' should be run on the given env. - * - * @throws \Exception - */ - public function deployPantheonSync(string $env = 'test', bool $do_deploy = TRUE): void { - $pantheon_info = $this->getPantheonNameAndEnv(); - $pantheon_terminus_environment = $pantheon_info['name'] . '.' . $env; - - $task = $this->taskExecStack() - ->stopOnFail(); - - if ($do_deploy) { - $task->exec("terminus env:deploy $pantheon_terminus_environment"); - } - - $result = $task - ->exec("terminus remote:drush $pantheon_terminus_environment -- updb --no-interaction") - ->exec("terminus remote:drush $pantheon_terminus_environment -- cr") - // A repeat config import may be required. Run it in any case. - ->exec("terminus remote:drush $pantheon_terminus_environment -- cim --no-interaction") - ->exec("terminus remote:drush $pantheon_terminus_environment -- cim --no-interaction") - ->exec("terminus remote:drush $pantheon_terminus_environment -- cr") - ->run() - ->getExitCode(); - if ($result !== 0) { - $message = "The site could not be fully updated at Pantheon at $env. Try fixing manually."; - $this->deployNotify($env, $message); - throw new \Exception($message); - } - - $result = $this->localeImport(FALSE, $env); - if ($result->getExitCode() !== 0) { - $message = "The deployment went well to $env, but the locale import failed. Try to perform manually later."; - $this->deployNotify($env, $message); - throw new \Exception($message); - } - - $result = $this->taskExecStack() - ->stopOnFail() - ->exec("terminus remote:drush $pantheon_terminus_environment -- sapi-r") - ->exec("terminus remote:drush $pantheon_terminus_environment -- sapi-i") - ->run() - ->getExitCode(); - - if ($result !== 0) { - $message = "The deployment went well to $env, but the re-indexing to ElasticSearch failed. Try to perform manually later."; - $this->deployNotify($env, $message); - throw new \Exception($message); - } - - $result = $this->taskExecStack() - ->stopOnFail() - ->exec("terminus remote:drush $pantheon_terminus_environment -- uli") - ->run() - ->getExitCode(); - - if ($result !== 0) { - $message = "Could not generate a login link at $env. Try again manually or check earlier errors."; - $this->deployNotify($env, $message); - throw new \Exception($message); - } - - try { - $this->deployCheckRequirementErrors($env); - } - catch (\Exception $e) { - $message = "The deployment went well to $env, but there are requirement errors. Address these:" . PHP_EOL . $e->getMessage(); - $this->deployNotify($env, $message); - throw new \Exception($message); - } - } - - /** - * Check for requirement errors on the given environment. - * - * @param string $environment - * The environment to check. - * - * @throws \Exception - */ - public function deployCheckRequirementErrors(string $environment): void { - $pantheon_info = $this->getPantheonNameAndEnv(); - $pantheon_terminus_environment = $pantheon_info['name'] . '.' . $environment; - $task = $this->taskExecStack() - ->stopOnFail(); - $output = $task - ->exec("terminus remote:drush $pantheon_terminus_environment -- rq --format=csv") - ->printOutput(FALSE) - ->run() - ->getMessage(); - - $errors = []; - $parsed_output = str_getcsv($output, "\n"); - if (empty($parsed_output)) { - return; - } - - $exclude = (string) getenv('DEPLOY_EXCLUDE_WARNING'); - $exclude_list = explode('|', $exclude); - - foreach ($parsed_output as $row) { - $row = str_getcsv($row, ","); - if (empty($row[0])) { - continue; - } - if (empty($row[1])) { - continue; - } - if ($row[1] !== 'Error') { - continue; - } - if (in_array($row[0], $exclude_list)) { - continue; - } - $errors[] = $row[2]; - } - if (empty($errors)) { - return; - } - throw new \Exception(print_r($errors, TRUE)); - } - - /** - * Install the site on specific env on Pantheon from scratch. - * - * Running this command via `ddev` will require terminus login inside ddev: - * `ddev auth ssh`. - * - * @param string $env - * The environment to install. - * @param string $pantheon_name - * The Pantheon site name. - * - * @throws \Exception - */ - public function deployPantheonInstallEnv(string $env = 'qa', string $pantheon_name = NULL): void { - $forbidden_envs = [ - 'live', - ]; - if (in_array($env, $forbidden_envs)) { - throw new \Exception("Reinstalling the site on `$env` environment is forbidden."); - } - - if ($pantheon_name === NULL) { - $pantheon_info = $this->getPantheonNameAndEnv(); - $pantheon_terminus_environment = $pantheon_info['name'] . '.' . $env; - } - else { - $pantheon_terminus_environment = $pantheon_name . '.' . $env; - } - - // This set of commands should work, so expecting no failures - // (tend to invoke the same flow as DDEV's `config.local.yaml`). - $task = $this - ->taskExecStack() - ->stopOnFail(); - - $task - ->exec("terminus remote:drush $pantheon_terminus_environment -- si server --no-interaction --existing-config") - ->exec("terminus remote:drush $pantheon_terminus_environment -- pm-enable default_content --no-interaction") - ->exec("terminus remote:drush $pantheon_terminus_environment -- pm-enable server_default_content --no-interaction") - ->exec("terminus remote:drush $pantheon_terminus_environment -- pm:uninstall server_default_content default_content --no-interaction") - ->exec("terminus remote:drush $pantheon_terminus_environment -- set-homepage") - ->exec("terminus remote:drush $pantheon_terminus_environment -- uli"); - - $result = $task->run()->getExitCode(); - - if ($result !== 0) { - throw new \Exception("The site failed to install on Pantheon's `$env` environment."); - } - } - - /** - * Prepares the repository to perform automatic deployment to Pantheon. - * - * @param string $token - * Terminus machine token: https://pantheon.io/docs/machine-tokens. - * @param string $github_token - * Personal GitHub token (Travis auth): - * https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/creating-a-personal-access-token. - * @param string $github_deploy_branch - * The branch that should be pushed automatically to Pantheon. By default, - * it's 'main', the default GitHub branch for any new project. - * @param string $pantheon_deploy_branch - * The branch at the artifact repo that should be the target of the - * deployment. As we typically deploy to QA, the default value here is 'qa', - * that multi-dev environment should be created by hand beforehand. - * - * @throws \Exception - */ - public function deployConfigAutodeploy(string $token, string $github_token, string $github_deploy_branch = 'main', string $pantheon_deploy_branch = 'qa'): void { - $this->_exec("cp .travis.template.yml .travis.yml"); - $pantheon_info = $this->getPantheonNameAndEnv(); - $project_name = $pantheon_info['name']; - - if (empty(shell_exec("which travis"))) { - // We do not bake it into the Docker image to save on disk space. - // We rarely need this operation, also not all the developers - // will use it. - $result = $this->taskExecStack() - ->exec('sudo apt update') - ->exec('sudo apt install ruby ruby-dev make g++ --yes') - ->exec('sudo gem install travis --no-document') - ->stopOnFail() - ->run() - ->getExitCode(); - - if ($result !== 0) { - throw new \Exception('The installation of the dependencies failed.'); - } - } - - $result = $this->taskExec('ssh-keygen -f travis-key -P ""')->run(); - if ($result->getExitCode() !== 0) { - throw new \Exception('The key generation failed.'); - } - - $result = $this->taskExec('travis login --pro --github-token="' . $github_token . '"')->run(); - if ($result->getExitCode() !== 0) { - throw new \Exception('The authentication with GitHub via Travis CLI failed.'); - } - - $result = $this->taskExec('travis encrypt-file travis-key --add --no-interactive --pro') - ->run(); - if ($result->getExitCode() !== 0) { - throw new \Exception('The encryption of the private key failed.'); - } - - $result = $this->taskExec('travis encrypt TERMINUS_TOKEN="' . $token . '" --add --no-interactive --pro') - ->run(); - if ($result->getExitCode() !== 0) { - throw new \Exception('The encryption of the Terminus token failed.'); - } - - $result = $this->taskExec("terminus connection:info $project_name.dev --fields='Git Command' --format=string | awk '{print $3}'") - ->printOutput(FALSE) - ->run(); - $pantheon_git_url = trim($result->getMessage()); - $this->taskReplaceInFile('.travis.yml') - ->from('{{ PANTHEON_GIT_URL }}') - ->to($pantheon_git_url) - ->run(); - $this->taskReplaceInFile('.travis.yml') - ->from('{{ PANTHEON_DEPLOY_BRANCH }}') - ->to($pantheon_deploy_branch) - ->run(); - $this->taskReplaceInFile('.travis.yml') - ->from('{{ GITHUB_DEPLOY_BRANCH }}') - ->to($github_deploy_branch) - ->run(); - - $result = $this->taskExec('git add .travis.yml travis-key.enc')->run(); - if ($result->getExitCode() !== 0) { - throw new \Exception("git add failed."); - } - $this->say("The project was prepared for the automatic deployment to Pantheon"); - $this->say("Review the changes and make a commit from the added files."); - $this->say("Add the SSH key to the Pantheon account: https://pantheon.io/docs/ssh-keys ."); - $this->say("Add the SSH key to the GitHub project as a deploy key: https://docs.github.com/en/developers/overview/managing-deploy-keys ."); - $this->say("Convert the project to nested docroot: https://pantheon.io/docs/nested-docroot ."); - } - - /** - * Posts a comment on the GitHub issue that the code got deployed to Pantheon. - * - * @param string $pantheon_environment - * The Pantheon environment where the code was deployed. - * @param string $issue_comment - * The comment to post on the issue. - */ - public function deployNotify(string $pantheon_environment = 'qa', string $issue_comment = '') { - if (!empty($issue_comment)) { - $data = ['body' => $issue_comment]; - $issue_comment = json_encode($data); - } - $github_token = getenv('GITHUB_TOKEN'); - $git_commit_message = getenv('TRAVIS_COMMIT_MESSAGE'); - if (strstr($git_commit_message, 'Merge pull request') === FALSE && strstr($git_commit_message, ' (#') === FALSE) { - $this->say($git_commit_message); - return; - } - - $issue_matches = []; - $issue_numbers = []; - // If the PR was simply merged, then we have this: - preg_match_all('!from [a-zA-Z-_0-9]+/([0-9]+)!', $git_commit_message, $issue_matches); - if (!isset($issue_matches[1][0])) { - $this->say("Could not determine the issue number from the commit message name: $git_commit_message"); - - // If the PR was merged with a squash, then we have this: - // blah blah (#1234) - // Where 1234 is the PR number. - $pr_matches = []; - preg_match_all('!\(#([0-9]+)\)!', $git_commit_message, $pr_matches); - if (!isset($pr_matches[0][0])) { - $this->say("Could not determine the PR number from the commit message: $git_commit_message"); - return; - } - // Retrieve the issue number from the PR description via GitHub API. - $pr_number = $pr_matches[1][0]; - $pr = $this->taskExec("curl -H \"Authorization: token $github_token\" https://api.github.com/repos/" . self::$githubProject . "/pulls/$pr_number") - ->printOutput(FALSE) - ->run() - ->getMessage(); - $pr = json_decode($pr); - if (!isset($pr->body)) { - $this->say("Could not determine the issue number from the PR: $git_commit_message"); - return; - } - // The issue number should be the "#1234"-like reference in the PR body. - preg_match_all('!#([0-9]+)\s+!', $pr->body, $issue_matches); - if (!isset($issue_matches[1][0])) { - $this->say("Could not determine the issue number from the PR description: $pr->body"); - return; - } - foreach ($issue_matches[1] as $issue_match) { - if (!is_numeric($issue_match)) { - continue; - } - $issue_numbers[] = $issue_match; - } - } - else { - $issue_numbers[] = $issue_matches[1][0]; - if (!is_numeric($issue_numbers[0])) { - throw new \Exception("Could not determine the issue number from the branch name in the commit message: $git_commit_message"); - } - } - - if (empty($issue_numbers)) { - $this->say("Giving up, no notification sent to GitHub"); - return; - } - - $pantheon_info = $this->getPantheonNameAndEnv(); - // Let's figure out if the repository is public or not via GitHub API. - $repo = $this->taskExec("curl -H \"Authorization: token $github_token\" https://api.github.com/repos/" . self::$githubProject) - ->printOutput(FALSE) - ->run() - ->getMessage(); - $repo = json_decode($repo); - if (!isset($repo->private)) { - $this->yell("Could not determine if the repository is private or not."); - return; - } - if ($repo->private) { - // If the repository is private, we can put a login link into the comment. - $quick_link = $this->taskExec("terminus remote:drush " . $pantheon_info['name'] . "." . $pantheon_environment . " uli") - ->printOutput(FALSE) - ->run() - ->getMessage(); - } - else { - // Otherwise, just link the environment. - $quick_link = "https://" . $pantheon_environment . "-" . $pantheon_info['name'] . ".pantheonsite.io"; - } - - if (empty($issue_comment)) { - if (empty($pr_number)) { - $issue_comment = "{\"body\": \"The latest merged PR just got deployed successfully to Pantheon [`$pantheon_environment`]($quick_link) environment\"}"; - } - else { - $issue_comment = "{\"body\": \"The latest merged PR #$pr_number just got deployed successfully to Pantheon [`$pantheon_environment`]($quick_link) environment\"}"; - } - } - foreach ($issue_numbers as $issue_number) { - $result = $this->taskExec("curl -X POST -H 'Authorization: token $github_token' -d '$issue_comment' https://api.github.com/repos/" . self::$githubProject . "/issues/$issue_number/comments") - ->printOutput(FALSE) - ->run(); - $exit_code = $result->getExitCode(); - if ($exit_code) { - throw new \Exception("Could not notify GitHub of the deployment, GitHub API error: " . $result->getMessage()); - } - } - } - -} diff --git a/robo-components/ElasticSearchTrait.php b/robo-components/ElasticSearchTrait.php deleted file mode 100644 index 550db6140..000000000 --- a/robo-components/ElasticSearchTrait.php +++ /dev/null @@ -1,268 +0,0 @@ -taskExec("curl -u $username:$password $es_url/_security/user") - ->printOutput(FALSE) - ->run() - ->getMessage(), TRUE); - if (isset($result['error'])) { - throw new \Exception('Cannot connect to ES or security not enabled'); - } - foreach (array_keys($result) as $existing_username) { - foreach ($this->sites as $site) { - if (strstr($existing_username, $site) !== FALSE) { - // Users do exist with the site name. - $needs_users = FALSE; - break 2; - } - } - - } - } - - $index_creation = $this->taskParallelExec(); - $role_creation = $this->taskParallelExec(); - $user_creation = $this->taskParallelExec(); - $credentials = []; - if (!empty($environment)) { - $this->environments = [$environment]; - } - foreach ($this->environments as $environment) { - $environment = str_replace('-', '_', $environment); - foreach ($this->indices as $index) { - $index_creation->process("curl -u $username:$password -X PUT $es_url/" . self::$indexPrefix . "{$index}_$environment"); - } - foreach ($this->sites as $site) { - if (!isset($credentials[$site])) { - $credentials[$site] = []; - } - if (!isset($credentials[$site][$environment])) { - $credentials[$site][$environment] = []; - } - $allowed_indices = []; - foreach ($this->indices as $index) { - if (strstr($index, $site) !== FALSE) { - $allowed_indices[] = '"' . self::$indexPrefix . $index . '_' . $environment . '"'; - } - } - $allowed_indices = implode(',', $allowed_indices); - - $role_data = <<process("curl -u $username:$password -X POST $es_url/_security/role/${site}_$environment -H 'Content-Type: application/json' --data '$role_data'"); - - // Generate random password or re-use an existing one from the JSON. - $existing_password = $this->getUserPassword($site, $environment); - $user_pw = !empty($existing_password) ? $existing_password : $this->randomStr(); - $user_data = <<process("curl -u $username:$password -X POST $es_url/_security/user/{$site}_$environment -H 'Content-Type: application/json' --data '$user_data'"); - } - - } - - $index_creation->run(); - if ($needs_users || $needs_users_override) { - $role_creation->run(); - $user_creation->run(); - - // We expose the credentials as files on the system. - // Should be securely handled and deleted after the execution. - foreach ($credentials as $site => $credential_per_environment) { - file_put_contents($site . '.es.secrets.json', json_encode($credential_per_environment)); - } - } - - $this->elasticsearchAnalyzer($es_url, $username, $password); - } - - /** - * Apply / actualize the default analyzer. - * - * @param string $es_url - * Fully qualified URL to ES, for example: http://elasticsearch:9200 . - * @param string $username - * The username of the ES admin user. - * @param string $password - * The password of the ES admin user. - * - * @throws \Exception - */ - public function elasticsearchAnalyzer(string $es_url, string $username = '', string $password = ''): void { - $analyzer_data = <<applyIndexSettings($es_url, $username, $password, $analyzer_data); - } - - /** - * Apply index configuration snippet to all indices. - * - * @param string $es_url - * Fully qualified URL to ES, for example: http://elasticsearch:9200 . - * @param string $username - * The username of the ES admin user. - * @param string $password - * The password of the ES admin user. - * @param string $data - * The JSON snippet to apply. - */ - private function applyIndexSettings(string $es_url, string $username, string $password, string $data): void { - foreach ($this->environments as $environment) { - $environment = str_replace('-', '_', $environment); - foreach ($this->indices as $index) { - $this->taskExec("curl -u $username:$password -X POST $es_url/" . self::$indexPrefix . "{$index}_$environment/_close")->run(); - $this->taskExec("curl -u $username:$password -X PUT $es_url/" . self::$indexPrefix . "{$index}_$environment/_settings -H 'Content-Type: application/json' --data '$data'")->run(); - $this->taskExec("curl -u $username:$password -X POST $es_url/" . self::$indexPrefix . "{$index}_$environment/_open")->run(); - } - } - } - - /** - * Returns an already existing password for the given user. - * - * @param string $site - * The site ID. - * @param string $environment - * The environment ID. - * - * @return string|null - * The password of the user in ElasticSearch, if exists. - */ - protected function getUserPassword(string $site, string $environment): ?string { - $credentials_file = $site . '.es.secrets.json'; - if (!file_exists($credentials_file)) { - return NULL; - } - $credentials = file_get_contents($credentials_file); - if (empty($credentials)) { - return NULL; - } - $credentials = json_decode($credentials, TRUE); - if (!is_array($credentials)) { - return NULL; - } - if (!isset($credentials[$environment])) { - return NULL; - } - return $credentials[$environment]; - } - -} diff --git a/robo-components/ThemeTrait.php b/robo-components/ThemeTrait.php deleted file mode 100644 index 72a796d05..000000000 --- a/robo-components/ThemeTrait.php +++ /dev/null @@ -1,216 +0,0 @@ -_deleteDir(self::$themeBase . '/dist'); - foreach ($directories as $dir) { - $directory = self::$themeBase . '/dist/' . $dir; - $this->_mkdir($directory); - } - - $theme_dir = self::$themeBase; - - // Make sure we have all the node packages. - $this->_exec("cd $theme_dir && npm install"); - - $result = $this->_exec("cd $theme_dir && npx postcss ./src/pcss/style.pcss --output=./dist/css/style.css"); - - if ($result->getExitCode() !== 0) { - $this->taskCleanDir(['dist/css']); - return; - } - - // Javascript. - if ($optimize) { - // Minify the JS files. - foreach (glob(self::$themeBase . '/src/js/*.js') as $js_file) { - // Make the path relative to the theme root. - $from = str_replace('web/themes/custom/server_theme/', '', $js_file); - $to = str_replace('src/', 'dist/', $from); - // Minify the js. - $this->_exec("cd $theme_dir && npx minify $from > $to"); - } - } - else { - $this->_copyDir(self::$themeBase . '/src/js', self::$themeBase . '/dist/js'); - } - - // Images - Copy everything first. - $this->_copyDir(self::$themeBase . '/src/images', self::$themeBase . '/dist/images'); - - // Then for the formats that we can optimize, perform it. - if ($optimize) { - $input = [ - self::$themeBase . '/src/images/*.jpg', - self::$themeBase . '/src/images/*.png', - ]; - - $this->taskImageMinify($input) - ->to(self::$themeBase . '/dist/images/') - ->run(); - - // Compress all SVGs. - $this->themeSvgCompress(); - } - - $this->_exec('drush cache:rebuild'); - } - - /** - * Compile the theme (optimized). - */ - public function themeCompile(): void { - $this->say('Compiling (optimized).'); - $this->doThemeCompile(TRUE); - } - - /** - * Compile the theme. - * - * Non-optimized. - */ - public function themeCompileDebug(): void { - $this->say('Compiling (non-optimized).'); - $this->doThemeCompile(); - } - - /** - * Compress SVG files in the "dist" directories. - * - * This function is being called as part of `theme:compile`. - * - * @return \Robo\ResultData|null - * If there was an error a result data object is returned. Or void if - * successful. - * - * @see doThemeCompile() - */ - public function themeSvgCompress(): ?ResultData { - $directories = [ - './dist/images', - ]; - - $error_code = NULL; - - foreach ($directories as $directory) { - // Check if SVG files exists in this directory. - $finder = new Finder(); - $finder - ->in(self::$themeBase . '/' . $directory) - ->files() - ->name('*.svg'); - - if (!$finder->hasResults()) { - // No SVG files. - continue; - } - - $result = $this->_exec("cd " . self::$themeBase . " && npx svgo $directory/*.svg"); - if (empty($error_code) && !$result->wasSuccessful()) { - $error_code = $result->getExitCode(); - } - } - - if (!empty($error_code)) { - return new ResultData($error_code, '`svgo` failed to run.'); - } - return NULL; - } - - /** - * Directories that should be watched for the theme. - * - * @return array - * List of directories. - */ - protected function monitoredThemeDirectories(): array { - return [ - self::$themeBase . '/src', - ]; - } - - /** - * Watch the theme and compile on change (optimized). - */ - public function themeWatch(): void { - $this->say('Compiling and watching (optimized).'); - $this->doThemeCompile(TRUE); - foreach ($this->monitoredThemeDirectories() as $directory) { - $this->taskWatch() - ->monitor( - $directory, - function () { - $this->doThemeCompile(TRUE); - }, - FilesystemEvent::ALL - )->run(); - } - } - - /** - * Watch the theme path and compile on change (non-optimized). - */ - public function themeWatchDebug(): void { - $this->say('Compiling and watching (non-optimized).'); - $this->doThemeCompile(); - foreach ($this->monitoredThemeDirectories() as $directory) { - $this->taskWatch() - ->monitor( - $directory, - function () { - $this->doThemeCompile(); - }, - FilesystemEvent::ALL - )->run(); - } - } - - /** - * Update the caniuse-lite browserslist db. - * - * Any changes made as a result of this command should be committed. - * - * @return \Robo\ResultData - * The result. - */ - public function caniuseUpdatedb(): ResultData { - return $this->_exec('cd ' . self::$themeBase . ' && npx browserslist@latest --update-db'); - } - -} diff --git a/robo-components/TranslationManagement/ExportFromConfig.php b/robo-components/TranslationManagement/ExportFromConfig.php deleted file mode 100644 index d958dfb53..000000000 --- a/robo-components/TranslationManagement/ExportFromConfig.php +++ /dev/null @@ -1,155 +0,0 @@ -setLanguage($langcode); - $translations->getHeaders() - ->set('MIME-Version', '1.0') - ->set('Content-Type', 'text/plain; charset=utf-8') - ->set('Content-Transfer-Encoding', '8bit') - ->set('Plural-Forms', 'nplurals=2; plural=(n > 1);'); - - // Create the Translation objects and add to the Translations wrapper. - foreach ($term_map[$langcode] as $term => $context) { - $translated = $translated_map[$langcode][$term] ?? $term; - $translation = Translation::create(implode(',', $context), $term); - $translation->translate($translated); - $translations->add($translation); - } - - // Create the file for the language. - $filename = "config/po_files/{$langcode}_config.po"; - $po_generator->generateFile($translations, $filename); - $this->say("$langcode - exported translatable configuration strings to $filename"); - } - } - - /** - * Gets the English version of a configuration key. - * - * @param string $name - * A config name. Example field.field.node.video_story.body . - * @param string $key - * The key to extract the value. display.page.display_options.menu.title . - * - * @return string - * Returns the English version of the $name:$key config. - */ - protected function configKeyUnstranslated(string $name, string $key) { - $configFactory = \Drupal::service('config.factory'); - $base_config = $configFactory->get($name); - return $base_config->get($key); - } - - /** - * Gets the translated version of a configuration key. - * - * @param string $name - * A config name. Example "field.field.node.video_story.body". - * @param string $key - * The key to extract the value. - * For example: "display.page.display_options.menu.title". - * @param string $langcode - * The langcode of the translation to fetch. For example: "zh-hans". - * - * @return string - * Returns the translated version of the $name:$key config in $langcode. - */ - protected function configKeyTranslated(string $name, string $key, string $langcode) { - /** @var \Drupal\language\ConfigurableLanguageManager $language_manager */ - $language_manager = \Drupal::service('language_manager'); - $config = $language_manager->getLanguageConfigOverride($langcode, $name); - return $config->get($key); - } - - /** - * Returns the translations of a config entity for the specified keys. - * - * @param string $prefix - * A prefix to load configs. Example 'field.field.node.'. - * @param array $keys - * An array of keys to extract the value. Example: ['label', 'menu.title']. - * @param string $langcode - * The langcode of the translations to fetch. - * - * @return array - * A list of original value and the ID. - */ - protected function listConfigs(string $prefix, array $keys, string $langcode) { - $configFactory = \Drupal::service('config.factory'); - $fields = $configFactory->listAll($prefix); - $rows = []; - foreach ($fields as $field_config) { - foreach ($keys as $key) { - $columns = []; - $columns[] = $field_config . ':' . $key; - $columns[] = self::configKeyUnstranslated($field_config, $key); - $columns[] = self::configKeyTranslated($field_config, $key, $langcode); - - $rows[] = $columns; - } - } - return $rows; - } - -} diff --git a/robo-components/TranslationManagement/ImportToConfig.php b/robo-components/TranslationManagement/ImportToConfig.php deleted file mode 100644 index d2bf7fdb5..000000000 --- a/robo-components/TranslationManagement/ImportToConfig.php +++ /dev/null @@ -1,84 +0,0 @@ -getContext()); - $original = $translation->getOriginal(); - $translated = $translation->getTranslation(); - - if (empty($translated) || $original === $translated) { - // If it's empty or same as original, don't bother importing. - continue; - } - - foreach ($contexts as $context) { - [$config, $key] = explode(':', $context); - - if (empty($config) || empty($key)) { - // Missing one of the essential data pieces. Skip. - continue; - } - - self::translateConfig(trim($config), trim($key), $langcode, trim($translated)); - $counter++; - } - } - $this->say("$langcode - imported $counter translations into configuration"); - } - } - - /** - * Translates a configuration key with a new translation. - * - * @param string $name - * A config name. Example field.field.node.video_story.body . - * @param string $key - * The key to extract the value. display.page.display_options.menu.title . - * @param string $langcode - * The langcode key. Example: 'fr'. - * @param string $translation - * The text to use as translation. - */ - protected function translateConfig($name, $key, $langcode, $translation) { - /** @var \Drupal\language\ConfigurableLanguageManager $languageManager */ - $languageManager = \Drupal::service('language_manager'); - $config_translation = $languageManager->getLanguageConfigOverride($langcode, $name); - $config_translation->set($key, $translation); - $config_translation->save(); - } - - /** - * Load the translations from a PO file as Gettext Translations object. - * - * @param string $langcode - * The langcode. - * - * @return \Gettext\Translations - * The translations. - */ - protected static function getTranslations(string $langcode): Translations { - $loader = new PoLoader(); - return $loader->loadFile("config/po_files/{$langcode}_config.po"); - } - -} diff --git a/robo-components/TranslationManagement/ImportToUi.php b/robo-components/TranslationManagement/ImportToUi.php deleted file mode 100644 index f62f9c29a..000000000 --- a/robo-components/TranslationManagement/ImportToUi.php +++ /dev/null @@ -1,48 +0,0 @@ -getPantheonNameAndEnv(); - $pantheon_terminus_environment = $pantheon_info['name'] . '.' . $env; - foreach (self::INSTALLED_LANGUAGES as $language) { - $commands[] = "terminus drush $pantheon_terminus_environment -- locale:import --override=not-customized $language ../config/po_files/$language.po"; - } - } - return $this->_exec(implode(' && ', $commands)); - } - -} From ee9ac7f44f94a887feed727dc616ed342e34ef14 Mon Sep 17 00:00:00 2001 From: Aron Novak Date: Fri, 22 Sep 2023 11:27:02 +0200 Subject: [PATCH 5/5] translation robo update --- composer.lock | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/composer.lock b/composer.lock index 897843173..f01558328 100644 --- a/composer.lock +++ b/composer.lock @@ -6086,16 +6086,16 @@ }, { "name": "gizra/robo-translation", - "version": "0.1.0", + "version": "0.1.1", "source": { "type": "git", "url": "https://github.com/Gizra/robo-translation.git", - "reference": "a4630db5617ebdcec01bf070b906ae49e6bf5f15" + "reference": "5593c7491f45ea0d61569b90300b7b05732c44a0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Gizra/robo-translation/zipball/a4630db5617ebdcec01bf070b906ae49e6bf5f15", - "reference": "a4630db5617ebdcec01bf070b906ae49e6bf5f15", + "url": "https://api.github.com/repos/Gizra/robo-translation/zipball/5593c7491f45ea0d61569b90300b7b05732c44a0", + "reference": "5593c7491f45ea0d61569b90300b7b05732c44a0", "shasum": "" }, "require": { @@ -6112,9 +6112,9 @@ "description": "PO-file based translation workflow Robo command as PHP trait", "support": { "issues": "https://github.com/Gizra/robo-translation/issues", - "source": "https://github.com/Gizra/robo-translation/tree/0.1.0" + "source": "https://github.com/Gizra/robo-translation/tree/0.1.1" }, - "time": "2023-09-22T08:53:41+00:00" + "time": "2023-09-22T09:23:38+00:00" }, { "name": "grasmash/expander",