From 4777bf7e0b217fc84ee309aba9e11d7893b384c7 Mon Sep 17 00:00:00 2001 From: Vincent Massaro Date: Tue, 8 Jan 2013 09:18:38 -0500 Subject: [PATCH] Combined patches from #1277484 and #844224 --- commands/core/archive.drush.inc | 109 ++++++++++++++++++++++++++++-------- drush | 28 +++++++--- includes/drush.inc | 2 +- includes/environment.inc | 6 +- tests/archiveDumpTest.php | 121 +++++++++++++++++++++++++++++++--------- 5 files changed, 207 insertions(+), 59 deletions(-) diff --git a/commands/core/archive.drush.inc b/commands/core/archive.drush.inc index afb6d77..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,16 +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 '; - // 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); + } } } @@ -214,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)); @@ -257,6 +270,11 @@ function drush_archive_dump($sites_subdirs = '@self') { drush_shell_cd_and_exec($workdir, "$tar --dereference -vrf %s %s", $destination, $docroot . '/sites/default/default.settings.php'); } + // Switch back to original destination in case it was modified for tar on MinGW. + if (!empty($destination_orig)) { + $destination = $destination_orig; + } + // Compress the archive drush_shell_exec("gzip --no-name -f %s", $destination); @@ -297,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. @@ -316,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) { @@ -367,6 +395,7 @@ function drush_archive_restore($file, $site_id = NULL) { } } drush_log(dt('Archive restored to !dest', array('!dest' => $destination)), 'ok'); + return $destination; } @@ -390,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/drush b/drush index 8e3c806..aa3805a 100755 --- a/drush +++ b/drush @@ -11,6 +11,14 @@ SELF_DIRNAME="`dirname -- "$0"`" SELF_PATH="`cd -P -- "$SELF_DIRNAME" && pwd -P`/`basename -- "$0"`" +# Decide if we are running a Unix shell on Windows +case "`uname -a`" in + CYGWIN*) + CYGWIN=1 ;; + MINGW*) + MINGW=1 ;; +esac + # Resolve symlinks - this is the equivalent of "readlink -f", but also works with non-standard OS X readlink. while [ -h "$SELF_PATH" ]; do # 1) cd to directory of the symlink @@ -25,10 +33,9 @@ done # Build the path to drush.php. SCRIPT_PATH="`dirname "$SELF_PATH"`/drush.php" -case "`uname -a`" in - CYGWIN*) - SCRIPT_PATH="`cygpath -w -a -- "$SCRIPT_PATH"`" ;; -esac +if [ ! -z "$CYGWIN" ] ; then + SCRIPT_PATH="`cygpath -w -a -- "$SCRIPT_PATH"`" +fi # If not exported, try to determine and export the number of columns. # We do not want to run `tput cols` if $TERM is empty or "dumb", because @@ -37,7 +44,8 @@ esac # error message is suppressed, but tput cols becomes confused about the # terminal and prints out the default value (80). if [ -z $COLUMNS ] && [ -n "$TERM" ] && [ "$TERM" != dumb ] && [ ! -z "`which tput`" ] ; then - # Note to cygwin users: install the ncurses package to get tput command. + # Note to cygwin/mingw/msys users: install the ncurses package to get tput command. + # Note to mingw/msys users: there is no precompiled ncurses package. if COLUMNS="`tput cols`"; then export COLUMNS fi @@ -58,8 +66,7 @@ else fi # On MSYSGIT, we need to use "php", not the full path to php - # See http://drupal.org/node/1659014 for an explanation of the comparison op. - if [ ! -z "$MSYSTEM" ] && [ "x${MSYSTEM}" != "x${MSYSTEM#MINGW}" ] ; then + if [ ! -z "$MINGW" ] ; then php="php" fi fi @@ -67,6 +74,13 @@ fi # Check to see if the user has provided a php.ini file or drush.ini file in any conf dir # Last found wins, so search in reverse priority order for conf_dir in "`dirname "$SELF_PATH"`" /etc/drush $HOME/.drush ; do + if [ ! -d $conf_dir ] ; then + continue + fi + # Handle paths that don't start with a drive letter on MinGW shell. Equivalent to cygpath on Cygwin. + if [ ! -z "$MINGW" ] ; then + conf_dir=`sh -c "cd $conf_dir; pwd -W"` + fi if [ -f "$conf_dir/php.ini" ] ; then drush_php_ini=$conf_dir/php.ini fi diff --git a/includes/drush.inc b/includes/drush.inc index e1945a0..a20573c 100644 --- a/includes/drush.inc +++ b/includes/drush.inc @@ -868,7 +868,7 @@ function drush_download_file($url, $destination = FALSE, $cache_duration = 0) { function _drush_download_file($url, $destination, $overwrite = TRUE) { static $use_wget; if ($use_wget === NULL) { - $use_wget = drush_shell_exec('which wget'); + $use_wget = drush_shell_exec('wget --version'); } $destination_tmp = drush_tempnam('download_file'); diff --git a/includes/environment.inc b/includes/environment.inc index 9198ae8..fc35de5 100644 --- a/includes/environment.inc +++ b/includes/environment.inc @@ -644,14 +644,16 @@ function drush_is_mingw($os = NULL) { * Return tar executable name specific for the current OS */ function drush_get_tar_executable() { - return drush_is_windows() ? "bsdtar.exe" : "tar"; + return drush_is_windows() ? (drush_is_mingw() ? "tar.exe" : "bsdtar.exe") : "tar"; } /** * Checks if the operating system has bash. + * + * MinGW has bash, but PHP isn't part of MinGW and hence doesn't run in bash. */ function drush_has_bash($os = NULL) { - return drush_is_cygwin($os) || !drush_is_windows($os); + return (drush_is_cygwin($os) && !drush_is_mingw($os)) || !drush_is_windows($os); } /* 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