Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ACMS-3507: Include changes for DRS from branch ACMS-3507 #33

Closed
wants to merge 17 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 13 additions & 8 deletions .github/workflows/drs_ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -47,29 +47,34 @@ jobs:
../orca/bin/ci/after_failure.sh
../orca/bin/ci/after_script.sh
PHPUNIT_TESTS:
name: "Execute PHPUnit tests"
name: "Run PHPUnit tests on PHP: ${{ matrix.php-version }}"
runs-on: ubuntu-latest
strategy:
matrix:
php-version:
- 8.1
- 8.2
- 8.3
env:
ORCA_FIXTURE_DIR: "/home/runner/work/drupal-recommended-settings/orca-build"
ORCA_JOB: ISOLATED_TEST_ON_CURRENT
CI: TRUE
steps:
- uses: actions/checkout@v3
- uses: shivammathur/setup-php@v2
with:
php-version: 8.2
php-version: ${{ matrix.php-version }}
coverage: xdebug
ini-file: development
- name: Download ORCA
run: composer create-project --no-dev --ignore-platform-req=php acquia/orca ../orca "$ORCA_VERSION" -n
- name: Before Install
run: ../orca/bin/ci/before_install.sh
- name: Install
run: ../orca/bin/ci/install.sh
- name: Before script
- name: Before Script
run: ../orca/bin/ci/before_script.sh
- name: Run PHPUnit tests
run: |
composer install
./vendor/bin/phpunit tests
- name: Script
run: ../orca/bin/ci/script.sh
- name: After script
run: |
../orca/bin/ci/after_success.sh
Expand Down
1 change: 1 addition & 0 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
"acquia/coding-standards": "^2.0",
"composer/composer": "^2.2",
"dealerdirect/phpcodesniffer-composer-installer": "^1.0.0",
"drush/drush": "^11.6 || ^12",
"ergebnis/composer-normalize": "^2.30.2",
"phpro/grumphp-shim": "^2.2",
"phpunit/phpunit": "^9 || ^10"
Expand Down
8 changes: 4 additions & 4 deletions examples/example-drush-command/composer.json
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
{
"name": "example/example-drush-command",
"description": "Example custom drush commands.",
"keywords": [
"name": "example/example-drush-command",
"description": "Example custom drush commands.",
"license": "GPL-2.0-or-later",
"keywords": [
"drush",
"drupal"
],
"license": "GPL-2.0-or-later",
"require": {
"acquia/drupal-recommended-settings": "*"
},
Expand Down
2 changes: 1 addition & 1 deletion phpcs.xml.dist
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,6 @@
<exclude-pattern>var/</exclude-pattern>
<exclude-pattern>vendor/</exclude-pattern>

<rule ref="AcquiaDrupalStrict"/>
<rule ref="AcquiaPHP"/>

</ruleset>
41 changes: 41 additions & 0 deletions phpunit.xml.dist
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
<?xml version="1.0" encoding="UTF-8"?>

<!-- https://phpunit.de/manual/current/en/appendixes.configuration.html -->
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="vendor/phpunit/phpunit/phpunit.xsd"
colors="true"
cacheResultFile="./var/phpunit/test-results"
bootstrap="vendor/autoload.php"
failOnWarning="true"
failOnRisky="true"
convertErrorsToExceptions="true"
convertNoticesToExceptions="true"
convertWarningsToExceptions="true"
convertDeprecationsToExceptions="true"
>

<php>
<ini name="error_reporting" value="-1"/>
<env name="COLUMNS" value="80" force="true" />
<!-- <env name="ORCA_FIXTURE_DIR" value="/Applications/MAMP/htdocs/acquia_cms" force="true" />-->
</php>

<testsuites>
<testsuite name="DRS">
<directory>tests</directory>
</testsuite>
</testsuites>

<coverage cacheDirectory="var/phpunit/coverage-cache"
processUncoveredFiles="true">
<include>
<directory suffix=".php">src</directory>
</include>
<!-- <report>-->
<!-- <html outputDirectory="coverage" lowUpperBound="50" highLowerBound="90"/>-->
<!-- </report>-->
</coverage>
<!-- <logging>-->
<!-- <testdoxHtml outputFile="testdox.html"/>-->
<!-- </logging>-->
</phpunit>
1 change: 0 additions & 1 deletion settings/site/default.local.settings.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
*/

use Acquia\Drupal\RecommendedSettings\Helpers\EnvironmentDetector;
use Drupal\Component\Assertion\Handle;

$db_name = '${drupal.db.database}';

Expand Down
264 changes: 264 additions & 0 deletions src/Common/Executor.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,264 @@
<?php

namespace Acquia\Drupal\RecommendedSettings\Common;

use Acquia\Drupal\RecommendedSettings\Exceptions\SettingsException;
use Acquia\Drupal\RecommendedSettings\Robo\Config\ConfigAwareTrait;
use GuzzleHttp\Client;
use Psr\Log\LoggerAwareInterface;
use Psr\Log\LoggerAwareTrait;
use Robo\Collection\CollectionBuilder;
use Robo\Common\ProcessExecutor;
use Robo\Contract\ConfigAwareInterface;
use Robo\Robo;
use Robo\Task\Base\Exec;
use Symfony\Component\Process\Process;

/**
* A class for executing commands.
*
* This allows non-Robo-command classes to execute commands easily.
*/
class Executor implements ConfigAwareInterface, LoggerAwareInterface {

use ConfigAwareTrait;
use IO;
use LoggerAwareTrait;

/**
* A copy of the Robo builder.
*/
protected CollectionBuilder $builder;

/**
* Executor constructor.
*
* @param \Robo\Collection\CollectionBuilder $builder
* This is a copy of the collection builder, required for calling various
* Robo tasks from non-command files.
*/
public function __construct(CollectionBuilder $builder) {
$this->builder = $builder;
}

/**
* Returns $this->builder.
*
* @return \Robo\Collection\CollectionBuilder
* The builder.
*/
public function getBuilder(): CollectionBuilder {
return $this->builder;
}

/**
* Wrapper for taskExec().
*
* @param string $command
* The command string|array.
* Warning: symfony/process 5.x expects an array.
*
* @return \Robo\Task\Base\Exec
* The task. You must call run() on this to execute it!
*/
public function taskExec(string $command): Exec {
return $this->builder->taskExec($command);
}

/**
* Executes a drush command.
*
* @param mixed $command
* The command to execute, without "drush" prefix.
*
* @return \Robo\Common\ProcessExecutor
* The unexecuted process.
*/
public function drush(mixed $command): ProcessExecutor {
$drush_array = [];
// @todo Set to silent if verbosity is less than very verbose.
$drush_array[] = $this->getConfigValue('composer.bin') . DIRECTORY_SEPARATOR . "drush";
$drush_array[] = "@" . $this->getConfigValue('drush.alias');

// URIs do not work on remote drush aliases in Drush 9. Instead, it is
// expected that the alias define the uri in its configuration.
if ($this->getConfigValue('site') !== 'default') {
$drush_array[] = '--uri=' . $this->getConfigValue('drush.uri');
}

if (is_array($command)) {
$command_array = array_merge($drush_array, $command);
$this->logger->info("Running command " . implode(" ", $command_array));
$process_executor = $this->execute($command_array);
}
else {
$drush_string = implode (" ", $drush_array);
$this->logger->info("$drush_string $command");
$process_executor = $this->executeShell("$drush_string $command");
}
return $process_executor;
}

/**
* Executes a command.
*
* @param mixed $command
* The command string|array.
* Warning: symfony/process 5.x expects an array.
*
* @return \Robo\Common\ProcessExecutor
* The unexecuted command.
*/
public function execute(mixed $command): ProcessExecutor {
// Backwards compatibility check for legacy commands.
if (!is_array($command)) {
$this->say($command);
$this->say(StringManipulator::stringToArrayMsg());
$command = StringManipulator::commandConvert($command);
}
/** @var \Robo\Common\ProcessExecutor $process_executor */
$process_executor = Robo::process(new Process($command));
return $process_executor->dir($this->getConfigValue('repo.root'))
->printOutput(FALSE)
->printMetadata(FALSE)
->interactive(FALSE);
}

/**
* Executes a shell command.
*
* @param string $command
* The shell command string.
*
* @return \Robo\Common\ProcessExecutor
* The unexecuted command.
*/
public function executeShell(string $command): ProcessExecutor {
$process_executor = Robo::process(Process::fromShellCommandline($command));
return $process_executor->dir($this->getConfigValue('repo.root'))
->printOutput(FALSE)
->printMetadata(FALSE)
->interactive(FALSE);
}

/**
* Kills all system processes that are using a particular port.
*
* @param string $port
* The port number.
*/
public function killProcessByPort(string $port): void {
$this->logger->info("Killing all processes on port '$port'...");
// This is allowed to fail.
// @todo Replace with standardized call to Symfony Process.
// phpcs:ignore
exec("command -v lsof && lsof -ti tcp:$port | xargs kill l 2>&1");
// phpcs:ignore
exec("pkill -f $port 2>&1");
}

/**
* Kills all system processes containing a particular string.
*
* @param string $name
* The name of the process.
*/
public function killProcessByName(string $name): void {
$this->logger->info("Killing all processing containing string '$name'...");
// This is allowed to fail.
// @todo Replace with standardized call to Symfony Process.
// phpcs:ignore
exec("ps aux | grep -i $name | grep -v grep | awk '{print $2}' | xargs kill -9 2>&1");
// exec("ps aux | awk '/$name/ {print $2}' 2>&1 | xargs kill -9");.
}

/**
* Waits until a given URL responds with a non-50x response.
*
* This does have a maximum timeout, defined in wait().
*
* @param string $url
* The URL to wait for.
*/
public function waitForUrlAvailable(string $url): void {
$this->wait([$this, 'checkUrl'], [$url], "Waiting for non-50x response from $url...");
}

/**
* Waits until a given callable returns TRUE.
*
* This does have a maximum timeout.
*
* @param callable $callable
* The method/function to wait for a TRUE response from.
* @param string[] $args
* Arguments to pass to $callable.
* @param string $message
* The message to display when this function is called.
*
* @return bool
* TRUE if callable returns TRUE.
*
* @throws \Exception
*/
public function wait(callable $callable, array $args, string $message = ''): bool {
$maxWait = 60 * 1000;
$checkEvery = 1 * 1000;
$start = microtime(TRUE) * 1000;
$end = $start + $maxWait;

if (!$message) {
$method_name = is_array($callable) ? $callable[1] : $callable;
$message = "Waiting for $method_name() to return true.";
}

// For some reason we can't reuse $start here.
while (microtime(TRUE) * 1000 < $end) {
$this->logger->info($message);
try {
if (call_user_func_array($callable, $args)) {
return TRUE;
}
}
catch (\Exception $e) {
$this->logger->error($e->getMessage());
}
usleep($checkEvery * 1000);
}

throw new SettingsException("Timed out.");
}

/**
* Checks a URL for a non-50x response.
*
* @param string $url
* The URL to check.
*
* @return bool
* TRUE if URL responded with a non-50x response.
*/
public function checkUrl(string $url): bool {
try {
$client = new Client();
$res = $client->request('GET', $url, [
'connection_timeout' => 2,
'timeout' => 2,
'http_errors' => FALSE,
]);
if ($res->getStatusCode() && substr($res->getStatusCode(), 0, 1) != '5') {
return TRUE;
}
else {
$this->logger->info("Response code: " . $res->getStatusCode());
$this->logger->debug($res->getBody());
return FALSE;
}
}
catch (\Exception $e) {
$this->logger->debug($e->getMessage());
}
return FALSE;
}

}
Loading
Loading