From 7eca6af4f3fd905233a649acd602670db7fc993c Mon Sep 17 00:00:00 2001 From: apotek <279278+apotek@users.noreply.github.com> Date: Fri, 15 Sep 2023 19:30:14 -0400 Subject: [PATCH 01/10] Provide task for looking up alternative binaries And use this task to allow us to context-sensitively switch between mysql and mariadb depending on what is available on the image. --- .gitignore | 1 + RoboFile.php | 1 + .../Commands/DevelopmentModeBaseCommands.php | 38 ++++++--- src/Robo/Task/Discovery/Alternatives.php | 80 +++++++++++++++++++ src/Robo/Task/Discovery/Tasks.php | 16 ++++ 5 files changed, 123 insertions(+), 13 deletions(-) create mode 100644 src/Robo/Task/Discovery/Alternatives.php create mode 100644 src/Robo/Task/Discovery/Tasks.php diff --git a/.gitignore b/.gitignore index fa36fe5c..c09cf8b4 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ vendor composer.lock .idea +.DS_Store diff --git a/RoboFile.php b/RoboFile.php index f0b7919e..4703986d 100644 --- a/RoboFile.php +++ b/RoboFile.php @@ -12,4 +12,5 @@ * Robo commands available to developers. */ class RoboFile extends Tasks { + use Usher\Robo\Task\Discovery\Tasks; } diff --git a/src/Robo/Plugin/Commands/DevelopmentModeBaseCommands.php b/src/Robo/Plugin/Commands/DevelopmentModeBaseCommands.php index 955f9d2e..46be9091 100644 --- a/src/Robo/Plugin/Commands/DevelopmentModeBaseCommands.php +++ b/src/Robo/Plugin/Commands/DevelopmentModeBaseCommands.php @@ -5,14 +5,14 @@ use DrupalFinder\DrupalFinder; use Robo\Exception\TaskException; use Robo\Result; -use Robo\Robo; +use Robo\ResultData; use Robo\Tasks; -use Symfony\Component\Yaml\Exception\ParseException; use Symfony\Component\Yaml\Yaml; use Usher\Robo\Plugin\Enums\LocalDevEnvironmentTypes; use Usher\Robo\Plugin\Traits\DatabaseDownloadTrait; use Usher\Robo\Plugin\Traits\DrupalVersionTrait; use Usher\Robo\Plugin\Traits\SitesConfigTrait; +use Usher\Robo\Task\Discovery\Alternatives; /** * Robo commands related to changing development modes. @@ -123,43 +123,55 @@ public function databaseRefreshLando(string $siteName = 'default'): Result /** * Refresh database on Tugboat. */ - public function databaseRefreshTugboat(): Result + public function databaseRefreshTugboat(): ResultData { $this->io()->title('refresh tugboat databases.'); - $result = null; + $result_data = new ResultData(); + foreach (array_keys($this->getAllSitesConfig()) as $siteName) { try { $dbPath = $this->databaseDownload($siteName); - } catch (TaskException) { - $this->yell("$siteName: No database configured. Download/import skipped."); + } catch (TaskException $te) { + $result_data->append($te->getMessage()); // @todo: Should we run a site-install by default? continue; } - if (!is_string($dbPath) || strlen($dbPath) == 0) { + if (!isset($dbPath) || !is_string($dbPath) || strlen($dbPath) == 0) { $this->yell("'$siteName' database path not found."); + $result_data->append("'$siteName' database path not found."); continue; } $dbName = $siteName === 'default' ? 'tugboat' : $siteName; - $result = $this->taskExec('mysql') + $taskResult = $this->task(Alternatives::class, 'mariadb', 'mysql')->run(); + if (!$taskResult->wasSuccessful()) { + $result_data->append($taskResult); + continue; + } + $dbDriver = $taskResult->getData()['path']; + $taskResult = $this->taskExec($dbDriver) ->option('-h', 'mariadb') ->option('-u', 'tugboat') ->option('-ptugboat') ->option('-e', "drop database if exists $dbName; create database $dbName;") ->run(); - + $result_data->append($taskResult); $this->io()->section("import $siteName database."); - $result = $this->taskExec("zcat $dbPath | mysql -h mariadb -u tugboat -ptugboat $dbName") + $taskResult = $this->taskExec("zcat $dbPath | $dbDriver -h mariadb -u tugboat -ptugboat $dbName") ->run(); - $result = $this->taskExec('rm')->args($dbPath)->run(); + $result_data->append($taskResult); + $taskResult = $this->taskExec('rm')->args($dbPath)->run(); + $result_data->append($taskResult); if (!$this->drupalVersionIsD7($this->drupalRoot)) { - $result = $this->taskExec("$this->vendorDirectory/bin/drush") + $taskResult = $this->taskExec("$this->vendorDirectory/bin/drush") ->arg('cache:rebuild') ->dir("$this->drupalRoot/sites/$siteName") ->run(); + $result_data->append($taskResult); } } - return $result; + + return $result_data; } /** diff --git a/src/Robo/Task/Discovery/Alternatives.php b/src/Robo/Task/Discovery/Alternatives.php new file mode 100644 index 00000000..cf5bcf13 --- /dev/null +++ b/src/Robo/Task/Discovery/Alternatives.php @@ -0,0 +1,80 @@ +task(Alternatives::class, 'more', 'less,cat')->run(); + * $binary = $result->getData()['path'] + * ?> + * ``` + */ +class Alternatives extends BaseTask +{ + /** + * @var string + */ + protected string $alternatives; + + /** + * @var string + */ + protected string $command; + + /** + * @var int + */ + protected const SHELL_SUCCESS = 0; + + /** + * Alternatives constructor. + * + * @param string $command + * The preferred binary or path to binary to execute. + * @param string $alternatives + * A comma-separated list of alternative binaries or paths to binaries to + * execute. + */ + public function __construct(string $command, string $alternatives) + { + $this->command = $command; + $this->alternatives = $alternatives; + } + + /** + * {@inheritdoc} + * + * The array returned by Result::getData() will always contain a 'path' + * property, even if empty. + */ + public function run(): Result + { + $this->printTaskInfo("Resolving binary for {$this->command}..."); + if (@file_exists($this->command) && is_executable($this->command)) { + return Result::success($this, "Found {$this->command}", ['path' => $this->command]); + } + $output = []; + $result_code = NULL; + $alternatives = explode(',', $this->alternatives); + array_unshift($alternatives, $this->command); + foreach ($alternatives as $alternative) { + $arg = escapeshellarg($alternative); + exec("which $arg", $output, $result_code); + if ($result_code === static::SHELL_SUCCESS) { + return Result::success($this, "Resolved to $alternative", ['path' => current($output)]); + } + } + $this->printTaskWarning('Could not resolve any of the executables suggested.'); + return Result::cancelled('Could not resolve any of the executables suggested.', ['path' => '']); + } + +} diff --git a/src/Robo/Task/Discovery/Tasks.php b/src/Robo/Task/Discovery/Tasks.php new file mode 100644 index 00000000..8185e64c --- /dev/null +++ b/src/Robo/Task/Discovery/Tasks.php @@ -0,0 +1,16 @@ +task(Alternatives::class, $dirs); + } +} From 45b7b83f757196d574afc40c178b8f05b861b1bf Mon Sep 17 00:00:00 2001 From: apotek <279278+apotek@users.noreply.github.com> Date: Fri, 15 Sep 2023 19:30:43 -0400 Subject: [PATCH 02/10] Provide a simple lando setup for testing basic robo commands --- .lando.yml | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 .lando.yml diff --git a/.lando.yml b/.lando.yml new file mode 100644 index 00000000..58cf7b46 --- /dev/null +++ b/.lando.yml @@ -0,0 +1,9 @@ +name: usher +recipe: symfony +config: + php: '8.1' + webroot: . + +tooling: + robo: + service: appserver From 0ade5651e0385aefdc7f6d00dfb765ffb20c7923 Mon Sep 17 00:00:00 2001 From: apotek <279278+apotek@users.noreply.github.com> Date: Fri, 15 Sep 2023 19:57:32 -0400 Subject: [PATCH 03/10] Change alternatives from string to array Fix phpcs errors --- .../Commands/DevelopmentModeBaseCommands.php | 1 + src/Robo/Task/Discovery/Alternatives.php | 22 +++++++++---------- src/Robo/Task/Discovery/Tasks.php | 7 +++--- 3 files changed, 16 insertions(+), 14 deletions(-) diff --git a/src/Robo/Plugin/Commands/DevelopmentModeBaseCommands.php b/src/Robo/Plugin/Commands/DevelopmentModeBaseCommands.php index 46be9091..e4fd6f55 100644 --- a/src/Robo/Plugin/Commands/DevelopmentModeBaseCommands.php +++ b/src/Robo/Plugin/Commands/DevelopmentModeBaseCommands.php @@ -132,6 +132,7 @@ public function databaseRefreshTugboat(): ResultData try { $dbPath = $this->databaseDownload($siteName); } catch (TaskException $te) { + $this->yell("$siteName: No database configured. Download/import skipped."); $result_data->append($te->getMessage()); // @todo: Should we run a site-install by default? continue; diff --git a/src/Robo/Task/Discovery/Alternatives.php b/src/Robo/Task/Discovery/Alternatives.php index cf5bcf13..450b102a 100644 --- a/src/Robo/Task/Discovery/Alternatives.php +++ b/src/Robo/Task/Discovery/Alternatives.php @@ -4,6 +4,7 @@ use Robo\Task\BaseTask; use Robo\Result; +use Robo\ResultData; /** * Use this to figure out which binary to use from a series of alternatives. @@ -21,9 +22,9 @@ class Alternatives extends BaseTask { /** - * @var string + * @var array */ - protected string $alternatives; + protected array $alternatives; /** * @var string @@ -40,11 +41,11 @@ class Alternatives extends BaseTask * * @param string $command * The preferred binary or path to binary to execute. - * @param string $alternatives - * A comma-separated list of alternative binaries or paths to binaries to + * @param array $alternatives + * A list of alternative binaries or paths to binaries to * execute. */ - public function __construct(string $command, string $alternatives) + public function __construct(string $command, array $alternatives) { $this->command = $command; $this->alternatives = $alternatives; @@ -56,17 +57,17 @@ public function __construct(string $command, string $alternatives) * The array returned by Result::getData() will always contain a 'path' * property, even if empty. */ - public function run(): Result + public function run(): Result|ResultData { $this->printTaskInfo("Resolving binary for {$this->command}..."); if (@file_exists($this->command) && is_executable($this->command)) { return Result::success($this, "Found {$this->command}", ['path' => $this->command]); } $output = []; - $result_code = NULL; - $alternatives = explode(',', $this->alternatives); - array_unshift($alternatives, $this->command); - foreach ($alternatives as $alternative) { + $result_code = null; + + array_unshift($this->alternatives, $this->command); + foreach ($this->alternatives as $alternative) { $arg = escapeshellarg($alternative); exec("which $arg", $output, $result_code); if ($result_code === static::SHELL_SUCCESS) { @@ -76,5 +77,4 @@ public function run(): Result $this->printTaskWarning('Could not resolve any of the executables suggested.'); return Result::cancelled('Could not resolve any of the executables suggested.', ['path' => '']); } - } diff --git a/src/Robo/Task/Discovery/Tasks.php b/src/Robo/Task/Discovery/Tasks.php index 8185e64c..db8ec709 100644 --- a/src/Robo/Task/Discovery/Tasks.php +++ b/src/Robo/Task/Discovery/Tasks.php @@ -5,12 +5,13 @@ trait Tasks { /** - * @param string|string[] $dirs + * @param string $command + * @param array $alternatives * * @return \Usher\Robo\Task\Discovery\Alternatives|\Robo\Collection\CollectionBuilder */ - protected function taskAlternatives($dirs) + protected function taskAlternatives(string $command, array $alternatives) { - return $this->task(Alternatives::class, $dirs); + return $this->task(Alternatives::class, $command, $alternatives); } } From 84932cc26fea039f8bb078e63417a03a360b8050 Mon Sep 17 00:00:00 2001 From: apotek <279278+apotek@users.noreply.github.com> Date: Fri, 15 Sep 2023 20:50:04 -0400 Subject: [PATCH 04/10] Silence erroneous phpstan claim that $dbPath always exists and is not nullable phpstan complains about line 140 in DevelopmentModeBaseCommands. I could not find any specific rule indicator to silence the complaint. So I had to ignore the whole line. phpstan isn't good at ascertaining state of variable definition in try{}catch{} scenarios --- src/Robo/Plugin/Commands/DevelopmentModeBaseCommands.php | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Robo/Plugin/Commands/DevelopmentModeBaseCommands.php b/src/Robo/Plugin/Commands/DevelopmentModeBaseCommands.php index e4fd6f55..d2179a36 100644 --- a/src/Robo/Plugin/Commands/DevelopmentModeBaseCommands.php +++ b/src/Robo/Plugin/Commands/DevelopmentModeBaseCommands.php @@ -137,6 +137,7 @@ public function databaseRefreshTugboat(): ResultData // @todo: Should we run a site-install by default? continue; } + /* @phpstan-ignore-next-line */ if (!isset($dbPath) || !is_string($dbPath) || strlen($dbPath) == 0) { $this->yell("'$siteName' database path not found."); $result_data->append("'$siteName' database path not found."); From 806fea5954f8273aee8a71656ba46e7cdae1feef Mon Sep 17 00:00:00 2001 From: apotek <279278+apotek@users.noreply.github.com> Date: Fri, 15 Sep 2023 20:53:33 -0400 Subject: [PATCH 05/10] Fix phpcs balking at emoji. --- src/Robo/Plugin/Commands/DevelopmentModeBaseCommands.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/Robo/Plugin/Commands/DevelopmentModeBaseCommands.php b/src/Robo/Plugin/Commands/DevelopmentModeBaseCommands.php index d2179a36..2dbb0de2 100644 --- a/src/Robo/Plugin/Commands/DevelopmentModeBaseCommands.php +++ b/src/Robo/Plugin/Commands/DevelopmentModeBaseCommands.php @@ -137,6 +137,10 @@ public function databaseRefreshTugboat(): ResultData // @todo: Should we run a site-install by default? continue; } + // phpstan doesn't seem to be able to understand that $dbPath will + // be undefined if an exception is thrown in try{}, so it complains. + // I could not find any identifier that would silence this so I had + // to silence the whole line :(. /* @phpstan-ignore-next-line */ if (!isset($dbPath) || !is_string($dbPath) || strlen($dbPath) == 0) { $this->yell("'$siteName' database path not found."); From c8005cb45f5c26fc5075ee9ff77ff44c960b8b67 Mon Sep 17 00:00:00 2001 From: apotek <279278+apotek@users.noreply.github.com> Date: Fri, 15 Sep 2023 21:00:13 -0400 Subject: [PATCH 06/10] Rector changes --- src/Robo/Task/Discovery/Alternatives.php | 20 +++++--------------- 1 file changed, 5 insertions(+), 15 deletions(-) diff --git a/src/Robo/Task/Discovery/Alternatives.php b/src/Robo/Task/Discovery/Alternatives.php index 450b102a..897ec15b 100644 --- a/src/Robo/Task/Discovery/Alternatives.php +++ b/src/Robo/Task/Discovery/Alternatives.php @@ -21,16 +21,6 @@ */ class Alternatives extends BaseTask { - /** - * @var array - */ - protected array $alternatives; - - /** - * @var string - */ - protected string $command; - /** * @var int */ @@ -45,10 +35,10 @@ class Alternatives extends BaseTask * A list of alternative binaries or paths to binaries to * execute. */ - public function __construct(string $command, array $alternatives) - { - $this->command = $command; - $this->alternatives = $alternatives; + public function __construct( + protected string $command, + protected array $alternatives + ) { } /** @@ -68,7 +58,7 @@ public function run(): Result|ResultData array_unshift($this->alternatives, $this->command); foreach ($this->alternatives as $alternative) { - $arg = escapeshellarg($alternative); + $arg = escapeshellarg((string) $alternative); exec("which $arg", $output, $result_code); if ($result_code === static::SHELL_SUCCESS) { return Result::success($this, "Resolved to $alternative", ['path' => current($output)]); From 1d265144e0d3e7dbc87fb555bd2c7309698c4c14 Mon Sep 17 00:00:00 2001 From: K Widholm <279278+apotek@users.noreply.github.com> Date: Mon, 18 Sep 2023 17:37:14 -0400 Subject: [PATCH 07/10] Update src/Robo/Task/Discovery/Alternatives.php Co-authored-by: Adam Zimmermann --- src/Robo/Task/Discovery/Alternatives.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Robo/Task/Discovery/Alternatives.php b/src/Robo/Task/Discovery/Alternatives.php index 897ec15b..68e3594d 100644 --- a/src/Robo/Task/Discovery/Alternatives.php +++ b/src/Robo/Task/Discovery/Alternatives.php @@ -14,7 +14,7 @@ * // Figure out whether you should use more or less or cat on a system. The * // result data will always contain a 'path' property, even if no alternative * // could be resolved and the path is empty. - * $result = $this->task(Alternatives::class, 'more', 'less,cat')->run(); + * $result = $this->task(Alternatives::class, 'more', ['less', 'cat'])->run(); * $binary = $result->getData()['path'] * ?> * ``` From 515f35fd48f96cd19843fae5ed7f7385a93674c7 Mon Sep 17 00:00:00 2001 From: apotek <279278+apotek@users.noreply.github.com> Date: Mon, 18 Sep 2023 17:49:37 -0400 Subject: [PATCH 08/10] Respond to feedback on change set --- .../Commands/DevelopmentModeBaseCommands.php | 34 ++++++++----------- src/Robo/Task/Discovery/Alternatives.php | 8 ++--- 2 files changed, 19 insertions(+), 23 deletions(-) diff --git a/src/Robo/Plugin/Commands/DevelopmentModeBaseCommands.php b/src/Robo/Plugin/Commands/DevelopmentModeBaseCommands.php index 2dbb0de2..e83d2057 100644 --- a/src/Robo/Plugin/Commands/DevelopmentModeBaseCommands.php +++ b/src/Robo/Plugin/Commands/DevelopmentModeBaseCommands.php @@ -30,21 +30,21 @@ class DevelopmentModeBaseCommands extends Tasks * * @var string */ - protected $drupalRoot; + protected string $drupalRoot; /** * Composer vendor directory. * * @var string */ - protected $vendorDirectory; + protected string $vendorDirectory; /** * Path to front-end development services path. * * @var string */ - protected $devServicesPath; + protected string $devServicesPath; /** * Class constructor. @@ -126,31 +126,27 @@ public function databaseRefreshLando(string $siteName = 'default'): Result public function databaseRefreshTugboat(): ResultData { $this->io()->title('refresh tugboat databases.'); - $result_data = new ResultData(); + $resultData = new ResultData(); foreach (array_keys($this->getAllSitesConfig()) as $siteName) { + $dbPath = ''; try { $dbPath = $this->databaseDownload($siteName); - } catch (TaskException $te) { + } catch (TaskException $e) { $this->yell("$siteName: No database configured. Download/import skipped."); - $result_data->append($te->getMessage()); + $resultData->append($e->getMessage()); // @todo: Should we run a site-install by default? continue; } - // phpstan doesn't seem to be able to understand that $dbPath will - // be undefined if an exception is thrown in try{}, so it complains. - // I could not find any identifier that would silence this so I had - // to silence the whole line :(. - /* @phpstan-ignore-next-line */ - if (!isset($dbPath) || !is_string($dbPath) || strlen($dbPath) == 0) { + if (!is_string($dbPath) || strlen($dbPath) === 0) { $this->yell("'$siteName' database path not found."); - $result_data->append("'$siteName' database path not found."); + $resultData->append("'$siteName' database path not found."); continue; } $dbName = $siteName === 'default' ? 'tugboat' : $siteName; $taskResult = $this->task(Alternatives::class, 'mariadb', 'mysql')->run(); if (!$taskResult->wasSuccessful()) { - $result_data->append($taskResult); + $resultData->append($taskResult); continue; } $dbDriver = $taskResult->getData()['path']; @@ -160,24 +156,24 @@ public function databaseRefreshTugboat(): ResultData ->option('-ptugboat') ->option('-e', "drop database if exists $dbName; create database $dbName;") ->run(); - $result_data->append($taskResult); + $resultData->append($taskResult); $this->io()->section("import $siteName database."); $taskResult = $this->taskExec("zcat $dbPath | $dbDriver -h mariadb -u tugboat -ptugboat $dbName") ->run(); - $result_data->append($taskResult); + $resultData->append($taskResult); $taskResult = $this->taskExec('rm')->args($dbPath)->run(); - $result_data->append($taskResult); + $resultData->append($taskResult); if (!$this->drupalVersionIsD7($this->drupalRoot)) { $taskResult = $this->taskExec("$this->vendorDirectory/bin/drush") ->arg('cache:rebuild') ->dir("$this->drupalRoot/sites/$siteName") ->run(); - $result_data->append($taskResult); + $resultData->append($taskResult); } } - return $result_data; + return $resultData; } /** diff --git a/src/Robo/Task/Discovery/Alternatives.php b/src/Robo/Task/Discovery/Alternatives.php index 68e3594d..301d6d1a 100644 --- a/src/Robo/Task/Discovery/Alternatives.php +++ b/src/Robo/Task/Discovery/Alternatives.php @@ -50,17 +50,17 @@ public function __construct( public function run(): Result|ResultData { $this->printTaskInfo("Resolving binary for {$this->command}..."); - if (@file_exists($this->command) && is_executable($this->command)) { + if (file_exists($this->command) && is_executable($this->command)) { return Result::success($this, "Found {$this->command}", ['path' => $this->command]); } $output = []; - $result_code = null; + $resultCode = null; array_unshift($this->alternatives, $this->command); foreach ($this->alternatives as $alternative) { $arg = escapeshellarg((string) $alternative); - exec("which $arg", $output, $result_code); - if ($result_code === static::SHELL_SUCCESS) { + exec("which $arg", $output, $resultCode); + if ($resultCode === static::SHELL_SUCCESS) { return Result::success($this, "Resolved to $alternative", ['path' => current($output)]); } } From 209a26f807d7714a46a1bfd6f34337f6eb51b319 Mon Sep 17 00:00:00 2001 From: apotek <279278+apotek@users.noreply.github.com> Date: Mon, 18 Sep 2023 20:51:52 -0400 Subject: [PATCH 09/10] Fix Rector complaints --- src/Robo/Plugin/Commands/DevelopmentModeBaseCommands.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Robo/Plugin/Commands/DevelopmentModeBaseCommands.php b/src/Robo/Plugin/Commands/DevelopmentModeBaseCommands.php index e83d2057..31233d52 100644 --- a/src/Robo/Plugin/Commands/DevelopmentModeBaseCommands.php +++ b/src/Robo/Plugin/Commands/DevelopmentModeBaseCommands.php @@ -138,7 +138,7 @@ public function databaseRefreshTugboat(): ResultData // @todo: Should we run a site-install by default? continue; } - if (!is_string($dbPath) || strlen($dbPath) === 0) { + if (!is_string($dbPath) || $dbPath === '') { $this->yell("'$siteName' database path not found."); $resultData->append("'$siteName' database path not found."); continue; From 855ca90159d6896f51026c9a40143fb393936f9e Mon Sep 17 00:00:00 2001 From: Mark Dorison Date: Tue, 19 Sep 2023 09:03:46 -0400 Subject: [PATCH 10/10] Added DDEV configuration. --- .ddev/config.yaml | 262 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 262 insertions(+) create mode 100644 .ddev/config.yaml diff --git a/.ddev/config.yaml b/.ddev/config.yaml new file mode 100644 index 00000000..06a05b46 --- /dev/null +++ b/.ddev/config.yaml @@ -0,0 +1,262 @@ +name: usher +type: php +docroot: "" +php_version: "8.2" +webserver_type: nginx-fpm +xdebug_enabled: false +additional_hostnames: [] +additional_fqdns: [] +use_dns_when_possible: true +composer_version: "2" +web_environment: [] +nodejs_version: "18" +omit_containers: + - db + +# Key features of ddev's config.yaml: + +# name: # Name of the project, automatically provides +# http://projectname.ddev.site and https://projectname.ddev.site + +# type: # drupal6/7/8, backdrop, typo3, wordpress, php + +# docroot: # Relative path to the directory containing index.php. + +# php_version: "8.1" # PHP version to use, "5.6", "7.0", "7.1", "7.2", "7.3", "7.4", "8.0", "8.1", "8.2" + +# You can explicitly specify the webimage but this +# is not recommended, as the images are often closely tied to ddev's' behavior, +# so this can break upgrades. + +# webimage: # nginx/php docker image. + +# database: +# type: # mysql, mariadb, postgres +# version: # database version, like "10.4" or "8.0" +# mariadb versions can be 5.5-10.8 and 10.11, mysql versions can be 5.5-8.0 +# postgres versions can be 9-15. + +# router_http_port: # Port to be used for http (defaults to global configuration, usually 80) +# router_https_port: # Port for https (defaults to global configuration, usually 443) + +# xdebug_enabled: false # Set to true to enable xdebug and "ddev start" or "ddev restart" +# Note that for most people the commands +# "ddev xdebug" to enable xdebug and "ddev xdebug off" to disable it work better, +# as leaving xdebug enabled all the time is a big performance hit. + +# xhprof_enabled: false # Set to true to enable xhprof and "ddev start" or "ddev restart" +# Note that for most people the commands +# "ddev xhprof" to enable xhprof and "ddev xhprof off" to disable it work better, +# as leaving xhprof enabled all the time is a big performance hit. + +# webserver_type: nginx-fpm, apache-fpm, or nginx-gunicorn + +# timezone: Europe/Berlin +# This is the timezone used in the containers and by PHP; +# it can be set to any valid timezone, +# see https://en.wikipedia.org/wiki/List_of_tz_database_time_zones +# For example Europe/Dublin or MST7MDT + +# composer_root: +# Relative path to the composer root directory from the project root. This is +# the directory which contains the composer.json and where all Composer related +# commands are executed. + +# composer_version: "2" +# You can set it to "" or "2" (default) for Composer v2 or "1" for Composer v1 +# to use the latest major version available at the time your container is built. +# It is also possible to use each other Composer version channel. This includes: +# - 2.2 (latest Composer LTS version) +# - stable +# - preview +# - snapshot +# Alternatively, an explicit Composer version may be specified, for example "2.2.18". +# To reinstall Composer after the image was built, run "ddev debug refresh". + +# nodejs_version: "18" +# change from the default system Node.js version to another supported version, like 14, 16, 18, 20. +# Note that you can use 'ddev nvm' or nvm inside the web container to provide nearly any +# Node.js version, including v6, etc. + +# additional_hostnames: +# - somename +# - someothername +# would provide http and https URLs for "somename.ddev.site" +# and "someothername.ddev.site". + +# additional_fqdns: +# - example.com +# - sub1.example.com +# would provide http and https URLs for "example.com" and "sub1.example.com" +# Please take care with this because it can cause great confusion. + +# upload_dirs: "custom/upload/dir" +# +# upload_dirs: +# - custom/upload/dir +# - ../private +# +# would set the destination paths for ddev import-files to /custom/upload/dir +# When mutagen is enabled this path is bind-mounted so that all the files +# in the upload_dirs don't have to be synced into mutagen. + +# disable_upload_dirs_warning: false +# If true, turns off the normal warning that says +# "You have Mutagen enabled and your 'php' project type doesn't have upload_dirs set" + +# working_dir: +# web: /var/www/html +# db: /home +# would set the default working directory for the web and db services. +# These values specify the destination directory for ddev ssh and the +# directory in which commands passed into ddev exec are run. + +# omit_containers: [db, ddev-ssh-agent] +# Currently only these containers are supported. Some containers can also be +# omitted globally in the ~/.ddev/global_config.yaml. Note that if you omit +# the "db" container, several standard features of ddev that access the +# database container will be unusable. In the global configuration it is also +# possible to omit ddev-router, but not here. + +# performance_mode: "global" +# DDEV offers performance optimization strategies to improve the filesystem +# performance depending on your host system. Should be configured globally. +# +# If set, will override the global config. Possible values are: +# - "global": uses the value from the global config. +# - "none": disables performance optimization for this project. +# - "mutagen": enables Mutagen for this project. +# - "nfs": enables NFS for this project. +# +# See https://ddev.readthedocs.io/en/latest/users/install/performance/#nfs +# See https://ddev.readthedocs.io/en/latest/users/install/performance/#mutagen + +# fail_on_hook_fail: False +# Decide whether 'ddev start' should be interrupted by a failing hook + +# host_https_port: "59002" +# The host port binding for https can be explicitly specified. It is +# dynamic unless otherwise specified. +# This is not used by most people, most people use the *router* instead +# of the localhost port. + +# host_webserver_port: "59001" +# The host port binding for the ddev-webserver can be explicitly specified. It is +# dynamic unless otherwise specified. +# This is not used by most people, most people use the *router* instead +# of the localhost port. + +# host_db_port: "59002" +# The host port binding for the ddev-dbserver can be explicitly specified. It is dynamic +# unless explicitly specified. + +# mailhog_port: "8025" +# mailhog_https_port: "8026" +# The MailHog ports can be changed from the default 8025 and 8026 + +# host_mailhog_port: "8025" +# The mailhog port is not normally bound on the host at all, instead being routed +# through ddev-router, but it can be bound directly to localhost if specified here. + +# webimage_extra_packages: [php7.4-tidy, php-bcmath] +# Extra Debian packages that are needed in the webimage can be added here + +# dbimage_extra_packages: [telnet,netcat] +# Extra Debian packages that are needed in the dbimage can be added here + +# use_dns_when_possible: true +# If the host has internet access and the domain configured can +# successfully be looked up, DNS will be used for hostname resolution +# instead of editing /etc/hosts +# Defaults to true + +# project_tld: ddev.site +# The top-level domain used for project URLs +# The default "ddev.site" allows DNS lookup via a wildcard +# If you prefer you can change this to "ddev.local" to preserve +# pre-v1.9 behavior. + +# ngrok_args: --basic-auth username:pass1234 +# Provide extra flags to the "ngrok http" command, see +# https://ngrok.com/docs/ngrok-agent/config or run "ngrok http -h" + +# disable_settings_management: false +# If true, ddev will not create CMS-specific settings files like +# Drupal's settings.php/settings.ddev.php or TYPO3's AdditionalConfiguration.php +# In this case the user must provide all such settings. + +# You can inject environment variables into the web container with: +# web_environment: +# - SOMEENV=somevalue +# - SOMEOTHERENV=someothervalue + +# no_project_mount: false +# (Experimental) If true, ddev will not mount the project into the web container; +# the user is responsible for mounting it manually or via a script. +# This is to enable experimentation with alternate file mounting strategies. +# For advanced users only! + +# bind_all_interfaces: false +# If true, host ports will be bound on all network interfaces, +# not just the localhost interface. This means that ports +# will be available on the local network if the host firewall +# allows it. + +# default_container_timeout: 120 +# The default time that ddev waits for all containers to become ready can be increased from +# the default 120. This helps in importing huge databases, for example. + +#web_extra_exposed_ports: +#- name: nodejs +# container_port: 3000 +# http_port: 2999 +# https_port: 3000 +#- name: something +# container_port: 4000 +# https_port: 4000 +# http_port: 3999 +# Allows a set of extra ports to be exposed via ddev-router +# Fill in all three fields even if you don’t intend to use the https_port! +# If you don’t add https_port, then it defaults to 0 and ddev-router will fail to start. +# +# The port behavior on the ddev-webserver must be arranged separately, for example +# using web_extra_daemons. +# For example, with a web app on port 3000 inside the container, this config would +# expose that web app on https://.ddev.site:9999 and http://.ddev.site:9998 +# web_extra_exposed_ports: +# - name: myapp +# container_port: 3000 +# http_port: 9998 +# https_port: 9999 + +#web_extra_daemons: +#- name: "http-1" +# command: "/var/www/html/node_modules/.bin/http-server -p 3000" +# directory: /var/www/html +#- name: "http-2" +# command: "/var/www/html/node_modules/.bin/http-server /var/www/html/sub -p 3000" +# directory: /var/www/html + +# override_config: false +# By default, config.*.yaml files are *merged* into the configuration +# But this means that some things can't be overridden +# For example, if you have 'nfs_mount_enabled: true'' you can't override it with a merge +# and you can't erase existing hooks or all environment variables. +# However, with "override_config: true" in a particular config.*.yaml file, +# 'nfs_mount_enabled: false' can override the existing values, and +# hooks: +# post-start: [] +# or +# web_environment: [] +# or +# additional_hostnames: [] +# can have their intended affect. 'override_config' affects only behavior of the +# config.*.yaml file it exists in. + +# Many ddev commands can be extended to run tasks before or after the +# ddev command is executed, for example "post-start", "post-import-db", +# "pre-composer", "post-composer" +# See https://ddev.readthedocs.io/en/stable/users/extend/custom-commands/ for more +# information on the commands that can be extended and the tasks you can define +# for them. Example: +#hooks: