diff --git a/.gitignore b/.gitignore index 1f078b17d..247c56154 100644 --- a/.gitignore +++ b/.gitignore @@ -23,3 +23,4 @@ reports/* Brewfile.lock.json /.phive /.php-cs-fixer.cache +/terminus-data diff --git a/bin/terminus b/bin/terminus index 1df883b6f..cecf8c5b6 100755 --- a/bin/terminus +++ b/bin/terminus @@ -32,7 +32,7 @@ if (!getenv('TERMINUS_ALLOW_UNSUPPORTED_NEWER_PHP') && version_compare(PHP_VERSI // This variable is automatically managed via updateDependenciesversion() in /RoboFile.php, // which is run after every call to composer update. -$terminusPluginsDependenciesVersion = '10c370e142'; +$terminusPluginsDependenciesVersion = '27a446d993'; // Cannot use $_SERVER superglobal since that's empty during phpunit testing // getenv('HOME') isn't set on Windows and generates a Notice. diff --git a/composer.lock b/composer.lock index 6175aab39..48926d0c8 100644 --- a/composer.lock +++ b/composer.lock @@ -1907,16 +1907,16 @@ }, { "name": "symfony/console", - "version": "v5.4.23", + "version": "v5.4.24", "source": { "type": "git", "url": "https://github.com/symfony/console.git", - "reference": "90f21e27d0d88ce38720556dd164d4a1e4c3934c" + "reference": "560fc3ed7a43e6d30ea94a07d77f9a60b8ed0fb8" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/90f21e27d0d88ce38720556dd164d4a1e4c3934c", - "reference": "90f21e27d0d88ce38720556dd164d4a1e4c3934c", + "url": "https://api.github.com/repos/symfony/console/zipball/560fc3ed7a43e6d30ea94a07d77f9a60b8ed0fb8", + "reference": "560fc3ed7a43e6d30ea94a07d77f9a60b8ed0fb8", "shasum": "" }, "require": { @@ -1986,7 +1986,7 @@ "terminal" ], "support": { - "source": "https://github.com/symfony/console/tree/v5.4.23" + "source": "https://github.com/symfony/console/tree/v5.4.24" }, "funding": [ { @@ -2002,7 +2002,7 @@ "type": "tidelift" } ], - "time": "2023-04-24T18:47:29+00:00" + "time": "2023-05-26T05:13:16+00:00" }, { "name": "symfony/deprecation-contracts", @@ -2856,16 +2856,16 @@ }, { "name": "symfony/process", - "version": "v5.4.23", + "version": "v5.4.24", "source": { "type": "git", "url": "https://github.com/symfony/process.git", - "reference": "4b842fc4b61609e0a155a114082bd94e31e98287" + "reference": "e3c46cc5689c8782944274bb30702106ecbe3b64" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/process/zipball/4b842fc4b61609e0a155a114082bd94e31e98287", - "reference": "4b842fc4b61609e0a155a114082bd94e31e98287", + "url": "https://api.github.com/repos/symfony/process/zipball/e3c46cc5689c8782944274bb30702106ecbe3b64", + "reference": "e3c46cc5689c8782944274bb30702106ecbe3b64", "shasum": "" }, "require": { @@ -2898,7 +2898,7 @@ "description": "Executes commands in sub-processes", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/process/tree/v5.4.23" + "source": "https://github.com/symfony/process/tree/v5.4.24" }, "funding": [ { @@ -2914,7 +2914,7 @@ "type": "tidelift" } ], - "time": "2023-04-18T13:50:24+00:00" + "time": "2023-05-17T11:26:05+00:00" }, { "name": "symfony/service-contracts", @@ -3087,16 +3087,16 @@ }, { "name": "symfony/var-dumper", - "version": "v5.4.23", + "version": "v5.4.24", "source": { "type": "git", "url": "https://github.com/symfony/var-dumper.git", - "reference": "9a8a5b6d6508928174ded2109e29328a55342a42" + "reference": "8e12706bf9c68a2da633f23bfdc15b4dce5970b3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/var-dumper/zipball/9a8a5b6d6508928174ded2109e29328a55342a42", - "reference": "9a8a5b6d6508928174ded2109e29328a55342a42", + "url": "https://api.github.com/repos/symfony/var-dumper/zipball/8e12706bf9c68a2da633f23bfdc15b4dce5970b3", + "reference": "8e12706bf9c68a2da633f23bfdc15b4dce5970b3", "shasum": "" }, "require": { @@ -3105,7 +3105,6 @@ "symfony/polyfill-php80": "^1.16" }, "conflict": { - "phpunit/phpunit": "<5.4.3", "symfony/console": "<4.4" }, "require-dev": { @@ -3156,7 +3155,7 @@ "dump" ], "support": { - "source": "https://github.com/symfony/var-dumper/tree/v5.4.23" + "source": "https://github.com/symfony/var-dumper/tree/v5.4.24" }, "funding": [ { @@ -3172,7 +3171,7 @@ "type": "tidelift" } ], - "time": "2023-04-18T09:26:27+00:00" + "time": "2023-05-25T13:05:00+00:00" }, { "name": "symfony/yaml", @@ -3803,16 +3802,16 @@ }, { "name": "doctrine/deprecations", - "version": "v1.0.0", + "version": "v1.1.0", "source": { "type": "git", "url": "https://github.com/doctrine/deprecations.git", - "reference": "0e2a4f1f8cdfc7a92ec3b01c9334898c806b30de" + "reference": "8cffffb2218e01f3b370bf763e00e81697725259" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/deprecations/zipball/0e2a4f1f8cdfc7a92ec3b01c9334898c806b30de", - "reference": "0e2a4f1f8cdfc7a92ec3b01c9334898c806b30de", + "url": "https://api.github.com/repos/doctrine/deprecations/zipball/8cffffb2218e01f3b370bf763e00e81697725259", + "reference": "8cffffb2218e01f3b370bf763e00e81697725259", "shasum": "" }, "require": { @@ -3840,9 +3839,9 @@ "homepage": "https://www.doctrine-project.org/", "support": { "issues": "https://github.com/doctrine/deprecations/issues", - "source": "https://github.com/doctrine/deprecations/tree/v1.0.0" + "source": "https://github.com/doctrine/deprecations/tree/v1.1.0" }, - "time": "2022-05-02T15:47:09+00:00" + "time": "2023-05-29T18:55:17+00:00" }, { "name": "doctrine/instantiator", @@ -6120,16 +6119,16 @@ }, { "name": "symfony/dependency-injection", - "version": "v5.4.23", + "version": "v5.4.24", "source": { "type": "git", "url": "https://github.com/symfony/dependency-injection.git", - "reference": "bb7b7988c898c94f5338e16403c52b5a3cae1d93" + "reference": "4645e032d0963fb614969398ca28e47605b1a7da" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/dependency-injection/zipball/bb7b7988c898c94f5338e16403c52b5a3cae1d93", - "reference": "bb7b7988c898c94f5338e16403c52b5a3cae1d93", + "url": "https://api.github.com/repos/symfony/dependency-injection/zipball/4645e032d0963fb614969398ca28e47605b1a7da", + "reference": "4645e032d0963fb614969398ca28e47605b1a7da", "shasum": "" }, "require": { @@ -6189,7 +6188,7 @@ "description": "Allows you to standardize and centralize the way objects are constructed in your application", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/dependency-injection/tree/v5.4.23" + "source": "https://github.com/symfony/dependency-injection/tree/v5.4.24" }, "funding": [ { @@ -6205,7 +6204,7 @@ "type": "tidelift" } ], - "time": "2023-04-21T15:04:16+00:00" + "time": "2023-05-05T14:42:55+00:00" }, { "name": "symfony/options-resolver", @@ -6419,16 +6418,16 @@ }, { "name": "symfony/translation", - "version": "v5.4.22", + "version": "v5.4.24", "source": { "type": "git", "url": "https://github.com/symfony/translation.git", - "reference": "9a401392f01bc385aa42760eff481d213a0cc2ba" + "reference": "de237e59c5833422342be67402d487fbf50334ff" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/translation/zipball/9a401392f01bc385aa42760eff481d213a0cc2ba", - "reference": "9a401392f01bc385aa42760eff481d213a0cc2ba", + "url": "https://api.github.com/repos/symfony/translation/zipball/de237e59c5833422342be67402d487fbf50334ff", + "reference": "de237e59c5833422342be67402d487fbf50334ff", "shasum": "" }, "require": { @@ -6496,7 +6495,7 @@ "description": "Provides tools to internationalize your application", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/translation/tree/v5.4.22" + "source": "https://github.com/symfony/translation/tree/v5.4.24" }, "funding": [ { @@ -6512,7 +6511,7 @@ "type": "tidelift" } ], - "time": "2023-03-27T16:07:23+00:00" + "time": "2023-05-19T12:34:17+00:00" }, { "name": "symfony/translation-contracts", diff --git a/src/Commands/Env/CloneContentCommand.php b/src/Commands/Env/CloneContentCommand.php index 7f71ccc02..79404f6b1 100644 --- a/src/Commands/Env/CloneContentCommand.php +++ b/src/Commands/Env/CloneContentCommand.php @@ -12,6 +12,7 @@ /** * Class CloneContentCommand + * * @package Pantheon\Terminus\Commands\Env */ class CloneContentCommand extends TerminusCommand implements SiteAwareInterface @@ -23,6 +24,7 @@ class CloneContentCommand extends TerminusCommand implements SiteAwareInterface * @var Environment */ private $source_env; + /** * @var Environment */ @@ -43,9 +45,11 @@ class CloneContentCommand extends TerminusCommand implements SiteAwareInterface * * @command env:clone-content * - * @param string $site_env Origin site & environment in the format `site-name.env` + * @param string $site_env Origin site & environment in the format + * `site-name.env` * @param string $target_env Target environment * @param array $options + * * @option bool $cc Whether or not to clear caches * @option bool $db-only Only clone database * @option bool $files-only Only clone files @@ -55,12 +59,21 @@ class CloneContentCommand extends TerminusCommand implements SiteAwareInterface * * @throws \Pantheon\Terminus\Exceptions\TerminusException * - * @usage . Clones database and files from 's environment to environment. - * @usage . --cc Clones from 's environment to environment and clears the cache. - * @usage . --db-only Clones only the database from 's environment to environment. - * @usage . --files-only Clones only files from 's environment to environment. - * @usage . --updatedb Clones from 's environment to environment and updates the Drupal database (if applicable). - * @usage . --from-url=www.example.com --to-url=mulitidevenv.example.com (WordPress only) Clones from 's environment to environment and replaces www.example.com with mulitidevenv.example.com in the database. + * @usage . Clones database and files from 's + * environment to environment. + * @usage . --cc Clones from 's + * environment to environment and clears the cache. + * @usage . --db-only Clones only the database from + * 's environment to environment. + * @usage . --files-only Clones only files from + * 's environment to environment. + * @usage . --updatedb Clones from 's + * environment to environment and updates the Drupal + * database (if applicable). + * @usage . --from-url=www.example.com + * --to-url=mulitidevenv.example.com (WordPress only) Clones from + * 's environment to environment and replaces + * www.example.com with mulitidevenv.example.com in the database. */ public function cloneContent( $site_env, @@ -75,7 +88,9 @@ public function cloneContent( ] ) { if (!empty($options['db-only']) && !empty($options['files-only'])) { - throw new TerminusException('You cannot specify both --db-only and --files-only'); + throw new TerminusException( + 'You cannot specify both --db-only and --files-only' + ); } $this->requireSiteIsNotFrozen($site_env); @@ -84,7 +99,9 @@ public function cloneContent( $this->target_env = $site->getEnvironments()->get($target_env); if ($this->source_env->id === $target_env) { - $this->log()->notice('The clone has been skipped because the source and target environments are the same.'); + $this->log()->notice( + 'The clone has been skipped because the source and target environments are the same.' + ); return; } @@ -94,9 +111,9 @@ public function cloneContent( !$this->confirm( 'Are you sure you want to clone content from {from} to {to} on {site}?', [ - 'from' => $this->source_env->getName(), - 'site' => $site->getName(), - 'to' => $this->target_env->getName(), + 'from' => $this->source_env->getName(), + 'site' => $site->getName(), + 'to' => $this->target_env->getName(), ] ) ) { @@ -108,25 +125,19 @@ public function cloneContent( } if (empty($options['files-only'])) { - // If the site is a WordPress site, we need to pass the search-replace - // option to the API along with the from_url and to_url from each - // environment. - if ($site->getFramework()->isWordpressFramework()) { - $options['search-replace'] = [ - 'from_url' => $options['from-url'] ?? $this->source_env->domain(), - 'to_url' => $options['to-url'] ?? $this->target_env->domain(), - ]; - } $this->cloneDatabase($options); } } /** - * Checks to see whether the indicated environment is initialized and stops the process if it isn't + * Checks to see whether the indicated environment is initialized and stops + * the process if it isn't * * @param Environment $env * @param string $direction "into" or "from" are recommended. - * @throws TerminusException Thrown if the passed-in environment is not initialized + * + * @throws TerminusException Thrown if the passed-in environment is not + * initialized */ private function checkForInitialization(Environment $env, $direction = '') { @@ -152,8 +163,11 @@ private function cloneDatabase(array $options) 'clear_cache' => $options['cc'], 'updatedb' => $options['updatedb'], ]; - if (!empty($options['search-replace'])) { - $params['search_replace'] = $options['search-replace']; + if ($options['from-url'] != '') { + $params['from_url'] = $options['from-url']; + } + if ($options['to-url'] != '') { + $params['to_url'] = $options['to-url']; } $this->emitNotice('database'); $this->runClone( @@ -179,7 +193,10 @@ private function emitNotice($element) { $this->log()->notice( "Cloning {$element} from {source} environment to {target} environment", - ['source' => $this->source_env->getName(), 'target' => $this->target_env->getName(),] + [ + 'source' => $this->source_env->getName(), + 'target' => $this->target_env->getName(), + ] ); } diff --git a/tests/Functional/EnvCommandsTest.php b/tests/Functional/EnvCommandsTest.php index 5414b9a59..f3a87d201 100644 --- a/tests/Functional/EnvCommandsTest.php +++ b/tests/Functional/EnvCommandsTest.php @@ -15,9 +15,13 @@ class EnvCommandsTest extends TerminusTestBase * * @return string|null */ - protected function ensureSiteEnvironment(string $siteName, string $envName): ?string - { - return $this->terminus(sprintf('multidev:create %s.dev %s', $siteName, $envName)); + protected function ensureSiteEnvironment( + string $siteName, + string $envName + ): ?string { + return $this->terminus( + sprintf('multidev:create %s.dev %s', $siteName, $envName) + ); } /** @@ -26,9 +30,13 @@ protected function ensureSiteEnvironment(string $siteName, string $envName): ?st * * @return string|null */ - protected function deleteSiteEnvironment(string $siteName, string $envName): ?string - { - return $this->terminus(sprintf('multidev:delete %s.%s', $siteName, $envName)); + protected function deleteSiteEnvironment( + string $siteName, + string $envName + ): ?string { + return $this->terminus( + sprintf('multidev:delete %s.%s', $siteName, $envName) + ); } @@ -53,7 +61,9 @@ public function testClearCacheCommand() */ public function testDeployCommand() { - $this->terminus(sprintf('env:deploy %s.%s', $this->getSiteName(), 'live')); + $this->terminus( + sprintf('env:deploy %s.%s', $this->getSiteName(), 'live') + ); } /** @@ -65,7 +75,14 @@ public function testDeployCommand() */ public function testCloneContentCommand() { - $this->terminus(sprintf('env:clone-content %s.%s %s', $this->getSiteName(), 'dev', $this->getMdEnv())); + $this->terminus( + sprintf( + 'env:clone-content %s.%s %s', + $this->getSiteName(), + 'dev', + $this->getMdEnv() + ) + ); } /** @@ -78,11 +95,59 @@ public function testCloneContentCommand() public function testCloneContentForWordpressCommand() { // 1. Ensure that the multidev environment exists - $this->ensureSiteEnvironment($this->getSiteName("wordpress"), $this->getMdEnv()); - $this->terminus(sprintf('env:clone-content %s.%s %s', $this->getSiteName(), 'dev', $this->getMdEnv())); - $this->deleteSiteEnvironment($this->getSiteName("wordpress"), $this->getMdEnv()); + $this->ensureSiteEnvironment( + $this->getSiteName("wordpress"), + $this->getMdEnv() + ); + $this->terminus( + sprintf( + 'env:clone-content %s.%s %s', + $this->getSiteName("wordpress"), + 'dev', + $this->getMdEnv() + ) + ); + $this->deleteSiteEnvironment( + $this->getSiteName("wordpress"), + $this->getMdEnv() + ); } + /** + * @test + * @covers \Pantheon\Terminus\Commands\Env\CloneContentCommand + * + * @group env + * @group long + */ + public function testCloneContentForWordpressUrlsCommand() + { + // 1. Ensure that the multidev environment exists + $this->ensureSiteEnvironment( + $this->getSiteName("wordpress"), + $this->getMdEnv() + ); + $this->terminus( + sprintf( + 'env:clone-content %s.%s %s --from-url=%s --to-url=%s', + $this->getSiteName("wordpress"), + 'dev', + $this->getMdEnv(), + 'https://dev-' . $this->getSiteName( + "wordpress" + ) . '.pantheonsite.io', + 'https://test-' . $this->getSiteName( + "wordpress" + ) . '.pantheonsite.io' + ) + ); + $this->deleteSiteEnvironment( + $this->getSiteName("wordpress"), + $this->getMdEnv() + ); + } + + /** * @test * @covers \Pantheon\Terminus\Commands\Env\CodeLogCommand @@ -92,7 +157,9 @@ public function testCloneContentForWordpressCommand() */ public function testCodelogCommand() { - $codeLogs = $this->terminusJsonResponse(sprintf('env:code-log %s', $this->getSiteEnv())); + $codeLogs = $this->terminusJsonResponse( + sprintf('env:code-log %s', $this->getSiteEnv()) + ); $this->assertIsArray($codeLogs); $this->assertNotEmpty($codeLogs); $codeLog = array_shift($codeLogs); @@ -143,7 +210,9 @@ public function testCommitAndDiffStatCommands() sleep(60); // Check the diff - no diff is expected. - $diff = $this->terminusJsonResponse(sprintf('env:diffstat %s', $siteEnv)); + $diff = $this->terminusJsonResponse( + sprintf('env:diffstat %s', $siteEnv) + ); $this->assertEquals([], $diff); // Upload a test file to the site. @@ -158,9 +227,14 @@ public function testCommitAndDiffStatCommands() 'additions' => '1', ], ]; - $this->assertTerminusCommandResultEqualsInAttempts(function () use ($siteEnv) { - return $this->terminusJsonResponse(sprintf('env:diffstat %s', $siteEnv)); - }, $expectedDiff); + $this->assertTerminusCommandResultEqualsInAttempts( + function () use ($siteEnv) { + return $this->terminusJsonResponse( + sprintf('env:diffstat %s', $siteEnv) + ); + }, + $expectedDiff + ); // Commit the changes. $this->terminus( @@ -173,9 +247,14 @@ public function testCommitAndDiffStatCommands() sleep(60); // Check the diff - no diff is expected. - $this->assertTerminusCommandResultEqualsInAttempts(function () use ($siteEnv) { - return $this->terminusJsonResponse(sprintf('env:diffstat %s', $siteEnv)); - }, []); + $this->assertTerminusCommandResultEqualsInAttempts( + function () use ($siteEnv) { + return $this->terminusJsonResponse( + sprintf('env:diffstat %s', $siteEnv) + ); + }, + [] + ); } /** @@ -187,7 +266,9 @@ public function testCommitAndDiffStatCommands() */ public function testInfoCommand() { - $envInfo = $this->terminusJsonResponse(sprintf('env:info %s', $this->getSiteEnv())); + $envInfo = $this->terminusJsonResponse( + sprintf('env:info %s', $this->getSiteEnv()) + ); $this->assertIsArray($envInfo); $this->assertArrayHasKey( 'id', @@ -220,10 +301,16 @@ public function testInfoCommand() */ public function testMetricsCommand() { - $metrics = $this->terminusJsonResponse(sprintf('env:metrics %s', $this->getSiteEnv())); + $metrics = $this->terminusJsonResponse( + sprintf('env:metrics %s', $this->getSiteEnv()) + ); $this->assertIsArray($metrics); $this->assertNotEmpty($metrics); - $this->assertArrayHasKey('timeseries', $metrics, 'Metrics should have "timeseries" field.'); + $this->assertArrayHasKey( + 'timeseries', + $metrics, + 'Metrics should have "timeseries" field.' + ); $metric = array_shift($metrics['timeseries']); $this->assertIsArray($metric); $this->assertNotEmpty($metric); @@ -268,7 +355,9 @@ public function testMetricsCommand() */ public function testListCommand() { - $envs = $this->terminusJsonResponse(sprintf('env:list %s', $this->getSiteName())); + $envs = $this->terminusJsonResponse( + sprintf('env:list %s', $this->getSiteName()) + ); $this->assertIsArray($envs); $env = array_shift($envs); @@ -298,8 +387,14 @@ public function testListCommand() */ public function testViewCommand() { - $url = $this->terminus(sprintf('env:view %s --print', $this->getSiteEnv())); - $expectedUrl = sprintf('https://%s-%s.pantheonsite.io/', $this->getMdEnv(), $this->getSiteName()); + $url = $this->terminus( + sprintf('env:view %s --print', $this->getSiteEnv()) + ); + $expectedUrl = sprintf( + 'https://%s-%s.pantheonsite.io/', + $this->getMdEnv(), + $this->getSiteName() + ); $this->assertEquals($expectedUrl, $url); } }