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; }
/** * 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; }