public function promptAuth(HttpGetResponse $res, CConfig $config, IO\IOInterface $io) { $httpCode = $res->info['http_code']; $message = "\nCould not fetch {$this->getURL()}, please create a GitHub OAuth token "; if (404 === $httpCode) { $message .= 'to access private repos'; } else { $message .= 'to go over the API rate limit'; } $github = new Util\GitHub($io, $config, null); if ($github->authorizeOAuth($this->origin)) { return true; } if ($io->isInteractive() && $github->authorizeOAuthInteractively($this->origin, $message)) { return true; } throw new Downloader\TransportException("Could not authenticate against {$this->origin}", 401); }
/** * {@inheritDoc} */ protected function getContents($url, $fetchingRepoData = false) { try { return parent::getContents($url); } catch (TransportException $e) { $gitHubUtil = new GitHub($this->io, $this->config, $this->process, $this->remoteFilesystem); switch ($e->getCode()) { case 401: case 404: // try to authorize only if we are fetching the main /repos/foo/bar data, otherwise it must be a real 404 if (!$fetchingRepoData) { throw $e; } if ($gitHubUtil->authorizeOAuth($this->originUrl)) { return parent::getContents($url); } if (!$this->io->isInteractive()) { return $this->attemptCloneFallback(); } $gitHubUtil->authorizeOAuthInteractively($this->originUrl, 'Your GitHub credentials are required to fetch private repository metadata (<info>' . $this->url . '</info>)'); return parent::getContents($url); case 403: if (!$this->io->hasAuthentication($this->originUrl) && $gitHubUtil->authorizeOAuth($this->originUrl)) { return parent::getContents($url); } if (!$this->io->isInteractive() && $fetchingRepoData) { return $this->attemptCloneFallback(); } $rateLimited = false; foreach ($e->getHeaders() as $header) { if (preg_match('{^X-RateLimit-Remaining: *0$}i', trim($header))) { $rateLimited = true; } } if (!$this->io->hasAuthentication($this->originUrl)) { if (!$this->io->isInteractive()) { $this->io->writeError('<error>GitHub API limit exhausted. Failed to get metadata for the ' . $this->url . ' repository, try running in interactive mode so that you can enter your GitHub credentials to increase the API limit</error>'); throw $e; } $gitHubUtil->authorizeOAuthInteractively($this->originUrl, 'API limit exhausted. Enter your GitHub credentials to get a larger API limit (<info>' . $this->url . '</info>)'); return parent::getContents($url); } if ($rateLimited) { $rateLimit = $this->getRateLimit($e->getHeaders()); $this->io->writeError(sprintf('<error>GitHub API limit (%d calls/hr) is exhausted. You are already authorized so you have to wait until %s before doing more requests</error>', $rateLimit['limit'], $rateLimit['reset'])); } throw $e; default: throw $e; } } }
protected function promptAuthAndRetry($httpStatus, $reason = null) { if ($this->config && in_array($this->originUrl, $this->config->get('github-domains'), true)) { $message = "\n" . 'Could not fetch ' . $this->fileUrl . ', please create a GitHub OAuth token ' . ($httpStatus === 404 ? 'to access private repos' : 'to go over the API rate limit'); $gitHubUtil = new GitHub($this->io, $this->config, null); if (!$gitHubUtil->authorizeOAuth($this->originUrl) && (!$this->io->isInteractive() || !$gitHubUtil->authorizeOAuthInteractively($this->originUrl, $message))) { throw new TransportException('Could not authenticate against ' . $this->originUrl, 401); } } elseif ($this->config && in_array($this->originUrl, $this->config->get('gitlab-domains'), true)) { $message = "\n" . 'Could not fetch ' . $this->fileUrl . ', enter your ' . $this->originUrl . ' credentials ' . ($httpStatus === 401 ? 'to access private repos' : 'to go over the API rate limit'); $gitLabUtil = new GitLab($this->io, $this->config, null); if (!$gitLabUtil->authorizeOAuth($this->originUrl) && (!$this->io->isInteractive() || !$gitLabUtil->authorizeOAuthInteractively($this->scheme, $this->originUrl, $message))) { throw new TransportException('Could not authenticate against ' . $this->originUrl, 401); } } else { // 404s are only handled for github if ($httpStatus === 404) { return; } // fail if the console is not interactive if (!$this->io->isInteractive()) { if ($httpStatus === 401) { $message = "The '" . $this->fileUrl . "' URL required authentication.\nYou must be using the interactive console to authenticate"; } if ($httpStatus === 403) { $message = "The '" . $this->fileUrl . "' URL could not be accessed: " . $reason; } throw new TransportException($message, $httpStatus); } // fail if we already have auth if ($this->io->hasAuthentication($this->originUrl)) { throw new TransportException("Invalid credentials for '" . $this->fileUrl . "', aborting.", $httpStatus); } $this->io->overwriteError(' Authentication required (<info>' . parse_url($this->fileUrl, PHP_URL_HOST) . '</info>):'); $username = $this->io->ask(' Username: '******' Password: '******'store-auths'); } $this->retry = true; throw new TransportException('RETRY'); }
public function runCommand($commandCallable, $url, $cwd, $initialClone = false) { if (preg_match('{^(http|git):}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."); } if ($initialClone) { $origCwd = $cwd; $cwd = null; } if (preg_match('{^ssh://[^@]+@[^:]+:[^0-9]+}', $url)) { throw new \InvalidArgumentException('The source URL ' . $url . ' is invalid, ssh URLs should have a port number after ":".' . "\n" . 'Use ssh://git@example.com:22/path or just git@example.com:path if you do not want to provide a password or custom port.'); } if (!$initialClone) { // capture username/password from URL if there is one $this->process->execute('git remote -v', $output, $cwd); if (preg_match('{^(?:composer|origin)\\s+https?://(.+):(.+)@([^/]+)}im', $output, $match)) { $this->io->setAuthentication($match[3], urldecode($match[1]), urldecode($match[2])); } } $protocols = $this->config->get('github-protocols'); if (!is_array($protocols)) { throw new \RuntimeException('Config value "github-protocols" must be an array, got ' . gettype($protocols)); } // public github, autoswitch protocols if (preg_match('{^(?:https?|git)://' . self::getGitHubDomainsRegex($this->config) . '/(.*)}', $url, $match)) { $messages = array(); foreach ($protocols as $protocol) { if ('ssh' === $protocol) { $protoUrl = "git@" . $match[1] . ":" . $match[2]; } else { $protoUrl = $protocol . "://" . $match[1] . "/" . $match[2]; } if (0 === $this->process->execute(call_user_func($commandCallable, $protoUrl), $ignoredOutput, $cwd)) { return; } $messages[] = '- ' . $protoUrl . "\n" . preg_replace('#^#m', ' ', $this->process->getErrorOutput()); if ($initialClone) { $this->filesystem->removeDirectory($origCwd); } } // failed to checkout, first check git accessibility $this->throwException('Failed to clone ' . self::sanitizeUrl($url) . ' via ' . implode(', ', $protocols) . ' protocols, aborting.' . "\n\n" . implode("\n", $messages), $url); } // if we have a private github url and the ssh protocol is disabled then we skip it and directly fallback to https $bypassSshForGitHub = preg_match('{^git@' . self::getGitHubDomainsRegex($this->config) . ':(.+?)\\.git$}i', $url) && !in_array('ssh', $protocols, true); $command = call_user_func($commandCallable, $url); $auth = null; if ($bypassSshForGitHub || 0 !== $this->process->execute($command, $ignoredOutput, $cwd)) { // private github repository without git access, try https with auth if (preg_match('{^git@' . self::getGitHubDomainsRegex($this->config) . ':(.+?)\\.git$}i', $url, $match)) { if (!$this->io->hasAuthentication($match[1])) { $gitHubUtil = new GitHub($this->io, $this->config, $this->process); $message = 'Cloning failed using an ssh key for authentication, enter your GitHub credentials to access private repos'; if (!$gitHubUtil->authorizeOAuth($match[1]) && $this->io->isInteractive()) { $gitHubUtil->authorizeOAuthInteractively($match[1], $message); } } if ($this->io->hasAuthentication($match[1])) { $auth = $this->io->getAuthentication($match[1]); $authUrl = 'https://' . rawurlencode($auth['username']) . ':' . rawurlencode($auth['password']) . '@' . $match[1] . '/' . $match[2] . '.git'; $command = call_user_func($commandCallable, $authUrl); if (0 === $this->process->execute($command, $ignoredOutput, $cwd)) { return; } } } elseif ($this->isAuthenticationFailure($url, $match)) { // private non-github repo that failed to authenticate if (strpos($match[2], '@')) { list($authParts, $match[2]) = explode('@', $match[2], 2); } $storeAuth = false; if ($this->io->hasAuthentication($match[2])) { $auth = $this->io->getAuthentication($match[2]); } elseif ($this->io->isInteractive()) { $defaultUsername = null; if (isset($authParts) && $authParts) { if (false !== strpos($authParts, ':')) { list($defaultUsername, ) = explode(':', $authParts, 2); } else { $defaultUsername = $authParts; } } $this->io->writeError(' Authentication required (<info>' . parse_url($url, PHP_URL_HOST) . '</info>):'); $auth = array('username' => $this->io->ask(' Username: '******'password' => $this->io->askAndHideAnswer(' Password: '******'store-auths'); } if ($auth) { $authUrl = $match[1] . rawurlencode($auth['username']) . ':' . rawurlencode($auth['password']) . '@' . $match[2] . $match[3]; $command = call_user_func($commandCallable, $authUrl); if (0 === $this->process->execute($command, $ignoredOutput, $cwd)) { $this->io->setAuthentication($match[2], $auth['username'], $auth['password']); $authHelper = new AuthHelper($this->io, $this->config); $authHelper->storeAuth($match[2], $storeAuth); return; } } } if ($initialClone) { $this->filesystem->removeDirectory($origCwd); } $this->throwException('Failed to execute ' . self::sanitizeUrl($command) . "\n\n" . $this->process->getErrorOutput(), $url); } }
public function download(PackageInterface $package, $path) { $url = $package->getDistUrl(); if (!$url) { throw new \InvalidArgumentException('The given package is missing url information'); } $this->filesystem->removeDirectory($path); $this->filesystem->ensureDirectoryExists($path); $fileName = $this->getFileName($package, $path); $this->io->write(" - Installing <info>" . $package->getName() . "</info> (<comment>" . VersionParser::formatVersion($package) . "</comment>)"); $processedUrl = $this->processUrl($package, $url); $hostname = parse_url($processedUrl, PHP_URL_HOST); $preFileDownloadEvent = new PreFileDownloadEvent(PluginEvents::PRE_FILE_DOWNLOAD, $this->rfs, $processedUrl); if ($this->eventDispatcher) { $this->eventDispatcher->dispatch($preFileDownloadEvent->getName(), $preFileDownloadEvent); } $rfs = $preFileDownloadEvent->getRemoteFilesystem(); if (strpos($hostname, '.github.com') === strlen($hostname) - 11) { $hostname = 'github.com'; } try { $checksum = $package->getDistSha1Checksum(); $cacheKey = $this->getCacheKey($package); try { if (!$this->cache || $checksum && $checksum !== $this->cache->sha1($cacheKey) || !$this->cache->copyTo($cacheKey, $fileName)) { if (!$this->outputProgress) { $this->io->write(' Downloading'); } $retries = 3; while ($retries--) { try { $rfs->copy($hostname, $processedUrl, $fileName, $this->outputProgress); break; } catch (TransportException $e) { if (0 !== $e->getCode() && !in_array($e->getCode(), array(500, 502, 503, 504)) || !$retries) { throw $e; } if ($this->io->isVerbose()) { $this->io->write(' Download failed, retrying...'); } usleep(500000); } } if ($this->cache) { $this->cache->copyFrom($cacheKey, $fileName); } } else { $this->io->write(' Loading from cache'); } } catch (TransportException $e) { if (!in_array($e->getCode(), array(404, 403, 412))) { throw $e; } if ('github.com' === $hostname && !$this->io->hasAuthentication($hostname)) { $message = "\n" . 'Could not fetch ' . $processedUrl . ', enter your GitHub credentials ' . ($e->getCode() === 404 ? 'to access private repos' : 'to go over the API rate limit'); $gitHubUtil = new GitHub($this->io, $this->config, null, $rfs); if (!$gitHubUtil->authorizeOAuth($hostname) && (!$this->io->isInteractive() || !$gitHubUtil->authorizeOAuthInteractively($hostname, $message))) { throw $e; } $rfs->copy($hostname, $processedUrl, $fileName, $this->outputProgress); } else { throw $e; } } if (!file_exists($fileName)) { throw new \UnexpectedValueException($url . ' could not be saved to ' . $fileName . ', make sure the' . ' directory is writable and you have internet connectivity'); } if ($checksum && hash_file('sha1', $fileName) !== $checksum) { throw new \UnexpectedValueException('The checksum verification of the file failed (downloaded from ' . $url . ')'); } } catch (\Exception $e) { $this->filesystem->removeDirectory($path); $this->clearCache($package, $path); throw $e; } return $fileName; }
protected function promptAuth(Aspects\HttpGetRequest $req, Aspects\HttpGetResponse $res) { $io = $this->io; $httpCode = $res->info['http_code']; if ('github' === $req->special) { $message = "\nCould not fetch {$req->getURL()}, please create a GitHub OAuth token "; if (404 === $httpCode) { $message .= 'to access private repos'; } else { $message .= 'to go over the API rate limit'; } $github = new Util\GitHub($io, $this->config, null); if ($github->authorizeOAuth($req->origin)) { $this->retry = true; return; } if ($io->isInteractive() && $github->authorizeOAuthInteractively($req->origin, $message)) { $this->retry = true; return; } throw new Downloader\TransportException("Could not authenticate against {$req->origin}", 401); } if ('gitlab' === $req->special) { $message = "\nCould not fetch {$req->getURL()}, enter your {$req->origin} credentials "; if (401 === $httpCode) { $message .= 'to access private repos'; } else { $message .= 'to go over the API rate limit'; } $gitlab = new Util\GitLab($io, $this->config, null); if ($gitlab->authorizeOAuth($req->origin)) { $this->retry = true; return; } if ($io->isInteractive() && $gitlab->authorizeOAuthInteractively($req->origin, $message)) { $this->retry = true; return; } throw new Downloader\TransportException("Could not authenticate against {$req->origin}", 401); } // 404s are only handled for github if (404 === $httpCode) { return; } // fail if the console is not interactive if (!$io->isInteractive()) { switch ($httpCode) { case 401: $message = "The '{$req->getURL()}' URL required authentication.\nYou must be using the interactive console to authenticate"; break; case 403: $message = "The '{$req->getURL()}' URL could not be accessed."; break; } throw new Downloader\TransportException($message, $httpCode); } // fail if we already have auth if ($io->hasAuthentication($req->origin)) { throw new Downloader\TransportException("Invalid credentials for '{$req->getURL()}', aborting.", $res->info['http_code']); } $io->overwrite(" Authentication required (<info>{$req->host}</info>):"); $username = $io->ask(' Username: '******' Password: '); $io->setAuthentication($req->origin, $username, $password); $this->retry = true; }
protected function runCommand($commandCallable, $url, $cwd, $initialClone = false) { if ($initialClone) { $origCwd = $cwd; $cwd = null; } if (preg_match('{^ssh://[^@]+@[^:]+:[^0-9]+}', $url)) { throw new \InvalidArgumentException('The source URL ' . $url . ' is invalid, ssh URLs should have a port number after ":".' . "\n" . 'Use ssh://git@example.com:22/path or just git@example.com:path if you do not want to provide a password or custom port.'); } if (preg_match('{^(?:https?|git)(://' . $this->getGitHubDomainsRegex() . '/.*)}', $url, $match)) { $protocols = $this->config->get('github-protocols'); if (!is_array($protocols)) { throw new \RuntimeException('Config value "github-protocols" must be an array, got ' . gettype($protocols)); } $messages = array(); foreach ($protocols as $protocol) { $url = $protocol . $match[1]; if (0 === $this->process->execute(call_user_func($commandCallable, $url), $ignoredOutput, $cwd)) { return; } $messages[] = '- ' . $url . "\n" . preg_replace('#^#m', ' ', $this->process->getErrorOutput()); if ($initialClone) { $this->filesystem->removeDirectory($origCwd); } } $this->throwException('Failed to clone ' . $this->sanitizeUrl($url) . ' via ' . implode(', ', $protocols) . ' protocols, aborting.' . "\n\n" . implode("\n", $messages), $url); } $command = call_user_func($commandCallable, $url); if (0 !== $this->process->execute($command, $ignoredOutput, $cwd)) { if (preg_match('{^git@' . $this->getGitHubDomainsRegex() . ':(.+?)\\.git$}i', $url, $match)) { if (!$this->io->hasAuthentication($match[1])) { $gitHubUtil = new GitHub($this->io, $this->config, $this->process); $message = 'Cloning failed using an ssh key for authentication, enter your GitHub credentials to access private repos'; if (!$gitHubUtil->authorizeOAuth($match[1]) && $this->io->isInteractive()) { $gitHubUtil->authorizeOAuthInteractively($match[1], $message); } } if ($this->io->hasAuthentication($match[1])) { $auth = $this->io->getAuthentication($match[1]); $url = 'https://' . urlencode($auth['username']) . ':' . urlencode($auth['password']) . '@' . $match[1] . '/' . $match[2] . '.git'; $command = call_user_func($commandCallable, $url); if (0 === $this->process->execute($command, $ignoredOutput, $cwd)) { return; } } } elseif ($this->io->isInteractive() && preg_match('{(https?://)([^/]+)(.*)$}i', $url, $match) && strpos($this->process->getErrorOutput(), 'fatal: Authentication failed') !== false) { if ($this->io->hasAuthentication($match[2])) { $auth = $this->io->getAuthentication($match[2]); } else { $this->io->write($url . ' requires Authentication'); $auth = array('username' => $this->io->ask('Username: '******'password' => $this->io->askAndHideAnswer('Password: '******'username']) . ':' . urlencode($auth['password']) . '@' . $match[2] . $match[3]; $command = call_user_func($commandCallable, $url); if (0 === $this->process->execute($command, $ignoredOutput, $cwd)) { $this->io->setAuthentication($match[2], $auth['username'], $auth['password']); return; } } if ($initialClone) { $this->filesystem->removeDirectory($origCwd); } $this->throwException('Failed to execute ' . $this->sanitizeUrl($command) . "\n\n" . $this->process->getErrorOutput(), $url); } }
/** * {@inheritDoc} */ public function download(PackageInterface $package, $path) { $url = $package->getDistUrl(); if (!$url) { throw new \InvalidArgumentException('The given package is missing url information'); } $this->filesystem->ensureDirectoryExists($path); $fileName = $this->getFileName($package, $path); $this->io->write(" - Installing <info>" . $package->getName() . "</info> (<comment>" . VersionParser::formatVersion($package) . "</comment>)"); $processUrl = $this->processUrl($package, $url); try { try { if (!$this->cache || !$this->cache->copyTo($this->getCacheKey($package), $fileName)) { $this->rfs->copy(parse_url($processUrl, PHP_URL_HOST), $processUrl, $fileName); if ($this->cache) { $this->cache->copyFrom($this->getCacheKey($package), $fileName); } } } catch (TransportException $e) { if (404 === $e->getCode() && 'github.com' === parse_url($processUrl, PHP_URL_HOST)) { $message = "\n" . 'Could not fetch ' . $processUrl . ', enter your GitHub credentials to access private repos'; $gitHubUtil = new GitHub($this->io, $this->config, null, $this->rfs); if (!$gitHubUtil->authorizeOAuth('github.com') && (!$this->io->isInteractive() || !$gitHubUtil->authorizeOAuthInteractively('github.com', $message))) { throw $e; } $this->rfs->copy(parse_url($processUrl, PHP_URL_HOST), $processUrl, $fileName); } else { throw $e; } } if (!file_exists($fileName)) { throw new \UnexpectedValueException($url . ' could not be saved to ' . $fileName . ', make sure the' . ' directory is writable and you have internet connectivity'); } $checksum = $package->getDistSha1Checksum(); if ($checksum && hash_file('sha1', $fileName) !== $checksum) { throw new \UnexpectedValueException('The checksum verification of the file failed (downloaded from ' . $url . ')'); } } catch (\Exception $e) { // clean up $this->filesystem->removeDirectory($path); throw $e; } }
/** * {@inheritDoc} */ public function download(PackageInterface $package, $path) { $url = $package->getDistUrl(); if (!$url) { throw new \InvalidArgumentException('The given package is missing url information'); } $this->filesystem->ensureDirectoryExists($path); $fileName = $this->getFileName($package, $path); $this->io->write(" - Installing <info>" . $package->getName() . "</info> (<comment>" . VersionParser::formatVersion($package) . "</comment>)"); $processedUrl = $this->processUrl($package, $url); $hostname = parse_url($processedUrl, PHP_URL_HOST); if (strpos($hostname, '.github.com') === strlen($hostname) - 11) { $hostname = 'github.com'; } try { try { if (!$this->cache || !$this->cache->copyTo($this->getCacheKey($package), $fileName)) { if (!$this->outputProgress) { $this->io->write(' Downloading'); } // try to download 3 times then fail hard $retries = 3; while ($retries--) { try { $this->rfs->copy($hostname, $processedUrl, $fileName, $this->outputProgress); break; } catch (TransportException $e) { // if we got an http response with a proper code, then requesting again will probably not help, abort if (0 !== $e->getCode() || !$retries) { throw $e; } if ($this->io->isVerbose()) { $this->io->write(' Download failed, retrying...'); } usleep(500000); } } if ($this->cache) { $this->cache->copyFrom($this->getCacheKey($package), $fileName); } } else { $this->io->write(' Loading from cache'); } } catch (TransportException $e) { if (in_array($e->getCode(), array(404, 403)) && 'github.com' === $hostname && !$this->io->hasAuthentication($hostname)) { $message = "\n" . 'Could not fetch ' . $processedUrl . ', enter your GitHub credentials ' . ($e->getCode() === 404 ? 'to access private repos' : 'to go over the API rate limit'); $gitHubUtil = new GitHub($this->io, $this->config, null, $this->rfs); if (!$gitHubUtil->authorizeOAuth($hostname) && (!$this->io->isInteractive() || !$gitHubUtil->authorizeOAuthInteractively($hostname, $message))) { throw $e; } $this->rfs->copy($hostname, $processedUrl, $fileName, $this->outputProgress); } else { throw $e; } } if (!file_exists($fileName)) { throw new \UnexpectedValueException($url . ' could not be saved to ' . $fileName . ', make sure the' . ' directory is writable and you have internet connectivity'); } $checksum = $package->getDistSha1Checksum(); if ($checksum && hash_file('sha1', $fileName) !== $checksum) { throw new \UnexpectedValueException('The checksum verification of the file failed (downloaded from ' . $url . ')'); } } catch (\Exception $e) { // clean up $this->filesystem->removeDirectory($path); $this->clearCache($package, $path); throw $e; } }
/** * Runs a command doing attempts for each protocol supported by github. * * @param callable $commandCallable A callable building the command for the given url * @param string $url * @param string $path The directory to remove for each attempt (null if not needed) * @throws \RuntimeException */ protected function runCommand($commandCallable, $url, $path = null) { $handler = array($this, 'outputHandler'); // public github, autoswitch protocols if (preg_match('{^(?:https?|git)(://github.com/.*)}', $url, $match)) { $protocols = $this->config->get('github-protocols'); if (!is_array($protocols)) { throw new \RuntimeException('Config value "github-protocols" must be an array, got ' . gettype($protocols)); } $messages = array(); foreach ($protocols as $protocol) { $url = $protocol . $match[1]; if (0 === $this->process->execute(call_user_func($commandCallable, $url), $handler)) { return; } $messages[] = '- ' . $url . "\n" . preg_replace('#^#m', ' ', $this->process->getErrorOutput()); if (null !== $path) { $this->filesystem->removeDirectory($path); } } // failed to checkout, first check git accessibility $this->throwException('Failed to clone ' . $this->sanitizeUrl($url) . ' via git, https and http protocols, aborting.' . "\n\n" . implode("\n", $messages), $url); } $command = call_user_func($commandCallable, $url); if (0 !== $this->process->execute($command, $handler)) { // private github repository without git access, try https with auth if (preg_match('{^git@(github.com):(.+?)\\.git$}i', $url, $match)) { if (!$this->io->hasAuthentication($match[1])) { $gitHubUtil = new GitHub($this->io, $this->config, $this->process); $message = 'Cloning failed using an ssh key for authentication, enter your GitHub credentials to access private repos'; if (!$gitHubUtil->authorizeOAuth($match[1]) && $this->io->isInteractive()) { $gitHubUtil->authorizeOAuthInteractively($match[1], $message); } } if ($this->io->hasAuthentication($match[1])) { $auth = $this->io->getAuthentication($match[1]); $url = 'https://' . $auth['username'] . ':' . $auth['password'] . '@' . $match[1] . '/' . $match[2] . '.git'; $command = call_user_func($commandCallable, $url); if (0 === $this->process->execute($command, $handler)) { return; } } } if (null !== $path) { $this->filesystem->removeDirectory($path); } $this->throwException('Failed to execute ' . $this->sanitizeUrl($command) . "\n\n" . $this->process->getErrorOutput(), $url); } }