public static function setUpBeforeClass()
 {
     parent::setUpBeforeClass();
     if (self::$testConfig->testSite->installationType !== 'standard') {
         throw new \PHPUnit_Framework_SkippedTestSuiteError();
     }
     $testDataPath = __DIR__ . '/../test-data';
     self::$pluginInfo = ['zipfile' => realpath($testDataPath . '/hello-dolly.1.6.zip'), 'url-fragment' => 'hello-dolly', 'name' => 'Hello Dolly', 'affected-path' => 'hello-dolly/*'];
     self::$secondPluginInfo = ['zipfile' => realpath($testDataPath . '/hello-dolly.1.6-2.zip'), 'url-fragment' => 'hello-dolly-2', 'name' => 'Hello Dolly 2', 'affected-path' => 'hello-dolly-2/*'];
     self::$worker->setPluginInfo(self::$pluginInfo);
     self::$worker->setSecondPluginInfo(self::$secondPluginInfo);
     // possibly delete single-file Hello dolly
     try {
         self::$wpAutomation->runWpCliCommand('plugin', 'uninstall', ['hello']);
     } catch (\Exception $e) {
     }
     // possibly delete our testing plugins
     try {
         self::$wpAutomation->runWpCliCommand('plugin', 'uninstall', ['hello-dolly']);
         self::$wpAutomation->runWpCliCommand('plugin', 'uninstall', ['hello-dolly-2']);
     } catch (\Exception $e) {
     }
     $process = new Process("git add -A && git commit -m " . ProcessUtils::escapeshellarg("Plugin setup"), self::$testConfig->testSite->path);
     $process->run();
 }
 /**
  * @test
  */
 public function windowsEscapingPassesStandardTests()
 {
     // @codingStandardsIgnoreLine
     // Inspired by https://github.com/php/php-src/blob/3551083c2c188d3d5de5e58f3bd2624f2abfefc4/ext/standard/tests/general_functions/escapeshellarg_basic-win32.phpt
     // but actually updated for the current behavior (for instance, % used to be replaced with a space,
     // now it's doubled up to %%)
     $this->assertEquals("\"Mr O'Neil\"", ProcessUtils::escapeshellarg("Mr O'Neil", "windows"));
     $this->assertEquals("\"Mr O\\\\'Neil\"", ProcessUtils::escapeshellarg("Mr O\\'Neil", "windows"));
     $this->assertEquals("\"%%FILENAME\"", ProcessUtils::escapeshellarg("%FILENAME", "windows"));
     $this->assertEquals("\"\"", ProcessUtils::escapeshellarg("", "windows"));
 }
 public static function runWpCliCommand($command, $subcommand, $args = [], $cwd = null)
 {
     $cliCommand = "wp {$command}";
     if ($subcommand) {
         $cliCommand .= " {$subcommand}";
     }
     // Colorize the output
     if (defined('WP_CLI') && WP_CLI && \WP_CLI::get_runner()->in_color()) {
         $args['color'] = null;
     }
     foreach ($args as $name => $value) {
         if (is_int($name)) {
             // positional argument
             $cliCommand .= " " . ProcessUtils::escapeshellarg($value);
         } elseif ($value !== null) {
             $cliCommand .= " --{$name}=" . ProcessUtils::escapeshellarg($value);
         } else {
             $cliCommand .= " --{$name}";
         }
     }
     return self::exec($cliCommand, $cwd);
 }
 private static function getPermissionInfo()
 {
     $proc = proc_open('whoami', [['pipe', 'r'], ['pipe', 'w'], ['pipe', 'w']], $pipes);
     $procOpenUser = trim(stream_get_contents($pipes[1]));
     $processInfo = ['exec-user' => exec('whoami'), 'proc_open-user' => $procOpenUser];
     $writeTargets = ['ABSPATH' => ABSPATH, 'WP_CONTENT_DIR' => WP_CONTENT_DIR, 'sys_temp_dir' => sys_get_temp_dir()];
     foreach ($writeTargets as $target => $directory) {
         $filePath = $directory . '/' . '.vp-try-write-php';
         /** @noinspection PhpUsageOfSilenceOperatorInspection */
         @file_put_contents($filePath, "");
         $processInfo['php-can-write'][$target] = is_file($filePath);
         FileSystem::remove($filePath);
         $processInfo['php-can-delete'][$target] = !is_file($filePath);
         $filePath = $directory . '/' . '.vp-try-write-process';
         $process = new Process(sprintf("echo test > %s", ProcessUtils::escapeshellarg($filePath)));
         $process->run();
         $processInfo['process-can-write'][$target] = is_file($filePath);
         try {
             FileSystem::remove($filePath);
             $processInfo['php-can-delete-file-created-by-process'][$target] = !is_file($filePath);
         } catch (IOException $ex) {
             $processInfo['php-can-delete-file-created-by-process'][$target] = false;
         }
     }
     return $processInfo;
 }
 /**
  * Run a Git command, either fully specified (e.g., 'git log') or just by the name (e.g., 'log').
  * The comamnd can contain `sprintf()` markers such as '%s' which are replaced by shell-escaped $args.
  *
  * Note: shell-escaping is actually pretty important even for things that are not paths, like revisions.
  * For example, `git log HEAD^` will not work on Windows, only `git log "HEAD^"` will. So the right
  * approach is to provide `git log %s` as the $command and rev range as $args.
  *
  * @param string $command E.g., 'git log' or 'git add %s' (path will be shell-escaped) or just 'log'
  *   (the "git " part is optional).
  * @param string[] $args Will be shell-escaped and replace sprintf markers in $command
  * @return array array('stdout' => , 'stderr' => )
  */
 private function runShellCommand($command, ...$args)
 {
     // replace (optional) "git " with the configured git binary
     $command = Strings::startsWith($command, "git ") ? substr($command, 4) : $command;
     $command = ProcessUtils::escapeshellarg($this->gitBinary) . " " . $command;
     $escapedArgs = @array_map(['VersionPress\\Utils\\ProcessUtils', 'escapeshellarg'], $args);
     $commandWithArguments = vsprintf($command, $escapedArgs);
     $result = $this->runProcess($commandWithArguments);
     return $result;
 }
 private function tryWrite()
 {
     $filename = ".vp-try-write";
     $testPaths = [ABSPATH, WP_CONTENT_DIR, sys_get_temp_dir()];
     $writable = true;
     foreach ($testPaths as $directory) {
         $filePath = $directory . '/' . $filename;
         /** @noinspection PhpUsageOfSilenceOperatorInspection */
         @file_put_contents($filePath, "");
         $writable &= is_file($filePath);
         FileSystem::remove($filePath);
         // Trying to create file from process (issue #522)
         $process = new Process(sprintf("echo test > %s", ProcessUtils::escapeshellarg($filePath)));
         $process->run();
         $writable &= is_file($filePath);
         try {
             FileSystem::remove($filePath);
         } catch (IOException $ex) {
             $writable = false;
             // the file could not be deleted - the permissions are wrong
         }
     }
     return $writable;
 }
Exemple #7
0
 /**
  * Pulls changes from another clone
  *
  * ## OPTIONS
  *
  * [--from=<name|path|url>]
  * : Where to pull from. Can be a clone name (specified previously during the
  * 'clone' command), a path or a URL. Defaults to 'origin' which is
  * automatically set in every clone by the 'clone' command.
  *
  * [--continue]
  * : Finishes the pull after solving merge conflicts.
  *
  * ## EXAMPLES
  *
  * Let's have a site 'wpsite' and a clone 'myclone' created from it. To pull the changes
  * from the clone back into the main site, use:
  *
  *     wp vp pull --from=myclone
  *
  * When in the clone, the pull can be run without any parameter:
  *
  *     wp vp pull
  *
  * This will pull the changes from 'origin' which was set to the parent site during the
  * 'clone' command.
  *
  */
 public function pull($args = [], $assoc_args = [])
 {
     if (!VersionPress::isActive()) {
         WP_CLI::error('This site is not tracked by VersionPress. Please run "wp vp activate" before cloning / merging.');
     }
     if (isset($assoc_args['continue'])) {
         $process = new Process('git diff --name-only --diff-filter=U', VP_PROJECT_ROOT);
         $process->run();
         if ($process->getConsoleOutput() !== '') {
             WP_CLI::error('There are still some conflicts. Please resolve them and run `wp vp pull --continue` again.');
         }
         $this->finishPull();
         return;
     }
     $remote = isset($assoc_args['from']) ? $assoc_args['from'] : 'origin';
     $process = VPCommandUtils::exec("git config --get remote." . ProcessUtils::escapeshellarg($remote) . ".url");
     $remoteUrl = $process->getConsoleOutput();
     if (is_dir($remoteUrl)) {
         $this->runVPInternalCommand('commit-frequently-written-entities', [], $remoteUrl);
     } else {
         // We currently do not support commiting frequently written entities for repositories on a different server
     }
     $this->switchMaintenance('on');
     $branchToPullFrom = 'master';
     // hardcoded until we support custom branches
     $pullCommand = 'git pull ' . ProcessUtils::escapeshellarg($remote) . ' ' . $branchToPullFrom;
     $process = VPCommandUtils::exec($pullCommand);
     if ($process->isSuccessful()) {
         WP_CLI::success("Pulled changes from '{$remote}'");
     } else {
         if (stripos($process->getConsoleOutput(), 'automatic merge failed') !== false) {
             WP_CLI::warning("");
             WP_CLI::warning("CONFLICTS DETECTED. Your options:");
             WP_CLI::warning("");
             WP_CLI::warning(" 1) Keep the conflicts. You will be able to resolve them manually.");
             WP_CLI::warning(" 2) Abort the process. The site will look like you never ran the pull.");
             WP_CLI::warning("");
             fwrite(STDOUT, "Choose 1 or 2: ");
             $answer = trim(fgets(STDIN));
             if ($answer == "1") {
                 WP_CLI::success("You've chosen to keep the conflicts on the disk. MAINTENANCE MODE IS STILL ON.");
                 WP_CLI::success("");
                 WP_CLI::success("Do this now:");
                 WP_CLI::success("");
                 WP_CLI::success(" 1. Resolve the conflicts manually as in a standard Git workflow");
                 WP_CLI::success(" 2. Stage and `git commit` the changes");
                 WP_CLI::success(" 3. Return here and run `wp vp pull --continue`");
                 WP_CLI::success("");
                 WP_CLI::success("That last step will turn the maintenance mode off.");
                 WP_CLI::success("You can also abort the merge manually by running `git merge --abort`");
                 exit;
             } else {
                 $process = VPCommandUtils::exec('git merge --abort');
                 if ($process->isSuccessful()) {
                     $this->switchMaintenance('off');
                     WP_CLI::success("Pull aborted, your site is now clean and running");
                     exit;
                 } else {
                     WP_CLI::error("Aborting pull failed, do it manually by executing 'git merge --abort'", false);
                     WP_CLI::error("and also don't fortget to turn off the maintenance mode.");
                 }
             }
         } else {
             // not a merge conflict, some other error
             $this->switchMaintenance('off');
             WP_CLI::error("Changes from {$remote} couldn't be pulled. Details:\n\n" . $process->getConsoleOutput());
         }
     }
     $this->finishPull();
 }
 public static function tearDownAfterClass()
 {
     $process = new Process("git add -A && git commit -m " . ProcessUtils::escapeshellarg("Commited changes made by " . get_called_class()), self::$testConfig->testSite->path);
     $process->run();
 }
 /**
  * Rewrites WP-CLI command to use a well-known binary and to possibly rewrite it for remote
  * execution over SSH. If the command is not a WP-CLI command (doesn't start with "wp ..."),
  * no rewriting is done.
  *
  * @param string $command
  * @param $autoSshTunnelling
  * @return string
  */
 private function rewriteWpCliCommand($command)
 {
     if (!Strings::startsWith($command, "wp ")) {
         return $command;
     }
     $command = substr($command, 3);
     // strip "wp " prefix
     $command = "php " . ProcessUtils::escapeshellarg($this->getWpCli()) . " {$command}";
     return $command;
 }