diff --git a/src/Composer/Repository/Vcs/BitbucketDriver.php b/src/Composer/Repository/Vcs/BitbucketDriver.php
new file mode 100644
index 000000000..a1d9a8e26
--- /dev/null
+++ b/src/Composer/Repository/Vcs/BitbucketDriver.php
@@ -0,0 +1,205 @@
+url, $match);
+ $this->owner = $match[1];
+ $this->repository = $match[2];
+ $this->originUrl = 'bitbucket.org';
+ $this->cache = new Cache(
+ $this->io,
+ implode('/', array(
+ $this->config->get('cache-repo-dir'),
+ $this->originUrl,
+ $this->owner,
+ $this->repository
+ ))
+ );
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public function getComposerInformation($identifier)
+ {
+ if ($this->fallbackDriver) {
+ return $this->fallbackDriver->getComposerInformation($identifier);
+ }
+
+ if (!isset($this->infoCache[$identifier])) {
+ if ($this->shouldCache($identifier) && $res = $this->cache->read($identifier)) {
+ return $this->infoCache[$identifier] = JsonFile::parseJson($res);
+ }
+
+ $composer = $this->getBaseComposerInformation($identifier);
+
+ // specials for bitbucket
+ if (!isset($composer['support']['source'])) {
+ $label = array_search(
+ $identifier,
+ $this->getTags()
+ ) ?: array_search(
+ $identifier,
+ $this->getBranches()
+ ) ?: $identifier;
+
+ if (array_key_exists($label, $tags = $this->getTags())) {
+ $hash = $tags[$label];
+ } elseif (array_key_exists($label, $branches = $this->getBranches())) {
+ $hash = $branches[$label];
+ }
+
+ if (! isset($hash)) {
+ $composer['support']['source'] = sprintf(
+ 'https://%s/%s/%s/src',
+ $this->originUrl,
+ $this->owner,
+ $this->repository
+ );
+ } else {
+ $composer['support']['source'] = sprintf(
+ 'https://%s/%s/%s/src/%s/?at=%s',
+ $this->originUrl,
+ $this->owner,
+ $this->repository,
+ $hash,
+ $label
+ );
+ }
+ }
+ if (!isset($composer['support']['issues']) && $this->hasIssues) {
+ $composer['support']['issues'] = sprintf(
+ 'https://%s/%s/%s/issues',
+ $this->originUrl,
+ $this->owner,
+ $this->repository
+ );
+ }
+
+ $this->infoCache[$identifier] = $composer;
+
+ if ($this->shouldCache($identifier)) {
+ $this->cache->write($identifier, json_encode($composer));
+ }
+ }
+
+ return $this->infoCache[$identifier];
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getFileContent($file, $identifier)
+ {
+ if ($this->fallbackDriver) {
+ return $this->fallbackDriver->getFileContent($file, $identifier);
+ }
+
+ $resource = $this->getScheme() . '://api.bitbucket.org/1.0/repositories/'
+ . $this->owner . '/' . $this->repository . '/src/' . $identifier . '/' . $file;
+ $fileData = JsonFile::parseJson($this->getContents($resource), $resource);
+ if (!is_array($fileData) || ! array_key_exists('data', $fileData)) {
+ return null;
+ }
+
+ return $fileData['data'];
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getChangeDate($identifier)
+ {
+ if ($this->fallbackDriver) {
+ return $this->fallbackDriver->getChangeDate($identifier);
+ }
+
+ $resource = $this->getScheme() . '://api.bitbucket.org/1.0/repositories/'
+ . $this->owner . '/' . $this->repository . '/changesets/' . $identifier;
+ $changeset = JsonFile::parseJson($this->getContents($resource), $resource);
+
+ return new \DateTime($changeset['timestamp']);
+ }
+
+ /**
+ * Get the remote content.
+ *
+ * @param string $url The URL of content
+ * @param bool $fetchingRepoData
+ *
+ * @return mixed The result
+ */
+ protected function getContentsWithOAuthCredentials($url, $fetchingRepoData = false)
+ {
+ try {
+ return parent::getContents($url);
+ } catch (TransportException $e) {
+ $bitbucketUtil = new Bitbucket($this->io, $this->config, $this->process, $this->remoteFilesystem);
+
+ if (403 === $e->getCode()) {
+ if (!$this->io->hasAuthentication($this->originUrl)
+ && $bitbucketUtil->authorizeOAuth($this->originUrl)
+ ) {
+ return parent::getContents($url);
+ }
+
+ if (!$this->io->isInteractive() && $fetchingRepoData) {
+ return $this->attemptCloneFallback();
+ }
+ }
+
+ throw $e;
+ }
+ }
+
+ /**
+ * Generate an SSH URL
+ *
+ * @return string
+ */
+ abstract protected function generateSshUrl();
+
+ protected function attemptCloneFallback()
+ {
+ try {
+ $this->setupFallbackDriver($this->generateSshUrl());
+ } catch (\RuntimeException $e) {
+ $this->fallbackDriver = null;
+
+ $this->io->writeError(
+ 'Failed to clone the ' . $this->generateSshUrl() . ' repository, try running in interactive mode'
+ . ' so that you can enter your Bitbucket OAuth consumer credentials'
+ );
+ throw $e;
+ }
+ }
+
+ abstract protected function setupFallbackDriver($url);
+}
diff --git a/src/Composer/Repository/Vcs/FossilDriver.php b/src/Composer/Repository/Vcs/FossilDriver.php
index 58fc9eb1b..b992db7ec 100644
--- a/src/Composer/Repository/Vcs/FossilDriver.php
+++ b/src/Composer/Repository/Vcs/FossilDriver.php
@@ -122,29 +122,27 @@ class FossilDriver extends VcsDriver
}
/**
- * {@inheritDoc}
+ * {@inheritdoc}
*/
- public function getComposerInformation($identifier)
+ public function getFileContent($file, $identifier)
{
- if (!isset($this->infoCache[$identifier])) {
- $command = sprintf('fossil cat -r %s composer.json', ProcessExecutor::escape($identifier));
- $this->process->execute($command, $composer, $this->checkoutDir);
+ $command = sprintf('fossil cat -r %s %s', ProcessExecutor::escape($identifier), ProcessExecutor::escape($file));
+ $this->process->execute($command, $content, $this->checkoutDir);
- if (trim($composer) === '') {
- return;
- }
-
- $composer = JsonFile::parseJson(trim($composer), $identifier);
-
- if (empty($composer['time'])) {
- $this->process->execute(sprintf('fossil finfo composer.json | head -n 2 | tail -n 1 | awk \'{print $1}\''), $output, $this->checkoutDir);
- $date = new \DateTime(trim($output), new \DateTimeZone('UTC'));
- $composer['time'] = $date->format('Y-m-d H:i:s');
- }
- $this->infoCache[$identifier] = $composer;
+ if (!trim($content)) {
+ return null;
}
- return $this->infoCache[$identifier];
+ return $content;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getChangeDate($identifier)
+ {
+ $this->process->execute(sprintf('fossil finfo composer.json | head -n 2 | tail -n 1 | awk \'{print $1}\''), $output, $this->checkoutDir);
+ return new \DateTime(trim($output), new \DateTimeZone('UTC'));
}
/**
diff --git a/src/Composer/Repository/Vcs/GitBitbucketDriver.php b/src/Composer/Repository/Vcs/GitBitbucketDriver.php
index 72af4183e..2874fe77d 100644
--- a/src/Composer/Repository/Vcs/GitBitbucketDriver.php
+++ b/src/Composer/Repository/Vcs/GitBitbucketDriver.php
@@ -12,53 +12,25 @@
namespace Composer\Repository\Vcs;
-use Composer\Cache;
use Composer\Config;
-use Composer\Downloader\TransportException;
use Composer\Json\JsonFile;
use Composer\IO\IOInterface;
-use Composer\Util\Bitbucket;
/**
* @author Per Bernhardt
*/
-class GitBitbucketDriver extends VcsDriver implements VcsDriverInterface
+class GitBitbucketDriver extends BitbucketDriver implements VcsDriverInterface
{
- /**
- * @var Cache
- */
- protected $cache;
- protected $owner;
- protected $repository;
- protected $tags;
- protected $branches;
- protected $rootIdentifier;
- protected $infoCache = array();
- private $hasIssues;
- /**
- * @var GitDriver
- */
- private $gitDriver;
- /**
- * {@inheritDoc}
- */
- public function initialize()
- {
- preg_match('#^https?://bitbucket\.org/([^/]+)/(.+?)\.git$#', $this->url, $match);
- $this->owner = $match[1];
- $this->repository = $match[2];
- $this->originUrl = 'bitbucket.org';
- $this->cache = new Cache($this->io, $this->config->get('cache-repo-dir').'/'.$this->originUrl.'/'.$this->owner.'/'.$this->repository);
- }
+
/**
* {@inheritDoc}
*/
public function getRootIdentifier()
{
- if ($this->gitDriver) {
- return $this->gitDriver->getRootIdentifier();
+ if ($this->fallbackDriver) {
+ return $this->fallbackDriver->getRootIdentifier();
}
if (null === $this->rootIdentifier) {
@@ -76,8 +48,8 @@ class GitBitbucketDriver extends VcsDriver implements VcsDriverInterface
*/
public function getUrl()
{
- if ($this->gitDriver) {
- return $this->gitDriver->getUrl();
+ if ($this->fallbackDriver) {
+ return $this->fallbackDriver->getUrl();
}
return 'https://' . $this->originUrl . '/'.$this->owner.'/'.$this->repository.'.git';
@@ -88,8 +60,8 @@ class GitBitbucketDriver extends VcsDriver implements VcsDriverInterface
*/
public function getSource($identifier)
{
- if ($this->gitDriver) {
- return $this->gitDriver->getSource($identifier);
+ if ($this->fallbackDriver) {
+ return $this->fallbackDriver->getSource($identifier);
}
return array('type' => 'git', 'url' => $this->getUrl(), 'reference' => $identifier);
@@ -105,76 +77,14 @@ class GitBitbucketDriver extends VcsDriver implements VcsDriverInterface
return array('type' => 'zip', 'url' => $url, 'reference' => $identifier, 'shasum' => '');
}
- /**
- * {@inheritDoc}
- */
- public function getComposerInformation($identifier)
- {
- if ($this->gitDriver) {
- return $this->gitDriver->getComposerInformation($identifier);
- }
-
- if (preg_match('{[a-f0-9]{40}}i', $identifier) && $res = $this->cache->read($identifier)) {
- $this->infoCache[$identifier] = JsonFile::parseJson($res);
- }
-
- if (!isset($this->infoCache[$identifier])) {
- $resource = $this->getScheme() . '://api.bitbucket.org/1.0/repositories/'.$this->owner.'/'.$this->repository.'/src/'.$identifier.'/composer.json';
- $file = JsonFile::parseJson($this->getContentsWithOAuthCredentials($resource), $resource);
- if (!is_array($file) || ! array_key_exists('data', $file)) {
- return array();
- }
-
- $composer = JsonFile::parseJson($file['data'], $resource);
-
- if (empty($composer['time'])) {
- $resource = $this->getScheme() . '://api.bitbucket.org/1.0/repositories/'.$this->owner.'/'.$this->repository.'/changesets/'.$identifier;
- $changeset = JsonFile::parseJson($this->getContentsWithOAuthCredentials($resource), $resource);
- $composer['time'] = $changeset['timestamp'];
- }
- if (!isset($composer['support']['source'])) {
- $label = array_search($identifier, $this->getTags()) ?: array_search($identifier, $this->getBranches()) ?: $identifier;
-
- if (array_key_exists($label, $tags = $this->getTags())) {
- $hash = $tags[$label];
- } elseif (array_key_exists($label, $branches = $this->getBranches())) {
- $hash = $branches[$label];
- }
-
- if (! isset($hash)) {
- $composer['support']['source'] = sprintf('https://%s/%s/%s/src', $this->originUrl, $this->owner, $this->repository);
- } else {
- $composer['support']['source'] = sprintf(
- 'https://%s/%s/%s/src/%s/?at=%s',
- $this->originUrl,
- $this->owner,
- $this->repository,
- $hash,
- $label
- );
- }
- }
- if (!isset($composer['support']['issues']) && $this->hasIssues) {
- $composer['support']['issues'] = sprintf('https://%s/%s/%s/issues', $this->originUrl, $this->owner, $this->repository);
- }
-
- if (preg_match('{[a-f0-9]{40}}i', $identifier)) {
- $this->cache->write($identifier, json_encode($composer));
- }
-
- $this->infoCache[$identifier] = $composer;
- }
-
- return $this->infoCache[$identifier];
- }
/**
* {@inheritDoc}
*/
public function getTags()
{
- if ($this->gitDriver) {
- return $this->gitDriver->getTags();
+ if ($this->fallbackDriver) {
+ return $this->fallbackDriver->getTags();
}
if (null === $this->tags) {
@@ -194,8 +104,8 @@ class GitBitbucketDriver extends VcsDriver implements VcsDriverInterface
*/
public function getBranches()
{
- if ($this->gitDriver) {
- return $this->gitDriver->getBranches();
+ if ($this->fallbackDriver) {
+ return $this->fallbackDriver->getBranches();
}
if (null === $this->branches) {
@@ -228,75 +138,26 @@ class GitBitbucketDriver extends VcsDriver implements VcsDriverInterface
return true;
}
- protected function attemptCloneFallback()
- {
- try {
- $this->setupGitDriver($this->generateSshUrl());
-
- return;
- } catch (\RuntimeException $e) {
- $this->gitDriver = null;
-
- $this->io->writeError('Failed to clone the '.$this->generateSshUrl().' repository, try running in interactive mode so that you can enter your Bitbucket OAuth consumer credentials');
- throw $e;
- }
- }
-
- /**
- * Generate an SSH URL
- *
- * @return string
- */
- private function generateSshUrl()
- {
- return 'git@' . $this->originUrl . ':' . $this->owner.'/'.$this->repository.'.git';
- }
-
- /**
- * Get the remote content.
- *
- * @param string $url The URL of content
- * @param bool $fetchingRepoData
- *
- * @return mixed The result
- */
- protected function getContentsWithOAuthCredentials($url, $fetchingRepoData = false)
- {
- try {
- return parent::getContents($url);
- } catch (TransportException $e) {
- $bitbucketUtil = new Bitbucket($this->io, $this->config, $this->process, $this->remoteFilesystem);
-
- switch ($e->getCode()) {
- case 403:
- if (!$this->io->hasAuthentication($this->originUrl) && $bitbucketUtil->authorizeOAuth($this->originUrl)) {
- return parent::getContents($url);
- }
-
- if (!$this->io->isInteractive() && $fetchingRepoData) {
- return $this->attemptCloneFallback();
- }
-
- throw $e;
-
- default:
- throw $e;
- }
- }
- }
-
/**
* @param string $url
*/
- private function setupGitDriver($url)
+ protected function setupFallbackDriver($url)
{
- $this->gitDriver = new GitDriver(
+ $this->fallbackDriver = new GitDriver(
array('url' => $url),
$this->io,
$this->config,
$this->process,
$this->remoteFilesystem
);
- $this->gitDriver->initialize();
+ $this->fallbackDriver->initialize();
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function generateSshUrl()
+ {
+ return 'git@' . $this->originUrl . ':' . $this->owner.'/'.$this->repository.'.git';
}
}
diff --git a/src/Composer/Repository/Vcs/GitDriver.php b/src/Composer/Repository/Vcs/GitDriver.php
index bacad4d2c..7093ce94d 100644
--- a/src/Composer/Repository/Vcs/GitDriver.php
+++ b/src/Composer/Repository/Vcs/GitDriver.php
@@ -120,38 +120,30 @@ class GitDriver extends VcsDriver
}
/**
- * {@inheritDoc}
+ * {@inheritdoc}
*/
- public function getComposerInformation($identifier)
+ public function getFileContent($file, $identifier)
{
- if (preg_match('{[a-f0-9]{40}}i', $identifier) && $res = $this->cache->read($identifier)) {
- $this->infoCache[$identifier] = JsonFile::parseJson($res);
+ $resource = sprintf('%s:%s', ProcessExecutor::escape($identifier), ProcessExecutor::escape($file));
+ $this->process->execute(sprintf('git show %s', $resource), $content, $this->repoDir);
+
+ if (!trim($content)) {
+ return null;
}
- if (!isset($this->infoCache[$identifier])) {
- $resource = sprintf('%s:composer.json', ProcessExecutor::escape($identifier));
- $this->process->execute(sprintf('git show %s', $resource), $composer, $this->repoDir);
+ return $content;
+ }
- if (!trim($composer)) {
- return;
- }
-
- $composer = JsonFile::parseJson($composer, $resource);
-
- if (empty($composer['time'])) {
- $this->process->execute(sprintf('git log -1 --format=%%at %s', ProcessExecutor::escape($identifier)), $output, $this->repoDir);
- $date = new \DateTime('@'.trim($output), new \DateTimeZone('UTC'));
- $composer['time'] = $date->format('Y-m-d H:i:s');
- }
-
- if (preg_match('{[a-f0-9]{40}}i', $identifier)) {
- $this->cache->write($identifier, json_encode($composer));
- }
-
- $this->infoCache[$identifier] = $composer;
- }
-
- return $this->infoCache[$identifier];
+ /**
+ * {@inheritdoc}
+ */
+ public function getChangeDate($identifier)
+ {
+ $this->process->execute(sprintf(
+ 'git log -1 --format=%%at %s',
+ ProcessExecutor::escape($identifier)
+ ), $output, $this->repoDir);
+ return new \DateTime('@'.trim($output), new \DateTimeZone('UTC'));
}
/**
diff --git a/src/Composer/Repository/Vcs/GitHubDriver.php b/src/Composer/Repository/Vcs/GitHubDriver.php
index 04d7321f5..b33b99916 100644
--- a/src/Composer/Repository/Vcs/GitHubDriver.php
+++ b/src/Composer/Repository/Vcs/GitHubDriver.php
@@ -146,50 +146,23 @@ class GitHubDriver extends VcsDriver
return $this->gitDriver->getComposerInformation($identifier);
}
- if (preg_match('{[a-f0-9]{40}}i', $identifier) && $res = $this->cache->read($identifier)) {
- $this->infoCache[$identifier] = JsonFile::parseJson($res);
- }
-
if (!isset($this->infoCache[$identifier])) {
- $notFoundRetries = 2;
- while ($notFoundRetries) {
- try {
- $resource = $this->getApiUrl() . '/repos/'.$this->owner.'/'.$this->repository.'/contents/composer.json?ref='.urlencode($identifier);
- $resource = JsonFile::parseJson($this->getContents($resource));
- if (empty($resource['content']) || $resource['encoding'] !== 'base64' || !($composer = base64_decode($resource['content']))) {
- throw new \RuntimeException('Could not retrieve composer.json for '.$identifier);
- }
- break;
- } catch (TransportException $e) {
- if (404 !== $e->getCode()) {
- throw $e;
- }
-
- // TODO should be removed when possible
- // retry fetching if github returns a 404 since they happen randomly
- $notFoundRetries--;
- $composer = null;
- }
+ if ($this->shouldCache($identifier) && $res = $this->cache->read($identifier)) {
+ return $this->infoCache[$identifier] = JsonFile::parseJson($res);
}
- if ($composer) {
- $composer = JsonFile::parseJson($composer, $resource);
+ $composer = $this->getBaseComposerInformation($identifier);
- if (empty($composer['time'])) {
- $resource = $this->getApiUrl() . '/repos/'.$this->owner.'/'.$this->repository.'/commits/'.urlencode($identifier);
- $commit = JsonFile::parseJson($this->getContents($resource), $resource);
- $composer['time'] = $commit['commit']['committer']['date'];
- }
- if (!isset($composer['support']['source'])) {
- $label = array_search($identifier, $this->getTags()) ?: array_search($identifier, $this->getBranches()) ?: $identifier;
- $composer['support']['source'] = sprintf('https://%s/%s/%s/tree/%s', $this->originUrl, $this->owner, $this->repository, $label);
- }
- if (!isset($composer['support']['issues']) && $this->hasIssues) {
- $composer['support']['issues'] = sprintf('https://%s/%s/%s/issues', $this->originUrl, $this->owner, $this->repository);
- }
+ // specials for github
+ if (!isset($composer['support']['source'])) {
+ $label = array_search($identifier, $this->getTags()) ?: array_search($identifier, $this->getBranches()) ?: $identifier;
+ $composer['support']['source'] = sprintf('https://%s/%s/%s/tree/%s', $this->originUrl, $this->owner, $this->repository, $label);
+ }
+ if (!isset($composer['support']['issues']) && $this->hasIssues) {
+ $composer['support']['issues'] = sprintf('https://%s/%s/%s/issues', $this->originUrl, $this->owner, $this->repository);
}
- if ($composer && preg_match('{[a-f0-9]{40}}i', $identifier)) {
+ if ($this->shouldCache($identifier)) {
$this->cache->write($identifier, json_encode($composer));
}
@@ -199,6 +172,54 @@ class GitHubDriver extends VcsDriver
return $this->infoCache[$identifier];
}
+ /**
+ * {@inheritdoc}
+ */
+ public function getFileContent($file, $identifier)
+ {
+ if ($this->gitDriver) {
+ return $this->gitDriver->getFileContent($file, $identifier);
+ }
+
+ $notFoundRetries = 2;
+ while ($notFoundRetries) {
+ try {
+
+ $resource = $this->getApiUrl() . '/repos/'.$this->owner.'/'.$this->repository.'/contents/' . $file . '?ref='.urlencode($identifier);
+ $resource = JsonFile::parseJson($this->getContents($resource));
+ if (empty($resource['content']) || $resource['encoding'] !== 'base64' || !($content = base64_decode($resource['content']))) {
+ throw new \RuntimeException('Could not retrieve ' . $file . ' for '.$identifier);
+ }
+
+ return $content;
+ } catch (TransportException $e) {
+ if (404 !== $e->getCode()) {
+ throw $e;
+ }
+
+ // TODO should be removed when possible
+ // retry fetching if github returns a 404 since they happen randomly
+ $notFoundRetries--;
+ return null;
+ }
+ }
+
+ return null;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getChangeDate($identifier) {
+ if ($this->gitDriver) {
+ return $this->gitDriver->getChangeDate($identifier);
+ }
+
+ $resource = $this->getApiUrl() . '/repos/'.$this->owner.'/'.$this->repository.'/commits/'.urlencode($identifier);
+ $commit = JsonFile::parseJson($this->getContents($resource), $resource);
+ return new \DateTime($commit['commit']['committer']['date']);
+ }
+
/**
* {@inheritDoc}
*/
diff --git a/src/Composer/Repository/Vcs/GitLabDriver.php b/src/Composer/Repository/Vcs/GitLabDriver.php
index ed91056d4..5f3538869 100644
--- a/src/Composer/Repository/Vcs/GitLabDriver.php
+++ b/src/Composer/Repository/Vcs/GitLabDriver.php
@@ -32,9 +32,6 @@ class GitLabDriver extends VcsDriver
private $owner;
private $repository;
- private $cache;
- private $infoCache = array();
-
/**
* @var array Project data returned by GitLab API
*/
@@ -99,13 +96,9 @@ class GitLabDriver extends VcsDriver
}
/**
- * Fetches the composer.json file from the project by a identifier.
- *
- * if specific keys arent present it will try and infer them by default values.
- *
- * {@inheritDoc}
+ * {@inheritdoc}
*/
- public function getComposerInformation($identifier)
+ public function getFileContent($file, $identifier)
{
// Convert the root identifier to a cachable commit id
if (!preg_match('{[a-f0-9]{40}}i', $identifier)) {
@@ -115,34 +108,33 @@ class GitLabDriver extends VcsDriver
}
}
- if (isset($this->infoCache[$identifier])) {
- return $this->infoCache[$identifier];
- }
-
- if (preg_match('{[a-f0-9]{40}}i', $identifier) && $res = $this->cache->read($identifier)) {
- return $this->infoCache[$identifier] = JsonFile::parseJson($res, $res);
- }
+ $resource = $this->getApiUrl().'/repository/blobs/'.$identifier.'?filepath=' . $file;
try {
- $composer = $this->fetchComposerFile($identifier);
+ $content = $this->getContents($resource);
} catch (TransportException $e) {
if ($e->getCode() !== 404) {
throw $e;
}
- $composer = false;
+ return null;
}
- if ($composer && !isset($composer['time']) && isset($this->commits[$identifier])) {
- $composer['time'] = $this->commits[$identifier]['committed_date'];
- }
-
- if (preg_match('{[a-f0-9]{40}}i', $identifier)) {
- $this->cache->write($identifier, json_encode($composer));
- }
-
- return $this->infoCache[$identifier] = $composer;
+ return $content;
}
+ /**
+ * {@inheritdoc}
+ */
+ public function getChangeDate($identifier)
+ {
+ if (isset($this->commits[$identifier])) {
+ return new \DateTime($this->commits[$identifier]['committed_date']);
+ }
+
+ return new \DateTime();
+ }
+
+
/**
* {@inheritDoc}
*/
@@ -209,20 +201,6 @@ class GitLabDriver extends VcsDriver
return $this->tags;
}
- /**
- * Fetches composer.json file from the repository through api.
- *
- * @param string $identifier
- *
- * @return array
- */
- protected function fetchComposerFile($identifier)
- {
- $resource = $this->getApiUrl().'/repository/blobs/'.$identifier.'?filepath=composer.json';
-
- return JsonFile::parseJson($this->getContents($resource), $resource);
- }
-
/**
* @return string Base URL for GitLab API v3
*/
diff --git a/src/Composer/Repository/Vcs/HgBitbucketDriver.php b/src/Composer/Repository/Vcs/HgBitbucketDriver.php
index eb6808601..c456121c0 100644
--- a/src/Composer/Repository/Vcs/HgBitbucketDriver.php
+++ b/src/Composer/Repository/Vcs/HgBitbucketDriver.php
@@ -12,7 +12,6 @@
namespace Composer\Repository\Vcs;
-use Composer\Cache;
use Composer\Config;
use Composer\Json\JsonFile;
use Composer\IO\IOInterface;
@@ -20,27 +19,8 @@ use Composer\IO\IOInterface;
/**
* @author Per Bernhardt
*/
-class HgBitbucketDriver extends VcsDriver
+class HgBitbucketDriver extends BitbucketDriver
{
- protected $cache;
- protected $owner;
- protected $repository;
- protected $tags;
- protected $branches;
- protected $rootIdentifier;
- protected $infoCache = array();
-
- /**
- * {@inheritDoc}
- */
- public function initialize()
- {
- preg_match('#^https?://bitbucket\.org/([^/]+)/([^/]+)/?$#', $this->url, $match);
- $this->owner = $match[1];
- $this->repository = $match[2];
- $this->originUrl = 'bitbucket.org';
- $this->cache = new Cache($this->io, $this->config->get('cache-repo-dir').'/'.$this->originUrl.'/'.$this->owner.'/'.$this->repository);
- }
/**
* {@inheritDoc}
@@ -53,6 +33,7 @@ class HgBitbucketDriver extends VcsDriver
if (array() === $repoData || !isset($repoData['tip'])) {
throw new \RuntimeException($this->url.' does not appear to be a mercurial repository, use '.$this->url.'.git if this is a git bitbucket repository');
}
+ $this->hasIssues = !empty($repoData['has_issues']);
$this->rootIdentifier = $repoData['tip']['raw_node'];
}
@@ -85,46 +66,6 @@ class HgBitbucketDriver extends VcsDriver
return array('type' => 'zip', 'url' => $url, 'reference' => $identifier, 'shasum' => '');
}
- /**
- * {@inheritDoc}
- */
- public function getComposerInformation($identifier)
- {
- if (preg_match('{[a-f0-9]{40}}i', $identifier) && $res = $this->cache->read($identifier)) {
- $this->infoCache[$identifier] = JsonFile::parseJson($res);
- }
-
- if (!isset($this->infoCache[$identifier])) {
- $resource = $this->getScheme() . '://bitbucket.org/api/1.0/repositories/'.$this->owner.'/'.$this->repository.'/src/'.$identifier.'/composer.json';
- $repoData = JsonFile::parseJson($this->getContents($resource), $resource);
-
- // Bitbucket does not send different response codes for found and
- // not found files, so we have to check the response structure.
- // found: {node: ..., data: ..., size: ..., ...}
- // not found: {node: ..., files: [...], directories: [...], ...}
-
- if (!array_key_exists('data', $repoData)) {
- return;
- }
-
- $composer = JsonFile::parseJson($repoData['data'], $resource);
-
- if (empty($composer['time'])) {
- $resource = $this->getScheme() . '://bitbucket.org/api/1.0/repositories/'.$this->owner.'/'.$this->repository.'/changesets/'.$identifier;
- $changeset = JsonFile::parseJson($this->getContents($resource), $resource);
- $composer['time'] = $changeset['timestamp'];
- }
-
- if (preg_match('{[a-f0-9]{40}}i', $identifier)) {
- $this->cache->write($identifier, json_encode($composer));
- }
-
- $this->infoCache[$identifier] = $composer;
- }
-
- return $this->infoCache[$identifier];
- }
-
/**
* {@inheritDoc}
*/
@@ -177,4 +118,24 @@ class HgBitbucketDriver extends VcsDriver
return true;
}
+
+ protected function setupFallbackDriver($url)
+ {
+ $this->fallbackDriver = new HgDriver(
+ array('url' => $url),
+ $this->io,
+ $this->config,
+ $this->process,
+ $this->remoteFilesystem
+ );
+ $this->fallbackDriver->initialize();
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function generateSshUrl()
+ {
+ return 'hg@' . $this->originUrl . '/' . $this->owner.'/'.$this->repository;
+ }
}
diff --git a/src/Composer/Repository/Vcs/HgDriver.php b/src/Composer/Repository/Vcs/HgDriver.php
index 4dc103d38..bad52bed3 100644
--- a/src/Composer/Repository/Vcs/HgDriver.php
+++ b/src/Composer/Repository/Vcs/HgDriver.php
@@ -17,6 +17,7 @@ use Composer\Json\JsonFile;
use Composer\Util\ProcessExecutor;
use Composer\Util\Filesystem;
use Composer\IO\IOInterface;
+use Symfony\Component\Process\Process;
/**
* @author Per Bernhardt
@@ -114,28 +115,34 @@ class HgDriver extends VcsDriver
}
/**
- * {@inheritDoc}
+ * {@inheritdoc}
*/
- public function getComposerInformation($identifier)
+ public function getFileContent($file, $identifier)
{
- if (!isset($this->infoCache[$identifier])) {
- $this->process->execute(sprintf('hg cat -r %s composer.json', ProcessExecutor::escape($identifier)), $composer, $this->repoDir);
+ $resource = sprintf('hg cat -r %s %s', ProcessExecutor::escape($identifier), ProcessExecutor::escape($file));
+ $this->process->execute(sprintf('hg cat -r %s', $resource), $content, $this->repoDir);
- if (!trim($composer)) {
- return;
- }
-
- $composer = JsonFile::parseJson($composer, $identifier);
-
- if (empty($composer['time'])) {
- $this->process->execute(sprintf('hg log --template "{date|rfc3339date}" -r %s', ProcessExecutor::escape($identifier)), $output, $this->repoDir);
- $date = new \DateTime(trim($output), new \DateTimeZone('UTC'));
- $composer['time'] = $date->format('Y-m-d H:i:s');
- }
- $this->infoCache[$identifier] = $composer;
+ if (!trim($content)) {
+ return;
}
- return $this->infoCache[$identifier];
+ return $content;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getChangeDate($identifier)
+ {
+ $this->process->execute(
+ sprintf(
+ 'hg log --template "{date|rfc3339date}" -r %s',
+ ProcessExecutor::escape($identifier)
+ ),
+ $output,
+ $this->repoDir
+ );
+ return new \DateTime(trim($output), new \DateTimeZone('UTC'));
}
/**
diff --git a/src/Composer/Repository/Vcs/PerforceDriver.php b/src/Composer/Repository/Vcs/PerforceDriver.php
index 01d4a52d8..7aaa6efb4 100644
--- a/src/Composer/Repository/Vcs/PerforceDriver.php
+++ b/src/Composer/Repository/Vcs/PerforceDriver.php
@@ -24,10 +24,8 @@ class PerforceDriver extends VcsDriver
{
protected $depot;
protected $branch;
+ /** @var Perforce */
protected $perforce;
- protected $composerInfo;
- protected $composerInfoIdentifier;
-
/**
* {@inheritDoc}
*/
@@ -59,19 +57,21 @@ class PerforceDriver extends VcsDriver
$this->perforce = Perforce::create($repoConfig, $this->getUrl(), $repoDir, $this->process, $this->io);
}
- /**
- * {@inheritDoc}
- */
- public function getComposerInformation($identifier)
- {
- if (!empty($this->composerInfoIdentifier)) {
- if (strcmp($identifier, $this->composerInfoIdentifier) === 0) {
- return $this->composerInfo;
- }
- }
- $composer_info = $this->perforce->getComposerInformation($identifier);
- return $composer_info;
+ /**
+ * {@inheritdoc}
+ */
+ public function getFileContent($file, $identifier)
+ {
+ return $this->perforce->getFileContent($file, $identifier);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getChangeDate($identifier)
+ {
+ return null;
}
/**
@@ -138,10 +138,10 @@ class PerforceDriver extends VcsDriver
*/
public function hasComposerFile($identifier)
{
- $this->composerInfo = $this->perforce->getComposerInformation('//' . $this->depot . '/' . $identifier);
- $this->composerInfoIdentifier = $identifier;
+ $composerInfo = $this->perforce->getComposerInformation('//' . $this->depot . '/' . $identifier);
+ $composerInfoIdentifier = $identifier;
- return !empty($this->composerInfo);
+ return !empty($composerInfo);
}
/**
diff --git a/src/Composer/Repository/Vcs/SvnDriver.php b/src/Composer/Repository/Vcs/SvnDriver.php
index c602ddaa3..f2d6a4754 100644
--- a/src/Composer/Repository/Vcs/SvnDriver.php
+++ b/src/Composer/Repository/Vcs/SvnDriver.php
@@ -116,56 +116,82 @@ class SvnDriver extends VcsDriver
}
/**
- * {@inheritDoc}
+ * {@inheritdoc}
*/
public function getComposerInformation($identifier)
{
- $identifier = '/' . trim($identifier, '/') . '/';
-
- if ($res = $this->cache->read($identifier.'.json')) {
- $this->infoCache[$identifier] = JsonFile::parseJson($res);
- }
-
if (!isset($this->infoCache[$identifier])) {
- preg_match('{^(.+?)(@\d+)?/$}', $identifier, $match);
- if (!empty($match[2])) {
- $path = $match[1];
- $rev = $match[2];
- } else {
- $path = $identifier;
- $rev = '';
+ if ($res = $this->cache->read($identifier.'.json')) {
+ return $this->infoCache[$identifier] = JsonFile::parseJson($res);
}
- try {
- $resource = $path.'composer.json';
- $output = $this->execute('svn cat', $this->baseUrl . $resource . $rev);
- if (!trim($output)) {
- return;
- }
- } catch (\RuntimeException $e) {
- throw new TransportException($e->getMessage());
- }
-
- $composer = JsonFile::parseJson($output, $this->baseUrl . $resource . $rev);
-
- if (empty($composer['time'])) {
- $output = $this->execute('svn info', $this->baseUrl . $path . $rev);
- foreach ($this->process->splitLines($output) as $line) {
- if ($line && preg_match('{^Last Changed Date: ([^(]+)}', $line, $match)) {
- $date = new \DateTime($match[1], new \DateTimeZone('UTC'));
- $composer['time'] = $date->format('Y-m-d H:i:s');
- break;
- }
- }
- }
+ $composer = $this->getBaseComposerInformation($identifier);
$this->cache->write($identifier.'.json', json_encode($composer));
+
$this->infoCache[$identifier] = $composer;
}
+
return $this->infoCache[$identifier];
}
+ /**
+ * @param string $file
+ * @param string $identifier
+ */
+ public function getFileContent($file, $identifier)
+ {
+ $identifier = '/' . trim($identifier, '/') . '/';
+
+ preg_match('{^(.+?)(@\d+)?/$}', $identifier, $match);
+ if (!empty($match[2])) {
+ $path = $match[1];
+ $rev = $match[2];
+ } else {
+ $path = $identifier;
+ $rev = '';
+ }
+
+ try {
+ $resource = $path.$file;
+ $output = $this->execute('svn cat', $this->baseUrl . $resource . $rev);
+ if (!trim($output)) {
+ return null;
+ }
+ } catch (\RuntimeException $e) {
+ throw new TransportException($e->getMessage());
+ }
+
+ return $output;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getChangeDate($identifier)
+ {
+ $identifier = '/' . trim($identifier, '/') . '/';
+
+ preg_match('{^(.+?)(@\d+)?/$}', $identifier, $match);
+ if (!empty($match[2])) {
+ $path = $match[1];
+ $rev = $match[2];
+ } else {
+ $path = $identifier;
+ $rev = '';
+ }
+
+ $output = $this->execute('svn info', $this->baseUrl . $path . $rev);
+ foreach ($this->process->splitLines($output) as $line) {
+ if ($line && preg_match('{^Last Changed Date: ([^(]+)}', $line, $match)) {
+ return new \DateTime($match[1], new \DateTimeZone('UTC'));
+ }
+ }
+
+ return null;
+ }
+
/**
* {@inheritDoc}
*/
diff --git a/src/Composer/Repository/Vcs/VcsDriver.php b/src/Composer/Repository/Vcs/VcsDriver.php
index 56f06a580..c72a6c6a8 100644
--- a/src/Composer/Repository/Vcs/VcsDriver.php
+++ b/src/Composer/Repository/Vcs/VcsDriver.php
@@ -12,10 +12,12 @@
namespace Composer\Repository\Vcs;
+use Composer\Cache;
use Composer\Downloader\TransportException;
use Composer\Config;
use Composer\Factory;
use Composer\IO\IOInterface;
+use Composer\Json\JsonFile;
use Composer\Util\ProcessExecutor;
use Composer\Util\RemoteFilesystem;
use Composer\Util\Filesystem;
@@ -41,6 +43,10 @@ abstract class VcsDriver implements VcsDriverInterface
protected $process;
/** @var RemoteFilesystem */
protected $remoteFilesystem;
+ /** @var array */
+ protected $infoCache = array();
+ /** @var Cache */
+ protected $cache;
/**
* Constructor.
@@ -66,6 +72,57 @@ abstract class VcsDriver implements VcsDriverInterface
$this->remoteFilesystem = $remoteFilesystem ?: Factory::createRemoteFilesystem($this->io, $config);
}
+ /**
+ * Returns whether or not the given $identifier should be cached or not.
+ *
+ * @param string $identifier
+ * @return boolean
+ */
+ protected function shouldCache($identifier)
+ {
+ return $this->cache && preg_match('{[a-f0-9]{40}}i', $identifier);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getComposerInformation($identifier)
+ {
+ if (!isset($this->infoCache[$identifier])) {
+ if ($this->shouldCache($identifier) && $res = $this->cache->read($identifier)) {
+ return $this->infoCache[$identifier] = JsonFile::parseJson($res);
+ }
+
+ $composer = $this->getBaseComposerInformation($identifier);
+
+ if ($this->shouldCache($identifier)) {
+ $this->cache->write($identifier, json_encode($composer));
+ }
+
+ $this->infoCache[$identifier] = $composer;
+ }
+
+
+ return $this->infoCache[$identifier];
+ }
+
+ protected function getBaseComposerInformation($identifier)
+ {
+ $composerFileContent = $this->getFileContent('composer.json', $identifier);
+
+ if (!$composerFileContent) {
+ return null;
+ }
+
+ $composer = JsonFile::parseJson($composerFileContent, $identifier . ':composer.json');
+
+ if (empty($composer['time']) && $changeDate = $this->getChangeDate($identifier)) {
+ $composer['time'] = $changeDate->format('Y-m-d H:i:s');
+ }
+
+ return $composer;
+ }
+
/**
* {@inheritDoc}
*/
diff --git a/src/Composer/Repository/Vcs/VcsDriverInterface.php b/src/Composer/Repository/Vcs/VcsDriverInterface.php
index 307d63ef6..5f77b3161 100644
--- a/src/Composer/Repository/Vcs/VcsDriverInterface.php
+++ b/src/Composer/Repository/Vcs/VcsDriverInterface.php
@@ -33,6 +33,23 @@ interface VcsDriverInterface
*/
public function getComposerInformation($identifier);
+ /**
+ * Return the content of $file or null if the file does not exist.
+ *
+ * @param string $file
+ * @param string $identifier
+ * @return string
+ */
+ public function getFileContent($file, $identifier);
+
+ /**
+ * Get the changedate for $identifier.
+ *
+ * @param string $identifier
+ * @return \DateTime
+ */
+ public function getChangeDate($identifier);
+
/**
* Return the root identifier (trunk, master, default/tip ..)
*
diff --git a/src/Composer/Util/Perforce.php b/src/Composer/Util/Perforce.php
index 632238985..f3c20fce3 100644
--- a/src/Composer/Util/Perforce.php
+++ b/src/Composer/Util/Perforce.php
@@ -304,7 +304,9 @@ class Perforce
public function connectClient()
{
- $p4CreateClientCommand = $this->generateP4Command('client -i < ' . str_replace(" ", "\\ ", $this->getP4ClientSpec()));
+ $p4CreateClientCommand = $this->generateP4Command(
+ 'client -i < ' . str_replace(" ", "\\ ", $this->getP4ClientSpec())
+ );
$this->executeCommand($p4CreateClientCommand);
}
@@ -396,56 +398,55 @@ class Perforce
}
public function getComposerInformation($identifier)
+ {
+ $composerFileContent = $this->getFileContent('composer.json', $identifier);
+
+ if (!$composerFileContent) {
+ return;
+ }
+
+ return json_decode($composerFileContent, true);
+ }
+
+ public function getFileContent($file, $identifier)
+ {
+ $path = $this->getFilePath($file, $identifier);
+
+ $command = $this->generateP4Command(' print ' . $path);
+ $this->executeCommand($command);
+ $result = $this->commandResult;
+
+ if (!trim($result)) {
+ return null;
+ }
+
+ return $result;
+ }
+
+ public function getFilePath($file, $identifier)
{
$index = strpos($identifier, '@');
if ($index === false) {
- $composerJson = $identifier. '/composer.json';
+ $path = $identifier. '/' . $file;
- return $this->getComposerInformationFromPath($composerJson);
- }
-
- return $this->getComposerInformationFromLabel($identifier, $index);
- }
-
- public function getComposerInformationFromPath($composerJson)
- {
- $command = $this->generateP4Command(' print ' . $composerJson);
- $this->executeCommand($command);
- $result = $this->commandResult;
- $index = strpos($result, '{');
- if ($index === false) {
- return '';
- }
- if ($index >= 0) {
- $rawData = substr($result, $index);
- $composer_info = json_decode($rawData, true);
-
- return $composer_info;
- }
-
- return '';
- }
-
- public function getComposerInformationFromLabel($identifier, $index)
- {
- $composerJsonPath = substr($identifier, 0, $index) . '/composer.json' . substr($identifier, $index);
- $command = $this->generateP4Command(' files ' . $composerJsonPath, false);
- $this->executeCommand($command);
- $result = $this->commandResult;
- $index2 = strpos($result, 'no such file(s).');
- if ($index2 === false) {
- $index3 = strpos($result, 'change');
- if (!($index3 === false)) {
- $phrase = trim(substr($result, $index3));
- $fields = explode(' ', $phrase);
- $id = $fields[1];
- $composerJson = substr($identifier, 0, $index) . '/composer.json@' . $id;
-
- return $this->getComposerInformationFromPath($composerJson);
+ return $path;
+ } else {
+ $path = substr($identifier, 0, $index) . '/' . $file . substr($identifier, $index);
+ $command = $this->generateP4Command(' files ' . $path, false);
+ $this->executeCommand($command);
+ $result = $this->commandResult;
+ $index2 = strpos($result, 'no such file(s).');
+ if ($index2 === false) {
+ $index3 = strpos($result, 'change');
+ if ($index3 !== false) {
+ $phrase = trim(substr($result, $index3));
+ $fields = explode(' ', $phrase);
+ return substr($identifier, 0, $index) . '/' . $file . '@' . $fields[1];
+ }
}
}
- return "";
+ return null;
}
public function getBranches()