diff --git a/.gitignore b/.gitignore index 3c62310db0..ea3e34bc70 100644 --- a/.gitignore +++ b/.gitignore @@ -14,3 +14,5 @@ sut/* sut/drush/sites/*test.site.yml /sandbox/ .env +# Test fixtures +sut/drush/sites/synctest.site.yml diff --git a/.scenarios.lock/php5/composer.json b/.scenarios.lock/php5/composer.json index 45f8df5929..76286f5e62 100644 --- a/.scenarios.lock/php5/composer.json +++ b/.scenarios.lock/php5/composer.json @@ -63,7 +63,7 @@ "consolidation/filter-via-dot-access-data": "^1", "consolidation/output-formatters": "^3.3.1", "consolidation/robo": "^1.4.6", - "consolidation/site-alias": "^3.0.0-beta1", + "consolidation/site-alias": "^3.0.0-beta3", "consolidation/site-process": "^2.0.0-beta5", "grasmash/yaml-expander": "^1.1.1", "league/container": "~2", diff --git a/.scenarios.lock/php5/composer.lock b/.scenarios.lock/php5/composer.lock index db31cd6d95..07cddcd0bb 100644 --- a/.scenarios.lock/php5/composer.lock +++ b/.scenarios.lock/php5/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": "27763652ea6dbfc078432fa1f6afec71", + "content-hash": "878a65dec65a4bbf51eac087c865b75e", "packages": [ { "name": "chi-teck/drupal-code-generator", @@ -115,16 +115,16 @@ }, { "name": "consolidation/annotated-command", - "version": "2.11.2", + "version": "2.12.0", "source": { "type": "git", "url": "https://github.com/consolidation/annotated-command.git", - "reference": "004af26391cd7d1cd04b0ac736dc1324d1b4f572" + "reference": "512a2e54c98f3af377589de76c43b24652bcb789" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/consolidation/annotated-command/zipball/004af26391cd7d1cd04b0ac736dc1324d1b4f572", - "reference": "004af26391cd7d1cd04b0ac736dc1324d1b4f572", + "url": "https://api.github.com/repos/consolidation/annotated-command/zipball/512a2e54c98f3af377589de76c43b24652bcb789", + "reference": "512a2e54c98f3af377589de76c43b24652bcb789", "shasum": "" }, "require": { @@ -207,7 +207,7 @@ } ], "description": "Initialize Symfony Console commands from annotated command class methods.", - "time": "2019-02-02T02:29:53+00:00" + "time": "2019-03-08T16:55:03+00:00" }, { "name": "consolidation/config", @@ -663,16 +663,16 @@ }, { "name": "consolidation/site-alias", - "version": "3.0.0-beta1", + "version": "3.0.0-beta3", "source": { "type": "git", "url": "https://github.com/consolidation/site-alias.git", - "reference": "8a785ca8544e009e69b65fa2e41c84742001d011" + "reference": "1cc54f0cf8ff019c07ebc8b4ba557aacfd0a16c2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/consolidation/site-alias/zipball/8a785ca8544e009e69b65fa2e41c84742001d011", - "reference": "8a785ca8544e009e69b65fa2e41c84742001d011", + "url": "https://api.github.com/repos/consolidation/site-alias/zipball/1cc54f0cf8ff019c07ebc8b4ba557aacfd0a16c2", + "reference": "1cc54f0cf8ff019c07ebc8b4ba557aacfd0a16c2", "shasum": "" }, "require": { @@ -731,20 +731,20 @@ } ], "description": "Manage alias records for local and remote sites.", - "time": "2019-03-03T20:24:22+00:00" + "time": "2019-03-11T16:02:18+00:00" }, { "name": "consolidation/site-process", - "version": "2.0.0-beta5", + "version": "2.0.0-beta6", "source": { "type": "git", "url": "https://github.com/consolidation/site-process.git", - "reference": "de0dd63e67ba4d9b96f28a2258d776dfaaa21f19" + "reference": "0a588d0db653b8d8d1af4e50f996a575d9d83687" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/consolidation/site-process/zipball/de0dd63e67ba4d9b96f28a2258d776dfaaa21f19", - "reference": "de0dd63e67ba4d9b96f28a2258d776dfaaa21f19", + "url": "https://api.github.com/repos/consolidation/site-process/zipball/0a588d0db653b8d8d1af4e50f996a575d9d83687", + "reference": "0a588d0db653b8d8d1af4e50f996a575d9d83687", "shasum": "" }, "require": { @@ -803,7 +803,7 @@ } ], "description": "A thin wrapper around the Symfony Process Component that allows applications to use the Site Alias library to specify the target for a remote call.", - "time": "2019-03-03T21:22:40+00:00" + "time": "2019-03-09T06:40:54+00:00" }, { "name": "container-interop/container-interop", @@ -1729,7 +1729,7 @@ }, { "name": "Gert de Pagter", - "email": "BackEndTea@gmail.com" + "email": "backendtea@gmail.com" } ], "description": "Symfony polyfill for ctype functions", @@ -5889,17 +5889,6 @@ { "name": "webflo/drupal-core-strict", "version": "8.6.x-dev", - "source": { - "type": "git", - "url": "https://github.com/webflo/drupal-core-strict.git", - "reference": "77919809646d790f8060b3c3fc695ae2b5dca6c4" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/webflo/drupal-core-strict/zipball/77919809646d790f8060b3c3fc695ae2b5dca6c4", - "reference": "77919809646d790f8060b3c3fc695ae2b5dca6c4", - "shasum": "" - }, "require": { "asm89/stack-cors": "1.2.0", "composer/installers": "v1.5.0", diff --git a/appveyor.yml b/appveyor.yml index 5272262cfb..1de013dd06 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -3,8 +3,8 @@ shallow_clone: false platform: 'x86' clone_folder: C:\projects\work branches: - except: - - gh-pages + only: + - master ## Cache composer bits cache: diff --git a/composer.json b/composer.json index 2310eb670a..a82f712828 100644 --- a/composer.json +++ b/composer.json @@ -39,7 +39,7 @@ "consolidation/filter-via-dot-access-data": "^1", "consolidation/output-formatters": "^3.3.1", "consolidation/robo": "^1.4.6", - "consolidation/site-alias": "^3.0.0-beta1", + "consolidation/site-alias": "^3.0.0-beta3", "consolidation/site-process": "^2.0.0-beta5", "grasmash/yaml-expander": "^1.1.1", "league/container": "~2", diff --git a/composer.lock b/composer.lock index db4c8ed9df..3116242b9a 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": "922372170c58f5ef6b802595d51e3af4", + "content-hash": "023ff293bb4a594a9d7e280cac10808a", "packages": [ { "name": "chi-teck/drupal-code-generator", @@ -115,16 +115,16 @@ }, { "name": "consolidation/annotated-command", - "version": "2.11.2", + "version": "2.12.0", "source": { "type": "git", "url": "https://github.com/consolidation/annotated-command.git", - "reference": "004af26391cd7d1cd04b0ac736dc1324d1b4f572" + "reference": "512a2e54c98f3af377589de76c43b24652bcb789" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/consolidation/annotated-command/zipball/004af26391cd7d1cd04b0ac736dc1324d1b4f572", - "reference": "004af26391cd7d1cd04b0ac736dc1324d1b4f572", + "url": "https://api.github.com/repos/consolidation/annotated-command/zipball/512a2e54c98f3af377589de76c43b24652bcb789", + "reference": "512a2e54c98f3af377589de76c43b24652bcb789", "shasum": "" }, "require": { @@ -207,7 +207,7 @@ } ], "description": "Initialize Symfony Console commands from annotated command class methods.", - "time": "2019-02-02T02:29:53+00:00" + "time": "2019-03-08T16:55:03+00:00" }, { "name": "consolidation/config", @@ -663,16 +663,16 @@ }, { "name": "consolidation/site-alias", - "version": "3.0.0-beta1", + "version": "3.0.0-beta3", "source": { "type": "git", "url": "https://github.com/consolidation/site-alias.git", - "reference": "8a785ca8544e009e69b65fa2e41c84742001d011" + "reference": "1cc54f0cf8ff019c07ebc8b4ba557aacfd0a16c2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/consolidation/site-alias/zipball/8a785ca8544e009e69b65fa2e41c84742001d011", - "reference": "8a785ca8544e009e69b65fa2e41c84742001d011", + "url": "https://api.github.com/repos/consolidation/site-alias/zipball/1cc54f0cf8ff019c07ebc8b4ba557aacfd0a16c2", + "reference": "1cc54f0cf8ff019c07ebc8b4ba557aacfd0a16c2", "shasum": "" }, "require": { @@ -731,20 +731,20 @@ } ], "description": "Manage alias records for local and remote sites.", - "time": "2019-03-03T20:24:22+00:00" + "time": "2019-03-11T16:02:18+00:00" }, { "name": "consolidation/site-process", - "version": "2.0.0-beta5", + "version": "2.0.0-beta6", "source": { "type": "git", "url": "https://github.com/consolidation/site-process.git", - "reference": "de0dd63e67ba4d9b96f28a2258d776dfaaa21f19" + "reference": "0a588d0db653b8d8d1af4e50f996a575d9d83687" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/consolidation/site-process/zipball/de0dd63e67ba4d9b96f28a2258d776dfaaa21f19", - "reference": "de0dd63e67ba4d9b96f28a2258d776dfaaa21f19", + "url": "https://api.github.com/repos/consolidation/site-process/zipball/0a588d0db653b8d8d1af4e50f996a575d9d83687", + "reference": "0a588d0db653b8d8d1af4e50f996a575d9d83687", "shasum": "" }, "require": { @@ -803,7 +803,7 @@ } ], "description": "A thin wrapper around the Symfony Process Component that allows applications to use the Site Alias library to specify the target for a remote call.", - "time": "2019-03-03T21:22:40+00:00" + "time": "2019-03-09T06:40:54+00:00" }, { "name": "container-interop/container-interop", @@ -1729,7 +1729,7 @@ }, { "name": "Gert de Pagter", - "email": "BackEndTea@gmail.com" + "email": "backendtea@gmail.com" } ], "description": "Symfony polyfill for ctype functions", @@ -6285,17 +6285,6 @@ { "name": "webflo/drupal-core-strict", "version": "8.6.x-dev", - "source": { - "type": "git", - "url": "https://github.com/webflo/drupal-core-strict.git", - "reference": "77919809646d790f8060b3c3fc695ae2b5dca6c4" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/webflo/drupal-core-strict/zipball/77919809646d790f8060b3c3fc695ae2b5dca6c4", - "reference": "77919809646d790f8060b3c3fc695ae2b5dca6c4", - "shasum": "" - }, "require": { "asm89/stack-cors": "1.2.0", "composer/installers": "v1.5.0", diff --git a/includes/backend.inc b/includes/backend.inc index 3f241ce4ed..93ce984c52 100644 --- a/includes/backend.inc +++ b/includes/backend.inc @@ -283,7 +283,7 @@ function _drush_backend_integrate($data, $backend_options, $outputted) { // Drush (version 4.x) that does not send backend packets, then we will // not have processed the log entries yet, and must print them here. $received_packets = drush_get_context('DRUSH_RECEIVED_BACKEND_PACKETS', FALSE); - if (is_array($data['log']) && $backend_options['log'] && (!$received_packets)) { + if (is_array($data['log']) && !empty($backend_options['log']) && (!$received_packets)) { foreach($data['log'] as $log) { $message = is_array($log['message']) ? implode("\n", $log['message']) : $log['message']; if (isset($backend_options['#output-label'])) { @@ -303,7 +303,7 @@ function _drush_backend_integrate($data, $backend_options, $outputted) { if (drush_cmp_error('DRUSH_APPLICATION_ERROR') && !empty($data['output'])) { drush_set_error("DRUSH_APPLICATION_ERROR", dt("Output from failed command :\n !output", ['!output' => $data['output']])); } - elseif ($backend_options['output']) { + elseif (!empty($backend_options['output'])) { _drush_backend_print_output($data['output'], $backend_options); } } diff --git a/src/Commands/sql/SqlSyncCommands.php b/src/Commands/sql/SqlSyncCommands.php index d819a941ae..942420c4e3 100644 --- a/src/Commands/sql/SqlSyncCommands.php +++ b/src/Commands/sql/SqlSyncCommands.php @@ -113,10 +113,14 @@ public function databaseName(SiteAlias $record) return 'simulated_db'; } - $process = $this->processManager()->drush($record, 'core-status', ['db-name'], ['format' => 'string']); + $process = $this->processManager()->drush($record, 'core-status', ['db-name'], ['format' => 'json']); $process->setSimulated(false); $process->mustRun(); - return trim($process->getOutput()); + $data = $process->getOutputAsJson(); + if (!isset($data['db-name'])) { + throw new \Exception('Could not look up database name for ' . $record->name()); + } + return trim($data['db-name']); } /** diff --git a/sut/drush/sites/self.site.yml b/sut/drush/sites/self.site.yml new file mode 100644 index 0000000000..223f5c0506 --- /dev/null +++ b/sut/drush/sites/self.site.yml @@ -0,0 +1,3 @@ +fake: + root: /path/to/fake/root + uri: https://fake.example.com diff --git a/tests/functional/RsyncTest.php b/tests/functional/RsyncTest.php index 55a5b1ded2..c57aaaaaab 100644 --- a/tests/functional/RsyncTest.php +++ b/tests/functional/RsyncTest.php @@ -9,7 +9,7 @@ * @group commands * @group slow */ -class RsyncCase extends CommandUnishTestCase +class RsyncTest extends CommandUnishTestCase { public function setUp() @@ -34,21 +34,6 @@ public function testRsyncSimulated() 'alias-path' => __DIR__ . '/resources/alias-fixtures', ]; - // Test simulated simple rsync with two local sites - $this->drush('rsync', ['@example.stage', '@example.dev'], $options, null, null, self::EXIT_SUCCESS, '2>&1'); - $expected = "[notice] Simulating: rsync -e 'ssh ' -akz /path/to/stage /path/to/dev"; - $this->assertOutputEquals($expected); - - // Test simulated rsync with relative paths - $this->drush('rsync', ['@example.dev:files', '@example.stage:files'], $options, null, null, self::EXIT_SUCCESS, '2>&1'); - $expected = "[notice] Simulating: rsync -e 'ssh ' -akz /path/to/dev/files /path/to/stage/files"; - $this->assertOutputEquals($expected); - - // Test simulated rsync on local machine with a remote target - $this->drush('rsync', ['@example.dev:files', '@example.live:files'], $options, null, null, self::EXIT_SUCCESS, '2>&1'); - $expected = "[notice] Simulating: rsync -e 'ssh -o PasswordAuthentication=example' -akz /path/to/dev/files www-admin@service-provider.com:/path/on/service-provider/files"; - $this->assertOutputEquals($expected); - // Test simulated backend invoke. // Note that command-specific options are not processed for remote // targets. The aliases are not interpreted at all until they recache diff --git a/tests/integration/RsyncTest.php b/tests/integration/RsyncTest.php new file mode 100644 index 0000000000..7efc3254e4 --- /dev/null +++ b/tests/integration/RsyncTest.php @@ -0,0 +1,60 @@ +isWindows()) { + $this->markTestSkipped('rsync paths may not contain colons on Windows.'); + } + + $options = [ + 'simulate' => true, + 'alias-path' => __DIR__ . '/../functional/resources/alias-fixtures', + ]; + + // Test simulated simple rsync between two imaginary files / directories + $this->drush('rsync', ['a', 'b'], $options, null, null, self::EXIT_SUCCESS, '2>&1'); + $expected = "[notice] Simulating: rsync -e 'ssh ' -akz a b"; + $this->assertErrorOutputEquals($expected); + + // Test simulated simple rsync with two local sites + $this->drush('rsync', ['@example.stage', '@example.dev'], $options, null, null, self::EXIT_SUCCESS, '2>&1'); + $expected = "[notice] Simulating: rsync -e 'ssh ' -akz /path/to/stage /path/to/dev"; + $this->assertErrorOutputEquals($expected); + + // Test simulated rsync with relative paths + $this->drush('rsync', ['@example.dev:files', '@example.stage:files'], $options, null, null, self::EXIT_SUCCESS, '2>&1'); + $expected = "[notice] Simulating: rsync -e 'ssh ' -akz /path/to/dev/files /path/to/stage/files"; + $this->assertErrorOutputEquals($expected); + + // Test simulated rsync on local machine with a remote target + $this->drush('rsync', ['@example.dev:files', '@example.live:files'], $options, null, null, self::EXIT_SUCCESS, '2>&1'); + $expected = "[notice] Simulating: rsync -e 'ssh -o PasswordAuthentication=example' -akz /path/to/dev/files www-admin@service-provider.com:/path/on/service-provider/files"; + $this->assertErrorOutputEquals($expected); + } + + public function testRsyncSimulatedWithSelfAlias() + { + $options = [ + 'simulate' => true, + ]; + + // Test simulated simple rsync between two imaginary files / directories + $this->drush('rsync', ['a', '@self.fake:b'], $options, null, null, self::EXIT_SUCCESS, '2>&1'); + $expected = "[notice] Simulating: rsync -e 'ssh ' -akz a /path/to/fake/root/b"; + $this->assertErrorOutputContains($expected); + } +} diff --git a/tests/unish/UnishIntegrationTestCase.php b/tests/unish/UnishIntegrationTestCase.php index d34b4875ed..5239ddc11a 100644 --- a/tests/unish/UnishIntegrationTestCase.php +++ b/tests/unish/UnishIntegrationTestCase.php @@ -186,4 +186,25 @@ protected function assertErrorOutputEquals($expected, $filter = '') } $this->assertEquals($expected, $output); } + + /** + * Checks that the error output contains the expected output. + * + * This matches against a simplified version of the actual output that has + * absolute paths and duplicate whitespace removed, to avoid false negatives + * on minor differences. + * + * @param string $expected + * The expected output. + * @param string $filter + * Optional regular expression that should be ignored in the error output. + */ + protected function assertErrorOutputContains($expected, $filter = '') + { + $output = $this->getSimplifiedErrorOutput(); + if (!empty($filter)) { + $output = preg_replace($filter, '', $output); + } + $this->assertContains($expected, $output); + } }