/** * Executes a shell command with escaping. * * @param string $cmd * @return bool */ protected function executeCommand($cmd) { // Shell-escape all arguments except the command. $args = func_get_args(); foreach ($args as $index => $arg) { if ($index !== 0) { $args[$index] = escapeshellarg($arg); } } // And replace the arguments. $command = call_user_func_array('sprintf', $args); $output = ''; if ($this->io->isVerbose()) { $this->io->write('<comment>' . $command . '</comment>'); $io = $this->io; $output = function ($type, $data) use($io) { if ($type == Process::ERR) { $io->write('<error>' . $data . '</error>'); } else { $io->write('<comment>' . $data . '</comment>'); } }; } return $this->executor->execute($command, $output) == 0; }
private function guessSvnVersion(array $packageConfig, $path) { SvnUtil::cleanEnv(); // try to fetch current version from svn if (0 === $this->process->execute('svn info --xml', $output, $path)) { $trunkPath = isset($packageConfig['trunk-path']) ? preg_quote($packageConfig['trunk-path'], '#') : 'trunk'; $branchesPath = isset($packageConfig['branches-path']) ? preg_quote($packageConfig['branches-path'], '#') : 'branches'; $tagsPath = isset($packageConfig['tags-path']) ? preg_quote($packageConfig['tags-path'], '#') : 'tags'; $urlPattern = '#<url>.*/(' . $trunkPath . '|(' . $branchesPath . '|' . $tagsPath . ')/(.*))</url>#'; if (preg_match($urlPattern, $output, $matches)) { if (isset($matches[2]) && ($branchesPath === $matches[2] || $tagsPath === $matches[2])) { // we are in a branches path $version = $this->versionParser->normalizeBranch($matches[3]); $prettyVersion = 'dev-' . $matches[3]; if ('9999999-dev' === $version) { $version = $prettyVersion; } return array('version' => $version, 'commit' => '', 'pretty_version' => $prettyVersion); } $prettyVersion = trim($matches[1]); $version = $this->versionParser->normalize($prettyVersion); return array('version' => $version, 'commit' => '', 'pretty_version' => $prettyVersion); } } }
/** * Initializes path repository. * * This method will basically read the folder and add the found package. */ protected function initialize() { parent::initialize(); foreach ($this->getUrlMatches() as $url) { $path = realpath($url) . DIRECTORY_SEPARATOR; $composerFilePath = $path . 'composer.json'; if (!file_exists($composerFilePath)) { continue; } $json = file_get_contents($composerFilePath); $package = JsonFile::parseJson($json, $composerFilePath); $package['dist'] = array('type' => 'path', 'url' => $url, 'reference' => ''); if (!isset($package['version'])) { $package['version'] = $this->versionGuesser->guessVersion($package, $path) ?: 'dev-master'; } $output = ''; if (is_dir($path . DIRECTORY_SEPARATOR . '.git') && 0 === $this->process->execute('git log -n1 --pretty=%H', $output, $path)) { $package['dist']['reference'] = trim($output); } else { $package['dist']['reference'] = Locker::getContentHash($json); } $package = $this->loader->load($package); $this->addPackage($package); } if (count($this->getPackages()) == 0) { throw new \RuntimeException(sprintf('No `composer.json` file found in any path repository in "%s"', $this->url)); } }
/** * Initializes path repository. * * This method will basically read the folder and add the found package. */ protected function initialize() { parent::initialize(); foreach ($this->getUrlMatches() as $url) { $path = realpath($url) . DIRECTORY_SEPARATOR; $composerFilePath = $path . 'composer.json'; if (!file_exists($composerFilePath)) { continue; } $json = file_get_contents($composerFilePath); $package = JsonFile::parseJson($json, $composerFilePath); $package['dist'] = array('type' => 'path', 'url' => $url, 'reference' => sha1($json)); $package['transport-options'] = $this->options; if (!isset($package['version'])) { $versionData = $this->versionGuesser->guessVersion($package, $path); $package['version'] = $versionData['version'] ?: 'dev-master'; } $output = ''; if (is_dir($path . DIRECTORY_SEPARATOR . '.git') && 0 === $this->process->execute('git log -n1 --pretty=%H', $output, $path)) { $package['dist']['reference'] = trim($output); } $package = $this->loader->load($package); $this->addPackage($package); } }
private static function installDependencies(IOInterface $io, $folder) { $io->write("[0;32mInstalling front end dependencies from package.json[0m"); $proc = new ProcessExecutor(); $proc->execute('cd ' . $folder . ' && npm install'); $io->write("[0;32mFront end dependencies installed[0m"); }
public function testExecuteOutputsIfNotCaptured() { $process = new ProcessExecutor(); ob_start(); $process->execute('echo foo'); $output = ob_get_clean(); $this->assertEquals("foo" . PHP_EOL, $output); }
private function checkGit() { $this->process->execute('git config color.ui', $output); if (strtolower(trim($output)) === 'always') { return '<comment>Your git color.ui setting is set to always, this is known to create issues. Use "git config --global color.ui true" to set it correctly.</comment>'; } return true; }
private function throwException($message, $url) { // git might delete a directory when it fails and php will not know clearstatcache(); if (0 !== $this->process->execute('git --version', $ignoredOutput)) { throw new \RuntimeException('Failed to clone ' . self::sanitizeUrl($url) . ', git was not found, check that it is installed and in your PATH env.' . "\n\n" . $this->process->getErrorOutput()); } throw new \RuntimeException($message); }
/** * Add time in composer. * * @param array $composer * @param ProcessExecutor $process * @param string $cmd * @param string $repoDir * @param string $datetimePrefix * * @return array The composer */ protected static function addComposerTime(array $composer, ProcessExecutor $process, $cmd, $repoDir, $datetimePrefix = '') { if (!isset($composer['time'])) { $process->execute($cmd, $output, $repoDir); $date = new \DateTime($datetimePrefix . trim($output), new \DateTimeZone('UTC')); $composer['time'] = $date->format('Y-m-d H:i:s'); } return $composer; }
/** * Retrieves the current git version. * * @return string|null The git version number. */ public function getVersion() { if (isset(self::$version)) { return self::$version; } if (0 !== $this->process->execute('git --version', $output)) { return; } if (preg_match('/^git version (\\d+(?:\\.\\d+)+)/m', $output, $matches)) { return self::$version = $matches[1]; } }
private function determineLocalPackageReference() { $basePath = realpath(getcwd()); if (is_dir($basePath . DIRECTORY_SEPARATOR . '.git')) { $process = new ProcessExecutor(); if ($process->execute('git rev-parse HEAD', $output, $basePath) === 0) { return trim($output); } } // TODO: add support for other VCS'es return null; }
public function applyPatches() { if (!is_dir($this->patchDir) && is_dir($this->patchDir)) { return; } $patches = glob($this->patchDir . '/*.patch'); if (count($patches)) { $io = $this->io; $handler = function ($type, $data) use($io) { if ($type == Process::ERR) { $io->write('<error>' . $data . '</error>'); } else { $io->write('<info>' . $data . '</info>'); } }; foreach ($patches as $patchFile) { $file = escapeshellarg($patchFile); $io->write("<comment>Applying patches from {$file}.</comment>"); $this->executor->execute("patch -r - -p0 --no-backup-if-mismatch -i " . $file, $handler); } } }
/** * Execute an SVN command and try to fix up the process with credentials * if necessary. * * @param string $command SVN command to run * @param string $url SVN url * @param string $cwd Working directory * @param string $path Target for a checkout * @param Boolean $verbose Output all output to the user * * @return string * * @throws \RuntimeException */ public function execute($command, $url, $cwd = null, $path = null, $verbose = false) { $svnCommand = $this->getCommand($command, $url, $path); $output = null; $io = $this->io; $handler = function ($type, $buffer) use(&$output, $io, $verbose) { if ($type !== 'out') { return; } $output .= $buffer; if ($verbose) { $io->write($buffer, false); } }; $status = $this->process->execute($svnCommand, $handler, $cwd); if (0 === $status) { return $output; } if (empty($output)) { $output = $this->process->getErrorOutput(); } // the error is not auth-related if (false === stripos($output, 'authorization failed:')) { throw new \RuntimeException($output); } // no auth supported for non interactive calls if (!$this->io->isInteractive()) { throw new \RuntimeException('can not ask for authentication in non interactive mode (' . $output . ')'); } // TODO keep a count of user auth attempts and ask 5 times before // failing hard (currently it fails hard directly if the URL has credentials) // try to authenticate if (!$this->hasAuth()) { $this->doAuthDance(); // restart the process return $this->execute($command, $url, $cwd, $path, $verbose); } throw new \RuntimeException('wrong credentials provided (' . $output . ')'); }
/** * Retrieve removed files list between two package versions * * @param PackageInterface $targert new package version to install * @param PackageInterface $initial previous package version * * @return array */ protected function getRemovedFiles(PackageInterface $target, PackageInterface $initial) { $output = false; $removedFiles = array(); if ($initial->getSourceReference() != $target->getSourceReference()) { if ($this->io->isVerbose()) { $this->io->write("Retrieving list of removed files from previous version installed."); } $command = sprintf('git diff --name-only --diff-filter=D %s %s', $initial->getSourceReference(), $target->getSourceReference()); if (0 !== $this->process->execute($command, $output, $this->rubedoRootDir)) { throw new \RuntimeException('Failed to execute ' . $command . "\n\n" . $this->process->getErrorOutput()); } $removedFiles = explode(PHP_EOL, ltrim(rtrim($output))); } return $removedFiles; }
/** * Executes a shell command * * @param string $command the command to execute * @param mixed $output the output will be written into this var if passed by ref * if a callable is passed it will be used as output handler * @param string $cwd the working directory * @return bool */ protected function executeCommand($command, &$output = null, $cwd = null) { $success = $this->executor->execute($command, $output, $cwd) == 0; if ($this->io->isVerbose()) { $this->io->write('<comment>' . $command . '</comment>'); $io = $this->io; $output = function ($type, $data) use($io) { if ($type == Process::ERR) { $io->write('<error>' . $data . '</error>'); } else { $io->write('<comment>' . $data . '</comment>'); } }; } return $success; }
/** * Moves front-end libraries to Lightning's installed directory. * * @param \Composer\Script\Event $event * The script event. */ public static function deployLibraries(Event $event) { $extra = $event->getComposer()->getPackage()->getExtra(); if (isset($extra['installer-paths'])) { foreach ($extra['installer-paths'] as $path => $criteria) { if (array_intersect(['drupal/lightning', 'type:drupal-profile'], $criteria)) { $lightning = $path; } } if (isset($lightning)) { $lightning = str_replace('{$name}', 'lightning', $lightning); $executor = new ProcessExecutor($event->getIO()); $output = NULL; $executor->execute('npm run install-libraries', $output, $lightning); } } }
/** * Initializes path repository. * * This method will basically read the folder and add the found package. */ protected function initialize() { parent::initialize(); $composerFilePath = $this->path . 'composer.json'; if (!file_exists($composerFilePath)) { throw new \RuntimeException(sprintf('No `composer.json` file found in path repository "%s"', $this->path)); } $json = file_get_contents($composerFilePath); $package = JsonFile::parseJson($json, $composerFilePath); $package['dist'] = array('type' => 'path', 'url' => $this->path, 'reference' => ''); if (!isset($package['version'])) { $package['version'] = $this->versionGuesser->guessVersion($package, $this->path) ?: 'dev-master'; } if (is_dir($this->path . '/.git') && 0 === $this->process->execute('git log -n1 --pretty=%H', $output, $this->path)) { $package['dist']['reference'] = trim($output); } $package = $this->loader->load($package); $this->addPackage($package); }
/** * Execute an SVN command and try to fix up the process with credentials * if necessary. * * @param string $command SVN command to run * @param string $url SVN url * @param string $cwd Working directory * @param string $path Target for a checkout * @param bool $verbose Output all output to the user * * @return string * * @throws \RuntimeException */ public function execute($command, $url, $cwd = null, $path = null, $verbose = false) { $svnCommand = $this->getCommand($command, $url, $path); $output = null; $io = $this->io; $handler = function ($type, $buffer) use(&$output, $io, $verbose) { if ($type !== 'out') { return; } if ('Redirecting to URL ' === substr($buffer, 0, 19)) { return; } $output .= $buffer; if ($verbose) { $io->write($buffer, false); } }; $status = $this->process->execute($svnCommand, $handler, $cwd); if (0 === $status) { return $output; } if (empty($output)) { $output = $this->process->getErrorOutput(); } // the error is not auth-related if (false === stripos($output, 'Could not authenticate to server:') && false === stripos($output, 'authorization failed') && false === stripos($output, 'svn: E170001:') && false === stripos($output, 'svn: E215004:')) { throw new \RuntimeException($output); } // no auth supported for non interactive calls if (!$this->io->isInteractive()) { throw new \RuntimeException('can not ask for authentication in non interactive mode (' . $output . ')'); } // try to authenticate if maximum quantity of tries not reached if ($this->qtyAuthTries++ < self::MAX_QTY_AUTH_TRIES || !$this->hasAuth()) { $this->doAuthDance(); // restart the process return $this->execute($command, $url, $cwd, $path, $verbose); } throw new \RuntimeException('wrong credentials provided (' . $output . ')'); }
/** * Execute an SVN command and try to fix up the process with credentials * if necessary. * * @param string $command SVN command to run * @param string $url SVN url * @param string $cwd Working directory * @param string $path Target for a checkout * @param bool $verbose Output all output to the user * * @throws \RuntimeException * @return string */ public function execute($command, $url, $cwd = null, $path = null, $verbose = false) { if (preg_match('{^(http|svn):}i', $url) && $this->config->get('secure-http')) { throw new TransportException("Your configuration does not allow connection to {$url}. See https://getcomposer.org/doc/06-config.md#secure-http for details."); } $svnCommand = $this->getCommand($command, $url, $path); $output = null; $io = $this->io; $handler = function ($type, $buffer) use(&$output, $io, $verbose) { if ($type !== 'out') { return; } if ('Redirecting to URL ' === substr($buffer, 0, 19)) { return; } $output .= $buffer; if ($verbose) { $io->writeError($buffer, false); } }; $status = $this->process->execute($svnCommand, $handler, $cwd); if (0 === $status) { return $output; } $errorOutput = $this->process->getErrorOutput(); $fullOutput = implode("\n", array($output, $errorOutput)); // the error is not auth-related if (false === stripos($fullOutput, 'Could not authenticate to server:') && false === stripos($fullOutput, 'authorization failed') && false === stripos($fullOutput, 'svn: E170001:') && false === stripos($fullOutput, 'svn: E215004:')) { throw new \RuntimeException($fullOutput); } if (!$this->hasAuth()) { $this->doAuthDance(); } // try to authenticate if maximum quantity of tries not reached if ($this->qtyAuthTries++ < self::MAX_QTY_AUTH_TRIES) { // restart the process return $this->execute($command, $url, $cwd, $path, $verbose); } throw new \RuntimeException('wrong credentials provided (' . $fullOutput . ')'); }
/** * Generates fake autoload file pointing to real autoload.php * * @param BasePackage packager * @param string target file path */ protected function generateFakeAutoloadFile(BasePackage $package, $targetPath) { // Prepare paths $packageInstallPath = $this->getInstallPath($package); $targetDir = $this->fs->normalizePath($packageInstallPath . '/' . dirname($targetPath)); $targetBasename = basename($targetPath); // Create short path to fake autoload file (for display purposes) $displayFilePath = $this->fs->findShortestPath($this->getBasePath(), "{$targetDir}/{$targetBasename}"); // If target directory does not exist, skip if (!is_dir($targetDir)) { $this->io->write('Skiping generation of fake autoload file: ' . $displayFilePath); return; } // Find relative path from directory of fake autoload file to real autoload.php $relativePath = $this->fs->findShortestPath($targetDir, $this->getVendorDirPath(), TRUE); $autoloadPath = var_export('/' . rtrim($relativePath, '/') . '/autoload.php', TRUE); // Create fake autoload content $content = <<<BOOTSTRAP_END <?php /** * @warning This file is automatically generated by Composer. * @see https://github.com/vbuilder/composer-plugin */ return include __DIR__ . {$autoloadPath}; BOOTSTRAP_END; // Write $this->io->write('Generating fake autoload in: ' . $displayFilePath); file_put_contents($targetDir . '/' . $targetBasename, $content); // Mark file as unchanged for Git if (is_dir("{$packageInstallPath}/.git")) { $exitCode = $this->process->execute('git --git-dir ' . escapeshellarg("{$packageInstallPath}/.git") . ' --work-tree ' . escapeshellarg($packageInstallPath) . ' update-index --assume-unchanged ' . escapeshellarg($targetPath)); if ($exitCode) { throw new \RuntimeException('Failed to mark ' . $displayFilePath . ' as unchanged'); } } }
/** * Execute an SVN command and try to fix up the process with credentials * if necessary. * * @param string $command SVN command to run * @param string $url SVN url * @param string $cwd Working directory * @param string $path Target for a checkout * @param bool $verbose Output all output to the user * * @throws \RuntimeException * @return string */ public function execute($command, $url, $cwd = null, $path = null, $verbose = false) { // Ensure we are allowed to use this URL by config $this->config->prohibitUrlByConfig($url, $this->io); $svnCommand = $this->getCommand($command, $url, $path); $output = null; $io = $this->io; $handler = function ($type, $buffer) use(&$output, $io, $verbose) { if ($type !== 'out') { return; } if ('Redirecting to URL ' === substr($buffer, 0, 19)) { return; } $output .= $buffer; if ($verbose) { $io->writeError($buffer, false); } }; $status = $this->process->execute($svnCommand, $handler, $cwd); if (0 === $status) { return $output; } $errorOutput = $this->process->getErrorOutput(); $fullOutput = implode("\n", array($output, $errorOutput)); // the error is not auth-related if (false === stripos($fullOutput, 'Could not authenticate to server:') && false === stripos($fullOutput, 'authorization failed') && false === stripos($fullOutput, 'svn: E170001:') && false === stripos($fullOutput, 'svn: E215004:')) { throw new \RuntimeException($fullOutput); } if (!$this->hasAuth()) { $this->doAuthDance(); } // try to authenticate if maximum quantity of tries not reached if ($this->qtyAuthTries++ < self::MAX_QTY_AUTH_TRIES) { // restart the process return $this->execute($command, $url, $cwd, $path, $verbose); } throw new \RuntimeException('wrong credentials provided (' . $fullOutput . ')'); }
protected function execute($command, array $args, IOInterface $io = null, $workingDirectory = null) { $processExecutor = new ProcessExecutor($io); array_unshift($args, $this->findBinary($command)); $args = array_map([$processExecutor, 'escape'], $args); $outputHandler = function ($type, $data) use($io) { if (!$io->isVerbose()) { return; } switch ($type) { case Process::ERR: $io->writeError($data); break; case Process::OUT: default: $io->write($data); break; } }; $fullCommand = implode(' ', $args); if ($processExecutor->execute($fullCommand, $outputHandler, $workingDirectory) > 0) { throw ProcessFailedException::create($command, $fullCommand, $processExecutor->getErrorOutput()); } }
/** * {@inheritDoc} */ public static function supports(IOInterface $io, Config $config, $url, $deep = false) { if (preg_match('#(^(?:https?|ssh)://(?:[^@]@)?bitbucket.org|https://(?:.*?)\\.kilnhg.com)#i', $url)) { return true; } // local filesystem if (Filesystem::isLocalPath($url)) { $url = Filesystem::getPlatformPath($url); if (!is_dir($url)) { throw new \RuntimeException('Directory does not exist: ' . $url); } $process = new ProcessExecutor(); // check whether there is a hg repo in that path if ($process->execute('hg summary', $output, $url) === 0) { return true; } } if (!$deep) { return false; } $processExecutor = new ProcessExecutor(); $exit = $processExecutor->execute(sprintf('hg identify %s', ProcessExecutor::escape($url)), $ignored); return $exit === 0; }
/** * {@inheritDoc} */ public static function supports(IOInterface $io, $url, $deep = false) { if (preg_match('#(^git://|\\.git$|git(?:olite)?@|//git\\.|//github.com/)#i', $url)) { return true; } // local filesystem if (static::isLocalUrl($url)) { if (!is_dir($url)) { throw new \RuntimeException('Directory does not exist: ' . $url); } $process = new ProcessExecutor(); $url = str_replace('file://', '', $url); // check whether there is a git repo in that path if ($process->execute('git tag', $output, $url) === 0) { return true; } } if (!$deep) { return false; } // TODO try to connect to the server return false; }
/** * {@inheritDoc} */ public static function supports(IOInterface $io, Config $config, $url, $deep = false) { if (preg_match('#(^git://|\\.git/?$|git(?:olite)?@|//git\\.|//github.com/)#i', $url)) { return true; } // local filesystem if (Filesystem::isLocalPath($url)) { $url = Filesystem::getPlatformPath($url); if (!is_dir($url)) { return false; } $process = new ProcessExecutor($io); // check whether there is a git repo in that path if ($process->execute('git tag', $output, $url) === 0) { return true; } } if (!$deep) { return false; } $process = new ProcessExecutor($io); if ($process->execute('git ls-remote --heads ' . ProcessExecutor::escape($url), $output) === 0) { return true; } return false; }
private function lockPackages(array $packages) { $locked = array(); foreach ($packages as $package) { if ($package instanceof AliasPackage) { continue; } $name = $package->getPrettyName(); $version = $package->getPrettyVersion(); if (!$name || !$version) { throw new \LogicException(sprintf('Package "%s" has no version or name and can not be locked', $package)); } $spec = $this->dumper->dump($package); unset($spec['version_normalized']); if ($package->isDev()) { if (function_exists('proc_open') && 'git' === $package->getSourceType() && ($path = $this->installationManager->getInstallPath($package))) { $sourceRef = $package->getSourceReference() ?: $package->getDistReference(); $process = new ProcessExecutor(); if (0 === $process->execute('git log -n1 --pretty=%ct ' . escapeshellarg($sourceRef), $output, $path)) { $datetime = new \DateTime('@' . $output, new \DateTimeZone('UTC')); $spec['time'] = $datetime->format('Y-m-d H:i:s'); } } } $locked[] = $spec; } usort($locked, function ($a, $b) { $comparison = strcmp($a['name'], $b['name']); if (0 !== $comparison) { return $comparison; } // If it is the same package, compare the versions to make the order deterministic return strcmp($a['version'], $b['version']); }); return $locked; }
/** * {@inheritDoc} */ public static function supports(IOInterface $io, $url, $deep = false) { $url = self::normalizeUrl($url); if (preg_match('#(^svn://|^svn\\+ssh://|svn\\.)#i', $url)) { return true; } // proceed with deep check for local urls since they are fast to process if (!$deep && !static::isLocalUrl($url)) { return false; } $processExecutor = new ProcessExecutor(); $exit = $processExecutor->execute("svn info --non-interactive {$url}", $ignoredOutput); if ($exit === 0) { // This is definitely a Subversion repository. return true; } if (false !== stripos($processExecutor->getErrorOutput(), 'authorization failed:')) { // This is likely a remote Subversion repository that requires // authentication. We will handle actual authentication later. return true; } return false; }
/** * {@inheritDoc} */ public static function supports(IOInterface $io, $url, $deep = false) { if (preg_match('#(^git://|\\.git$|git@|//git\\.|//github.com/)#i', $url)) { return true; } // local filesystem if (static::isLocalUrl($url)) { $process = new ProcessExecutor(); // check whether there is a git repo in that path if ($process->execute('git tag', $output, $url) === 0) { return true; } } if (!$deep) { return false; } // TODO try to connect to the server return false; }
/** * @param ProcessExecutor $processExecutor * @param $property * * @return string */ private static function getCompassProperty(ProcessExecutor $processExecutor, $property) { $output = ''; $processExecutor->execute("compass config -p {$property}", $output); return trim($output); }
/** * {@inheritDoc} */ public static function supports($url, $deep = false) { if (preg_match('#(^git://|\\.git$|git@|//git\\.)#i', $url)) { return true; } // local filesystem if (static::isLocalUrl($url)) { $process = new ProcessExecutor(); // check whether there is a git repo in that path if ($process->execute(sprintf('cd %s && git show', escapeshellarg($url)), $output) === 0) { return true; } } if (!$deep) { return false; } // TODO try to connect to the server return false; }