function drush_archive_dump

8.0.x archive.drush.inc drush_archive_dump($sites_subdirs = '@self')
6.x archive.drush.inc drush_archive_dump($sites_subdirs = '@self')
7.x archive.drush.inc drush_archive_dump($sites_subdirs = '@self')
4.x archive.drush.inc drush_archive_dump($sites_subdirs = '@self')
5.x archive.drush.inc drush_archive_dump($sites_subdirs = '@self')
master archive.drush.inc drush_archive_dump($sites_subdirs = '@self')

Command callback. Generate site archive file.

File

commands/core/archive.drush.inc, line 73
An early implementation of Site Archive dump/restore. See http://groups.drupal.org/site-archive-format.

Code

function drush_archive_dump($sites_subdirs = '@self') {
  $include_platform = !drush_get_option('no-core', FALSE);
  $tar = drush_get_tar_executable();

  $sites = array();
  list($aliases, $not_found) = drush_sitealias_resolve_sitespecs(explode(',', $sites_subdirs));
  if (!empty($not_found)) {
    drush_log(dt("Some site subdirectories are not valid Drupal sites: @list", array("@list" => implode(', ', $not_found))), LogLevel::WARNING);
  }
  foreach ($aliases as $key => $alias) {
    $sites[$key] = $alias;

    if (($db_record = sitealias_get_databases_from_record($alias))) {
      $sites[$key]['databases'] = $db_record;
    }
    else {
      $sites[$key]['databases'] = array();
      drush_log(dt('DB definition not found for !alias', array('!alias' => $key)), LogLevel::NOTICE);
    }
  }

  // The user can specify a destination filepath or not. That filepath might
  // end with .gz, .tgz, or something else. At the end of this command we will
  // gzip a file, and we want it to end up with the user-specified name (if
  // any), but gzip renames files and refuses to compress files ending with
  // .gz and .tgz, making our lives difficult. Solution:
  //
  // 1. Create a unique temporary base name to which gzip WILL append .gz.
  // 2. If no destination is provided, set $dest_dir to a backup directory and
  // $final_destination to be the unique name in that dir.
  // 3. If a destination is provided, set $dest_dir to that directory and
  // $final_destination to the exact name given.
  // 4. Set $destination, the actual working file we will build up, to the
  // unqiue name in $dest_dir.
  // 5. After gzip'ing $destination, rename $destination.gz to
  // $final_destination.
  //
  // Sheesh.

  // Create the unique temporary name.
  $prefix = 'none';
  if (!empty($sites)) {
    $first = current($sites);
    if (!empty($first['databases']['default']['default']['database'])) {
      $prefix = count($sites) > 1 ? 'multiple_sites' : str_replace('/', '-', $first['databases']['default']['default']['database']);
    }
  }
  $date = gmdate('Ymd_His');
  $temp_dest_name = "$prefix.$date.tar";

  $final_destination = drush_get_option('destination');
  if (!$final_destination) {
    // No destination provided.
    $backup = drush_include_engine('version_control', 'backup');
    // TODO: this standard Drush pattern leads to a slightly obtuse directory structure.
    $dest_dir = $backup->prepare_backup_dir('archive-dump');
    if (empty($dest_dir)) {
      $dest_dir = drush_tempdir();
    }
    $final_destination = "$dest_dir/$temp_dest_name.gz";
  }
  else {
    // Use the supplied --destination. If it is relative, resolve it
    // relative to the directory in which drush was invoked.
    $command_cwd = getcwd();
    drush_op('chdir', drush_get_context('DRUSH_OLDCWD', getcwd()));
    // This doesn't perform realpath on the basename, but that's okay. This is
    // not path-based security. We just use it for checking for perms later.
    drush_mkdir(dirname($final_destination));
    $dest_dir = realpath(dirname($final_destination));
    $final_destination = $dest_dir . '/' . basename($final_destination);
    drush_op('chdir', $command_cwd);
  }

  // $dest_dir is either the backup directory or specified directory. Set our
  // working file.
  $destination = "$dest_dir/$temp_dest_name";

  // Validate the FINAL destination. It should be a file that does not exist
  // (unless --overwrite) in a writable directory (and a writable file if
  // it exists). We check all this up front to avoid failing after a long
  // dump process.
  $overwrite = drush_get_option('overwrite');
  $dest_dir = dirname($final_destination);
  $dt_args = array(
    '!file' => $final_destination,
    '!dir' => $dest_dir,
  );
  if (is_dir($final_destination)) {
    drush_set_error('DRUSH_ARCHIVE_DEST_IS_DIR', dt('Destination !file must be a file, not a directory.', $dt_args));
    return;
  }
  else if (file_exists($final_destination)) {
    if (!$overwrite) {
      drush_set_error('DRUSH_ARCHIVE_DEST_EXISTS', dt('Destination !file exists; specify --overwrite to overwrite.', $dt_args));
      return;
    }
    else if (!is_writable($final_destination)) {
      drush_set_error('DRUSH_ARCHIVE_DEST_FILE_NOT_WRITEABLE', dt('Destination !file is not writable.', $dt_args));
      return;
    }
  }
  else if (!is_writable($dest_dir)) {
    drush_set_error('DRUSH_ARCHIVE_DEST_DIR_NOT_WRITEABLE', dt('Destination directory !dir is not writable.', $dt_args));
    return;
  }

  // Get the extra options for tar, if any
  $tar_extra_options = drush_sitealias_evaluate_paths_in_options(drush_get_option('tar-options', ''));

  // Start adding codebase to the archive.
  $docroot_path = realpath(drush_get_context('DRUSH_DRUPAL_ROOT'));
  $docroot = basename($docroot_path);
  $workdir = dirname($docroot_path);

  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. Last 2 items are new in Drupal8.
    $files_to_add = array(
      'sites/README.txt',
      'sites/sites.php',
      'sites/example.sites.php',
      'sites/development.services.yml',
      'sites/example.settings.local.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);
      }
    }
  }

  $tmp = drush_tempdir();
  $all_dbs = array();
  // Dump the default database for each site and add to the archive.
  foreach ($sites as $key => $alias) {
    if (isset($alias['databases']['default']['default'])) {
      $db_spec = $alias['databases']['default']['default'];
      // Use a subdirectory name matching the docroot name.
      drush_mkdir("{$tmp}/{$docroot}");

      // Ensure uniqueness by prefixing key if needed. Remove path delimiters.
      $dbname = str_replace(DIRECTORY_SEPARATOR, '-', $db_spec['database']);
      $result_file = count($sites) == 1 ? "$tmp/$dbname.sql" : str_replace('@', '', "$tmp/$key-$dbname.sql");

      $all_dbs[$key] = array(
        'file' => basename($result_file),
        'driver' => $db_spec['driver'],
      );
      $sql = drush_sql_get_class($db_spec);
      $sql->dump($result_file);
      drush_shell_cd_and_exec($tmp, "$tar {$tar_extra_options} --dereference -rf %s %s", $destination, basename($result_file));
    }
  }

  // Build a manifest file AND add sites/$subdir to archive as we go.
  $platform = array(
    'datestamp' => time(),
    'formatversion' => '1.0',
    'generator' => drush_get_option('generator', 'Drush archive-dump'),
    '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));

  $i = 0;
  foreach ($sites as $key => $alias) {
    $status = drush_invoke_process($alias, 'core-status', array(), array(), array('integrate' => FALSE));
    if (!$status || $status['error_log']) {
      drush_log(dt('Unable to determine sites directory for !alias', array('!alias' => $key)), LogLevel::WARNING);
    }

    // Add the site specific directory to archive.
    if (!empty($status['object']['%paths']['%site'])) {
      drush_shell_cd_and_exec($workdir, "$tar {$tar_extra_options} --dereference -rf %s %s", $destination, "{$docroot}/sites/" . basename($status['object']['%paths']['%site']));
    }

    $site = array(
      'docroot' => DRUPAL_ROOT,
      'sitedir' => @$status['object']['%paths']['%site'],
      'files-public' => @$status['object']['%paths']['%files'],
      'files-private' => @$status['object']['%paths']['%private'],
    );
    $site["database-default-file"] = $all_dbs[$key]['file'];
    $site["database-default-driver"] = $all_dbs[$key]['driver'];
    // The section title is the sites subdirectory name.
    $info[basename($site['sitedir'])] = $site;
    $contents .= "\n" . drush_export_ini($info);
    unset($info);
    $i++;
  }
  file_put_contents("{$tmp}/MANIFEST.ini", $contents);

  // Add manifest to archive.
  drush_shell_cd_and_exec($tmp, "$tar --dereference -rf %s %s", $destination, 'MANIFEST.ini');

  // Ensure that default/default.settings.php is in the archive. This is needed
  // by site-install when restoring a site without any DB.
  // NOTE: Windows tar file replace operation is broken so we have to check if file already exists.
  // Otherwise it will corrupt the archive.
  $res = drush_shell_cd_and_exec($workdir, "$tar -tf %s %s", $destination, $docroot . '/sites/default/default.settings.php');
  $output = drush_shell_exec_output();
  if (!$res || !isset($output[0]) || empty($output[0])) {
    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);

  // gzip appends .gz unless the name already ends in .gz, .tgz, or .taz.
  if ("{$destination}.gz" != $final_destination) {
    drush_move_dir("{$destination}.gz", $final_destination, $overwrite);
  }

  drush_log(dt('Archive saved to !dest', array('!dest' => $final_destination)), LogLevel::OK);
  return $final_destination;
}