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"); }
/** * 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; }
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; }
protected function extract($file, $path) { $processError = null; // Try to use unrar on *nix if (!Platform::isWindows()) { $command = 'unrar x ' . ProcessExecutor::escape($file) . ' ' . ProcessExecutor::escape($path) . ' >/dev/null && chmod -R u+w ' . ProcessExecutor::escape($path); if (0 === $this->process->execute($command, $ignoredOutput)) { return; } $processError = 'Failed to execute ' . $command . "\n\n" . $this->process->getErrorOutput(); } if (!class_exists('RarArchive')) { // php.ini path is added to the error message to help users find the correct file $iniMessage = IniHelper::getMessage(); $error = "Could not decompress the archive, enable the PHP rar extension or install unrar.\n" . $iniMessage . "\n" . $processError; if (!Platform::isWindows()) { $error = "Could not decompress the archive, enable the PHP rar extension.\n" . $iniMessage; } throw new \RuntimeException($error); } $rarArchive = RarArchive::open($file); if (false === $rarArchive) { throw new \UnexpectedValueException('Could not open RAR archive: ' . $file); } $entries = $rarArchive->getEntries(); if (false === $entries) { throw new \RuntimeException('Could not retrieve RAR archive entries'); } foreach ($entries as $entry) { if (false === $entry->extract($path)) { throw new \RuntimeException('Could not extract entry'); } } $rarArchive->close(); }
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); } } }
protected function execute(InputInterface $input, OutputInterface $output) { if ($input->getOption('list')) { return $this->listScripts(); } elseif (!$input->getArgument('script')) { throw new \RunTimeException('Missing required argument "script"'); } $script = $input->getArgument('script'); if (!in_array($script, $this->scriptEvents)) { if (defined('Composer\\Script\\ScriptEvents::' . str_replace('-', '_', strtoupper($script)))) { throw new \InvalidArgumentException(sprintf('Script "%s" cannot be run with this command', $script)); } } $composer = $this->getComposer(); $hasListeners = $composer->getEventDispatcher()->hasEventListeners(new CommandEvent($script, $composer, $this->getIO())); if (!$hasListeners) { throw new \InvalidArgumentException(sprintf('Script "%s" is not defined in this package', $script)); } $args = $input->getArgument('args'); if (!is_null($timeout = $input->getOption('timeout'))) { if (!ctype_digit($timeout)) { throw new \RuntimeException('Timeout value must be numeric and positive if defined, or 0 for forever'); } // Override global timeout set before in Composer by environment or config ProcessExecutor::setTimeout((int) $timeout); } return $composer->getEventDispatcher()->dispatchScript($script, $input->getOption('dev') || !$input->getOption('no-dev'), $args); }
protected function get($originUrl, $fileUrl, $options = [], $fileName = null, $progress = true) { if (strpos($fileUrl, 'ssh://') !== 0) { throw new \UnexpectedValueException("This does not appear to be a file that should be downloaded via ssh: {$fileUrl}"); } // strip off the pseudo protocol $fileUrl = substr($fileUrl, 6); if ($this->io->isVerbose()) { $this->io->write("Downloading {$fileUrl} via ssh."); } // filename means we want to save if ($fileName) { $cmd = 'scp ' . ProcessExecutor::escape($fileUrl) . ' ' . ProcessExecutor::escape($fileName); } else { // otherwise just return the file contents list($host, $path) = explode(':', $fileUrl); $cmd = 'ssh ' . ProcessExecutor::escape($host) . ' ' . ProcessExecutor::escape('cat ' . ProcessExecutor::escape($path)); } if ($progress) { $this->io->writeError(' Downloading: <comment>Connecting...</comment>', false); } // success? // @todo: do we need to catch any exceptions here? if ($this->process->execute($cmd, $output) === 0) { if ($progress) { $this->io->overwriteError(' Downloading: <comment>100%</comment>'); } return $output; } else { // some sort of error - boo! throw new \RuntimeException("Could not download {$fileUrl}. " . $process->getErrorOutput()); } }
/** * 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; }
/** * 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); } }
/** * 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)); } }
/** * {@inheritDoc} */ public function loadConfiguration(Config $config) { // reload oauth token from config if available if ($tokens = $config->get('github-oauth')) { foreach ($tokens as $domain => $token) { if (!preg_match('{^[a-z0-9]+$}', $token)) { throw new \UnexpectedValueException('Your github oauth token for ' . $domain . ' contains invalid characters: "' . $token . '"'); } $this->setAuthentication($domain, $token, 'x-oauth-basic'); } } if ($tokens = $config->get('gitlab-oauth')) { foreach ($tokens as $domain => $token) { $this->setAuthentication($domain, $token, 'oauth2'); } } // reload http basic credentials from config if available if ($creds = $config->get('http-basic')) { foreach ($creds as $domain => $cred) { $this->setAuthentication($domain, $cred['username'], $cred['password']); } } // setup process timeout ProcessExecutor::setTimeout((int) $config->get('process-timeout')); }
protected function extract($file, $path) { $processError = null; if (self::$hasSystemUnzip && !(class_exists('ZipArchive') && Platform::isWindows())) { $command = 'unzip ' . ProcessExecutor::escape($file) . ' -d ' . ProcessExecutor::escape($path); if (!Platform::isWindows()) { $command .= ' && chmod -R u+w ' . ProcessExecutor::escape($path); } try { if (0 === $this->process->execute($command, $ignoredOutput)) { return; } $processError = 'Failed to execute ' . $command . "\n\n" . $this->process->getErrorOutput(); } catch (\Exception $e) { $processError = 'Failed to execute ' . $command . "\n\n" . $e->getMessage(); } if (!class_exists('ZipArchive')) { throw new \RuntimeException($processError); } } $zipArchive = new ZipArchive(); if (true !== ($retval = $zipArchive->open($file))) { throw new \UnexpectedValueException(rtrim($this->getErrorMessage($retval, $file) . "\n" . $processError), $retval); } if (true !== $zipArchive->extractTo($path)) { throw new \RuntimeException(rtrim("There was an error extracting the ZIP file, it is either corrupted or using an invalid format.\n" . $processError)); } $zipArchive->close(); }
/** * 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); } } }
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; }
/** * Creates a Composer instance * * @param IOInterface $io IO instance * @param mixed $localConfig either a configuration array or a filename to read from, if null it will read from the default filename * @return Composer */ public function createComposer(IOInterface $io, $localConfig = null) { // load Composer configuration if (null === $localConfig) { $localConfig = getenv('COMPOSER') ?: 'composer.json'; } if (is_string($localConfig)) { $composerFile = $localConfig; $file = new JsonFile($localConfig, new RemoteFilesystem($io)); if (!$file->exists()) { if ($localConfig === 'composer.json') { $message = 'Composer could not find a composer.json file in ' . getcwd(); } else { $message = 'Composer could not find the config file: ' . $localConfig; } $instructions = 'To initialize a project, please create a composer.json file as described in the http://getcomposer.org/ "Getting Started" section'; throw new \InvalidArgumentException($message . PHP_EOL . $instructions); } $file->validateSchema(JsonFile::LAX_SCHEMA); $localConfig = $file->read(); } // Configuration defaults $config = $this->createConfig(); $config->merge($localConfig); $vendorDir = $config->get('vendor-dir'); $binDir = $config->get('bin-dir'); // setup process timeout ProcessExecutor::setTimeout((int) $config->get('process-timeout')); // initialize repository manager $rm = $this->createRepositoryManager($io, $config); // load default repository unless it's explicitly disabled $localConfig = $this->addPackagistRepository($localConfig); // load local repository $this->addLocalRepository($rm, $vendorDir); // load package $loader = new Package\Loader\RootPackageLoader($rm); $package = $loader->load($localConfig); // initialize download manager $dm = $this->createDownloadManager($io); // initialize installation manager $im = $this->createInstallationManager($rm, $dm, $vendorDir, $binDir, $io); // purge packages if they have been deleted on the filesystem $this->purgePackages($rm, $im); // initialize composer $composer = new Composer(); $composer->setConfig($config); $composer->setPackage($package); $composer->setRepositoryManager($rm); $composer->setDownloadManager($dm); $composer->setInstallationManager($im); // init locker if possible if (isset($composerFile)) { $lockFile = "json" === pathinfo($composerFile, PATHINFO_EXTENSION) ? substr($composerFile, 0, -4) . 'lock' : $composerFile . '.lock'; $locker = new Package\Locker(new JsonFile($lockFile, new RemoteFilesystem($io)), $rm, md5_file($composerFile)); $composer->setLocker($locker); } return $composer; }
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); }
protected function extract($file, $path) { $command = 'tar -xJf ' . ProcessExecutor::escape($file) . ' -C ' . ProcessExecutor::escape($path); if (0 === $this->process->execute($command, $ignoredOutput)) { return; } $processError = 'Failed to execute ' . $command . "\n\n" . $this->process->getErrorOutput(); throw new \RuntimeException($processError); }
protected function extract($file, $path) { // we must use cmdline tar, as PharData::extract() messes up symlinks $command = 'tar -xzf ' . ProcessExecutor::escape($file) . ' -C ' . ProcessExecutor::escape($path); if (0 === $this->process->execute($command, $ignoredOutput)) { return; } throw new \RuntimeException("Failed to execute '{$command}'\n\n" . $this->process->getErrorOutput()); }
public function testGetComposerInformationWithLabelWithStreamWithNoChange() { $this->setAssetPerforceToStream(); $expectedCommand = 'p4 -u user -p port files //depot/branch/ASSET.json@0.0.1'; $this->processExecutor->expects($this->at(0))->method('execute')->with($this->equalTo($expectedCommand))->will($this->returnCallback(function ($command, &$output) { $output = '//depot/ASSET.json#1 - branch 10001 (text)'; return $command ? true : true; })); $result = $this->perforce->getComposerInformation('//depot/branch@0.0.1'); $this->assertSame('', $result); }
/** * 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]; } }
public function testSplitLines() { $process = new ProcessExecutor(); $this->assertEquals(array(), $process->splitLines('')); $this->assertEquals(array(), $process->splitLines(null)); $this->assertEquals(array('foo'), $process->splitLines('foo')); $this->assertEquals(array('foo', 'bar'), $process->splitLines("foo\nbar")); $this->assertEquals(array('foo', 'bar'), $process->splitLines("foo\r\nbar")); $this->assertEquals(array('foo', 'bar'), $process->splitLines("foo\r\nbar\n")); }
public function doUpdate(PackageInterface $initial, PackageInterface $target, $path, $url) { $url = ProcessExecutor::escape($url); $ref = ProcessExecutor::escape($target->getSourceReference()); $this->io->writeError(" Updating to " . $target->getSourceReference()); if (!is_dir($path . '/.hg')) { throw new \RuntimeException('The .hg directory is missing from ' . $path . ', see http://getcomposer.org/commit-deps for more information'); } $command = sprintf('hg pull %s && hg up %s', $url, $ref); if (0 !== $this->process->execute($command, $ignoredOutput, realpath($path))) { throw new \RuntimeException('Failed to execute ' . $command . "\n\n" . $this->process->getErrorOutput()); } }
/** * 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 . ')'); }
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); } } }
/** * {@inheritDoc} */ public function doUpdate(PackageInterface $initial, PackageInterface $target, $path, $url) { // Ensure we are allowed to use this URL by config $this->config->prohibitUrlByConfig($url, $this->io); $url = ProcessExecutor::escape($url); $ref = ProcessExecutor::escape($target->getSourceReference()); $this->io->writeError(" Updating to " . $target->getSourceReference()); if (!$this->hasMetadataRepository($path)) { throw new \RuntimeException('The .hg directory is missing from ' . $path . ', see https://getcomposer.org/commit-deps for more information'); } $command = sprintf('hg pull %s && hg up %s', $url, $ref); if (0 !== $this->process->execute($command, $ignoredOutput, realpath($path))) { throw new \RuntimeException('Failed to execute ' . $command . "\n\n" . $this->process->getErrorOutput()); } }
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()); } }
/** * 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; }
protected function extract($file, $path) { $targetFilepath = $path . DIRECTORY_SEPARATOR . basename(substr($file, 0, -3)); if (!defined('PHP_WINDOWS_VERSION_BUILD')) { $command = 'gzip -cd ' . ProcessExecutor::escape($file) . ' > ' . ProcessExecutor::escape($targetFilepath); if (0 === $this->process->execute($command, $ignoredOutput)) { return; } $processError = 'Failed to execute ' . $command . "\n\n" . $this->process->getErrorOutput(); throw new \RuntimeException($processError); } $archiveFile = gzopen($file, 'rb'); $targetFile = fopen($targetFilepath, 'wb'); while ($string = gzread($archiveFile, 4096)) { fwrite($targetFile, $string, strlen($string)); } gzclose($archiveFile); fclose($targetFile); }
/** * 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); }