From ef5e2b6a1b810d7fd42bc8c503369bdc0af94ebd Mon Sep 17 00:00:00 2001 From: Vincent Massaro Date: Tue, 8 Jan 2013 09:09:15 -0500 Subject: [PATCH 2/2] Applying patch from http://drupal.org/node/1277484#comment-6909184 --- commands/core/archive.drush.inc | 110 ++++++++++++++++++++++++++---------- tests/archiveDumpTest.php | 121 +++++++++++++++++++++++++++++++--------- 2 files changed, 176 insertions(+), 55 deletions(-) diff --git a/commands/core/archive.drush.inc b/commands/core/archive.drush.inc index b7f3d80..9b84571 100644 --- a/commands/core/archive.drush.inc +++ b/commands/core/archive.drush.inc @@ -21,6 +21,7 @@ function archive_drush_command() { 'generatorversion' => 'The generator version number to store in the MANIFEST file. The default is ' . DRUSH_VERSION . '.', 'pipe' => 'Only print the destination of the archive. Useful for scripts that don\'t pass --destination.', 'preserve-symlinks' => 'Preserve symbolic links.', + 'no-core' => 'Exclude Drupal core, so the backup only contains the site specific stuff.', 'tar-options' => 'Options passed thru to the tar command.', ), 'examples' => array( @@ -65,7 +66,9 @@ function archive_drush_command() { * Command callback. Generate site archive file. */ function drush_archive_dump($sites_subdirs = '@self') { + $include_platform = !drush_get_option('no-core', FALSE); $tar = drush_get_tar_executable(); + $sites = array(); $aliases = drush_sitealias_resolve_sitespecs(explode(',', $sites_subdirs)); foreach ($aliases as $key => $alias) { @@ -169,22 +172,25 @@ function drush_archive_dump($sites_subdirs = '@self') { $docroot_path = realpath(drush_get_context('DRUSH_DRUPAL_ROOT')); $docroot = basename($docroot_path); $workdir = dirname($docroot_path); - $dereference = (drush_get_option('preserve-symlinks', FALSE)) ? '' : '--dereference '; - // Convert destination path to Unix style for tar on MinGW - see http://drupal.org/node/1844224 - if (drush_is_mingw()) { - $destination_orig = $destination; - $destination = str_replace('\\', '/', $destination); - $destination = preg_replace('$^([a-zA-Z]):$', '/$1', $destination); - } - // Archive Drupal core, excluding sites dir. - drush_shell_cd_and_exec($workdir, "$tar {$tar_extra_options} --exclude \"{$docroot}/sites\" {$dereference}-cf %s %s", $destination, $docroot); - // Add sites/all to the same archive. - drush_shell_cd_and_exec($workdir, "$tar {$tar_extra_options} {$dereference}-rf %s %s", $destination, "{$docroot}/sites/all"); - // Add special files in sites/ to the archive. - $files_to_add = array('sites/README.txt', 'sites/sites.php', 'sites/example.sites.php', 'sites/default/default.settings.php'); - foreach ($files_to_add as $file_to_add) { - if (file_exists($file_to_add)) { - drush_shell_cd_and_exec($workdir, "$tar {$dereference}-rf %s %s", $destination, $docroot . '/' . $file_to_add); + + if ($include_platform) { + $dereference = (drush_get_option('preserve-symlinks', FALSE)) ? '' : '--dereference '; + // Convert destination path to Unix style for tar on MinGW - see http://drupal.org/node/1844224 + if (drush_is_mingw()) { + $destination_orig = $destination; + $destination = str_replace('\\', '/', $destination); + $destination = preg_replace('$^([a-zA-Z]):$', '/$1', $destination); + } + // Archive Drupal core, excluding sites dir. + drush_shell_cd_and_exec($workdir, "$tar {$tar_extra_options} --exclude \"{$docroot}/sites\" {$dereference}-cf %s %s", $destination, $docroot); + // Add sites/all to the same archive. + drush_shell_cd_and_exec($workdir, "$tar {$tar_extra_options} {$dereference}-rf %s %s", $destination, "{$docroot}/sites/all"); + // Add special files in sites/ to the archive. + $files_to_add = array('sites/README.txt', 'sites/sites.php', 'sites/example.sites.php', 'sites/default/default.settings.php'); + foreach ($files_to_add as $file_to_add) { + if (file_exists($file_to_add)) { + drush_shell_cd_and_exec($workdir, "$tar {$dereference}-rf %s %s", $destination, $docroot . '/' . $file_to_add); + } } } @@ -220,6 +226,7 @@ function drush_archive_dump($sites_subdirs = '@self') { 'generatorversion' => drush_get_option('generatorversion', DRUSH_VERSION), 'description' => drush_get_option('description', ''), 'tags' => drush_get_option('tags', ''), + 'archiveformat' => ($include_platform ? 'platform' : 'site'), ); $contents = drush_export_ini(array('Global' => $platform)); @@ -308,17 +315,13 @@ function drush_archive_restore($file, $site_id = NULL) { } } else { - // No manifest. Try to find docroot and DB dump file. - $db_file = drush_scan_directory($tmp, '/\.sql$/', array('.', '..', 'CVS'), 0, 0); - $directories = glob($tmp . '/*' , GLOB_ONLYDIR); - $ini = array( - 'Global' => array(), - 'default' => array( - 'docroot' => reset($directories), - 'sitedir' => 'sites/default', - 'database-default-file' => key($db_file), - ), - ); + $ini = drush_archive_guess_manifest($tmp); + } + + // Backward compatibility: 'archiveformat' did not exist + // in older versions of archive-dump. + if (!isset( $ini['Global']['archiveformat'])) { + $ini['Global']['archiveformat'] = 'platform'; } // Grab the first site in the Manifest and move docroot to destination. @@ -327,15 +330,29 @@ function drush_archive_restore($file, $site_id = NULL) { $first = array_shift($ini_tmp); $docroot = basename($first['docroot']); $destination = drush_get_option('destination', realpath('.') . "/$docroot"); - if (!drush_move_dir("$tmp/$docroot", $destination, drush_get_option('overwrite'))) { - return drush_set_error('DRUSH_ARCHIVE_UNABLE _RESTORE_FILES', dt('Unable to restore files to !dest', array('!dest' => $destination))); + + if ($ini['Global']['archiveformat'] == 'platform') { + // Move the whole platform inplace at once. + if (!drush_move_dir("$tmp/$docroot", $destination, drush_get_option('overwrite'))) { + return drush_set_error('DRUSH_ARCHIVE_UNABLE_TO_RESTORE_FILES', dt('Unable to restore platform to !dest', array('!dest' => $destination))); + } + } + else { + // When no platform is included we do this on a per-site basis. } // Loop over sites and restore databases and append to settings.php. foreach ($ini as $section => $site) { if ($section != 'Global' && (is_null($site_id) || $section == $site_id) && !empty($site['database-default-file'])) { + $site_destination = $destination . '/' . $site['sitedir']; + // Restore site, in case not already done above. + if ($ini['Global']['archiveformat'] == 'site') { + if (!drush_move_dir("$tmp/$docroot/" . $site['sitedir'], $site_destination, drush_get_option('overwrite'))) { + return drush_set_error('DRUSH_ARCHIVE_UNABLE_TO_RESTORE_FILES', dt('Unable to restore site to !dest', array('!dest' => $site_destination))); + } + } - // Restore database + // Restore database. $sql_file = $tmp . '/' . $site['database-default-file']; if ($db_url = drush_get_option('db-url')) { if (empty($site_id) && count($ini) >= 3) { @@ -378,6 +395,7 @@ function drush_archive_restore($file, $site_id = NULL) { } } drush_log(dt('Archive restored to !dest', array('!dest' => $destination)), 'ok'); + return $destination; } @@ -401,3 +419,35 @@ function archive_archive_restore_complete() { ), ); } + +/** + * Try to find docroot and DB dump file in an extracted archive. + * + * @param string $path The location of the extracted archive. + * @return array The manifest data. + */ +function drush_archive_guess_manifest($path) { + $db_file = drush_scan_directory($path, '/\.sql$/', array('.', '..', 'CVS'), 0, 0); + + if (file_exists($path . '/index.php')) { + $docroot = './'; + } + else { + $directories = glob($path . '/*' , GLOB_ONLYDIR); + $docroot = reset($directories); + } + + $ini = array( + 'Global' => array( + // Very crude detection of a platform... + 'archiveformat' => (drush_drupal_version($docroot) ? 'platform' : 'site'), + ), + 'default' => array( + 'docroot' => $docroot, + 'sitedir' => 'sites/default', + 'database-default-file' => key($db_file), + ), + ); + + return $ini; +} diff --git a/tests/archiveDumpTest.php b/tests/archiveDumpTest.php index a046c95..a078a06 100644 --- a/tests/archiveDumpTest.php +++ b/tests/archiveDumpTest.php @@ -6,56 +6,127 @@ * @group commands */ class archiveDumpCase extends Drush_CommandTestCase { + /** + * archive-dump behaves slightly different when archiving a site installed + * at sites/default so we make the test to use sites/default as the + * installation directory instead of default sites/dev. + */ + const uri = 'default'; - /* - * Test dump and extraction. - * - * archive-dump behaves slightly different when archiving a site installed at sites/default - * so we make the test to use sites/default as the installation directory. + /** + * Install a site and dump it to an archive. */ - public function testArchiveDump() { - $uri = 'default'; - $this->fetchInstallDrupal($uri, TRUE, UNISH_DRUPAL_MAJOR_VERSION, 'testing'); + private function archiveDump($no_core) { + $this->fetchInstallDrupal(self::uri, TRUE, UNISH_DRUPAL_MAJOR_VERSION, 'testing'); $root = $this->webroot(); - $docroot = basename($root); - - $dump_dest = "dump.tar.gz"; + $dump_dest = UNISH_SANDBOX . DIRECTORY_SEPARATOR . 'dump.tar.gz'; $options = array( 'root' => $root, - 'uri' => $uri, + 'uri' => self::uri, 'yes' => NULL, 'destination' => $dump_dest, + 'overwrite' => NULL, ); - $this->drush('archive-dump', array($uri), $options); - $exec = sprintf('file %s%s%s', UNISH_SANDBOX, DIRECTORY_SEPARATOR, $dump_dest); - $this->execute($exec); - $output = $this->getOutput(); - $sep = self::is_windows() ? ';' : ':'; - $expected = UNISH_SANDBOX . DIRECTORY_SEPARATOR . "dump.tar.gz$sep gzip compressed data, from"; + if ($no_core) { + $options['no-core'] = NULL; + } + $this->drush('archive-dump', array(self::uri), $options); - $this->assertStringStartsWith($expected, $output); + return $dump_dest; + } - // Untar it, make sure it looks right. + /** + * Untar an archive and return the path to the untarred folder. + */ + private function unTar($dump_dest) { $untar_dest = UNISH_SANDBOX . DIRECTORY_SEPARATOR . 'untar'; + unish_file_delete_recursive($untar_dest); $tar = self::get_tar_executable(); - $exec = sprintf("mkdir %s && cd %s && $tar -xzf %s%s%s", $untar_dest, $untar_dest, UNISH_SANDBOX, DIRECTORY_SEPARATOR, $dump_dest); + $exec = sprintf("mkdir %s && cd %s && $tar -xzf %s", $untar_dest, $untar_dest, $dump_dest); $this->execute($exec); + + return $untar_dest; + } + + /** + * Test if tarball generated by archive-dump looks right. + */ + public function testArchiveDump() { + $dump_dest = $this->archiveDump(FALSE); + $docroot = basename($this->webroot()); + + // Check the dump file is a gzip file. + $exec = sprintf('file %s', $dump_dest); + $this->execute($exec); + $output = $this->getOutput(); + $sep = self::is_windows() ? ';' : ':'; + $expected = $dump_dest . "$sep gzip compressed data, from"; + $this->assertStringStartsWith($expected, $output); + + // Untar the archive and make sure it looks right. + $untar_dest = $this->unTar($dump_dest); + if (strpos(UNISH_DB_URL, 'mysql') !== FALSE) { - $this->execute(sprintf('head %s/unish_%s.sql | grep "MySQL dump"', $untar_dest, $uri)); + $this->execute(sprintf('head %s/unish_%s.sql | grep "MySQL dump"', $untar_dest, self::uri)); } $this->assertFileExists($untar_dest . '/MANIFEST.ini'); $this->assertFileExists($untar_dest . '/' . $docroot); - // Restore archive and verify that the file structure is identical. + return $dump_dest; + } + + /** + * Test archive-restore. + * + * Restore the archive generated in testArchiveDump() and verify that the + * directory contents are identical. + * + * @depends testArchiveDump + */ + public function testArchiveRestore($dump_dest) { require_once dirname(__FILE__) . '/../includes/filesystem.inc'; $restore_dest = UNISH_SANDBOX . DIRECTORY_SEPARATOR . 'restore'; $options = array( 'yes' => NULL, 'destination' => $restore_dest, ); - $this->drush('archive-restore', array(UNISH_SANDBOX . DIRECTORY_SEPARATOR . $dump_dest), $options); - $original_codebase = drush_dir_md5($root); + $this->drush('archive-restore', array($dump_dest), $options); + $original_codebase = drush_dir_md5($this->webroot()); $restored_codebase = drush_dir_md5($restore_dest); $this->assertEquals($original_codebase, $restored_codebase); } + + /** + * Test if tarball generated by archive-dump with --no-core looks right. + */ + public function testArchiveDumpNoCore() { + $dump_dest = $this->archiveDump(TRUE); + $untar_dest = $this->unTar($dump_dest); + $docroot = basename($this->webroot()); + $this->assertFileExists($untar_dest . '/MANIFEST.ini'); + $this->assertFileExists($untar_dest . '/' . $docroot); + $this->assertFileNotExists($untar_dest . '/' . $docroot . '/modules', 'No modules directory should exist with --no-core'); + + return $dump_dest; + } + + /** + * Test archive-restore for a site archive (--no-core). + * + * @depends testArchiveDumpNoCore + */ + public function testArchiveRestoreNoCore($dump_dest) { + $root = $this->webroot(); + $original_codebase = drush_dir_md5($root); + unish_file_delete_recursive($root . '/sites/' . self::uri); + + $options = array( + 'yes' => NULL, + 'destination' => $root, + ); + $this->drush('archive-restore', array($dump_dest), $options); + + $restored_codebase = drush_dir_md5($root); + $this->assertEquals($original_codebase, $restored_codebase); + } } -- 1.8.0.2