Merge remote-tracking branch 'tflori/feature-getFileContent'
commit
b2e1d4cb9d
|
@ -0,0 +1,205 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Composer\Repository\Vcs;
|
||||||
|
|
||||||
|
use Composer\Cache;
|
||||||
|
use Composer\Downloader\TransportException;
|
||||||
|
use Composer\Json\JsonFile;
|
||||||
|
use Composer\Util\Bitbucket;
|
||||||
|
|
||||||
|
abstract class BitbucketDriver extends VcsDriver
|
||||||
|
{
|
||||||
|
/** @var Cache */
|
||||||
|
protected $cache;
|
||||||
|
protected $owner;
|
||||||
|
protected $repository;
|
||||||
|
protected $hasIssues;
|
||||||
|
protected $rootIdentifier;
|
||||||
|
protected $tags;
|
||||||
|
protected $branches;
|
||||||
|
protected $infoCache = array();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var VcsDriver
|
||||||
|
*/
|
||||||
|
protected $fallbackDriver;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@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,
|
||||||
|
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(
|
||||||
|
'<error>Failed to clone the ' . $this->generateSshUrl() . ' repository, try running in interactive mode'
|
||||||
|
. ' so that you can enter your Bitbucket OAuth consumer credentials</error>'
|
||||||
|
);
|
||||||
|
throw $e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract protected function setupFallbackDriver($url);
|
||||||
|
}
|
|
@ -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 %s', ProcessExecutor::escape($identifier), ProcessExecutor::escape($file));
|
||||||
$command = sprintf('fossil cat -r %s composer.json', ProcessExecutor::escape($identifier));
|
$this->process->execute($command, $content, $this->checkoutDir);
|
||||||
$this->process->execute($command, $composer, $this->checkoutDir);
|
|
||||||
|
|
||||||
if (trim($composer) === '') {
|
if (!trim($content)) {
|
||||||
return;
|
return null;
|
||||||
}
|
|
||||||
|
|
||||||
$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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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'));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -12,53 +12,25 @@
|
||||||
|
|
||||||
namespace Composer\Repository\Vcs;
|
namespace Composer\Repository\Vcs;
|
||||||
|
|
||||||
use Composer\Cache;
|
|
||||||
use Composer\Config;
|
use Composer\Config;
|
||||||
use Composer\Downloader\TransportException;
|
|
||||||
use Composer\Json\JsonFile;
|
use Composer\Json\JsonFile;
|
||||||
use Composer\IO\IOInterface;
|
use Composer\IO\IOInterface;
|
||||||
use Composer\Util\Bitbucket;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Per Bernhardt <plb@webfactory.de>
|
* @author Per Bernhardt <plb@webfactory.de>
|
||||||
*/
|
*/
|
||||||
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}
|
* {@inheritDoc}
|
||||||
*/
|
*/
|
||||||
public function getRootIdentifier()
|
public function getRootIdentifier()
|
||||||
{
|
{
|
||||||
if ($this->gitDriver) {
|
if ($this->fallbackDriver) {
|
||||||
return $this->gitDriver->getRootIdentifier();
|
return $this->fallbackDriver->getRootIdentifier();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (null === $this->rootIdentifier) {
|
if (null === $this->rootIdentifier) {
|
||||||
|
@ -76,8 +48,8 @@ class GitBitbucketDriver extends VcsDriver implements VcsDriverInterface
|
||||||
*/
|
*/
|
||||||
public function getUrl()
|
public function getUrl()
|
||||||
{
|
{
|
||||||
if ($this->gitDriver) {
|
if ($this->fallbackDriver) {
|
||||||
return $this->gitDriver->getUrl();
|
return $this->fallbackDriver->getUrl();
|
||||||
}
|
}
|
||||||
|
|
||||||
return 'https://' . $this->originUrl . '/'.$this->owner.'/'.$this->repository.'.git';
|
return 'https://' . $this->originUrl . '/'.$this->owner.'/'.$this->repository.'.git';
|
||||||
|
@ -88,8 +60,8 @@ class GitBitbucketDriver extends VcsDriver implements VcsDriverInterface
|
||||||
*/
|
*/
|
||||||
public function getSource($identifier)
|
public function getSource($identifier)
|
||||||
{
|
{
|
||||||
if ($this->gitDriver) {
|
if ($this->fallbackDriver) {
|
||||||
return $this->gitDriver->getSource($identifier);
|
return $this->fallbackDriver->getSource($identifier);
|
||||||
}
|
}
|
||||||
|
|
||||||
return array('type' => 'git', 'url' => $this->getUrl(), 'reference' => $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' => '');
|
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}
|
* {@inheritDoc}
|
||||||
*/
|
*/
|
||||||
public function getTags()
|
public function getTags()
|
||||||
{
|
{
|
||||||
if ($this->gitDriver) {
|
if ($this->fallbackDriver) {
|
||||||
return $this->gitDriver->getTags();
|
return $this->fallbackDriver->getTags();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (null === $this->tags) {
|
if (null === $this->tags) {
|
||||||
|
@ -194,8 +104,8 @@ class GitBitbucketDriver extends VcsDriver implements VcsDriverInterface
|
||||||
*/
|
*/
|
||||||
public function getBranches()
|
public function getBranches()
|
||||||
{
|
{
|
||||||
if ($this->gitDriver) {
|
if ($this->fallbackDriver) {
|
||||||
return $this->gitDriver->getBranches();
|
return $this->fallbackDriver->getBranches();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (null === $this->branches) {
|
if (null === $this->branches) {
|
||||||
|
@ -228,75 +138,26 @@ class GitBitbucketDriver extends VcsDriver implements VcsDriverInterface
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function attemptCloneFallback()
|
|
||||||
{
|
|
||||||
try {
|
|
||||||
$this->setupGitDriver($this->generateSshUrl());
|
|
||||||
|
|
||||||
return;
|
|
||||||
} catch (\RuntimeException $e) {
|
|
||||||
$this->gitDriver = null;
|
|
||||||
|
|
||||||
$this->io->writeError('<error>Failed to clone the '.$this->generateSshUrl().' repository, try running in interactive mode so that you can enter your Bitbucket OAuth consumer credentials</error>');
|
|
||||||
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
|
* @param string $url
|
||||||
*/
|
*/
|
||||||
private function setupGitDriver($url)
|
protected function setupFallbackDriver($url)
|
||||||
{
|
{
|
||||||
$this->gitDriver = new GitDriver(
|
$this->fallbackDriver = new GitDriver(
|
||||||
array('url' => $url),
|
array('url' => $url),
|
||||||
$this->io,
|
$this->io,
|
||||||
$this->config,
|
$this->config,
|
||||||
$this->process,
|
$this->process,
|
||||||
$this->remoteFilesystem
|
$this->remoteFilesystem
|
||||||
);
|
);
|
||||||
$this->gitDriver->initialize();
|
$this->fallbackDriver->initialize();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
protected function generateSshUrl()
|
||||||
|
{
|
||||||
|
return 'git@' . $this->originUrl . ':' . $this->owner.'/'.$this->repository.'.git';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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)) {
|
$resource = sprintf('%s:%s', ProcessExecutor::escape($identifier), ProcessExecutor::escape($file));
|
||||||
$this->infoCache[$identifier] = JsonFile::parseJson($res);
|
$this->process->execute(sprintf('git show %s', $resource), $content, $this->repoDir);
|
||||||
|
|
||||||
|
if (!trim($content)) {
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!isset($this->infoCache[$identifier])) {
|
return $content;
|
||||||
$resource = sprintf('%s:composer.json', ProcessExecutor::escape($identifier));
|
}
|
||||||
$this->process->execute(sprintf('git show %s', $resource), $composer, $this->repoDir);
|
|
||||||
|
|
||||||
if (!trim($composer)) {
|
/**
|
||||||
return;
|
* {@inheritdoc}
|
||||||
}
|
*/
|
||||||
|
public function getChangeDate($identifier)
|
||||||
$composer = JsonFile::parseJson($composer, $resource);
|
{
|
||||||
|
$this->process->execute(sprintf(
|
||||||
if (empty($composer['time'])) {
|
'git log -1 --format=%%at %s',
|
||||||
$this->process->execute(sprintf('git log -1 --format=%%at %s', ProcessExecutor::escape($identifier)), $output, $this->repoDir);
|
ProcessExecutor::escape($identifier)
|
||||||
$date = new \DateTime('@'.trim($output), new \DateTimeZone('UTC'));
|
), $output, $this->repoDir);
|
||||||
$composer['time'] = $date->format('Y-m-d H:i:s');
|
return new \DateTime('@'.trim($output), new \DateTimeZone('UTC'));
|
||||||
}
|
|
||||||
|
|
||||||
if (preg_match('{[a-f0-9]{40}}i', $identifier)) {
|
|
||||||
$this->cache->write($identifier, json_encode($composer));
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->infoCache[$identifier] = $composer;
|
|
||||||
}
|
|
||||||
|
|
||||||
return $this->infoCache[$identifier];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -146,50 +146,23 @@ class GitHubDriver extends VcsDriver
|
||||||
return $this->gitDriver->getComposerInformation($identifier);
|
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])) {
|
if (!isset($this->infoCache[$identifier])) {
|
||||||
$notFoundRetries = 2;
|
if ($this->shouldCache($identifier) && $res = $this->cache->read($identifier)) {
|
||||||
while ($notFoundRetries) {
|
return $this->infoCache[$identifier] = JsonFile::parseJson($res);
|
||||||
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 ($composer) {
|
$composer = $this->getBaseComposerInformation($identifier);
|
||||||
$composer = JsonFile::parseJson($composer, $resource);
|
|
||||||
|
|
||||||
if (empty($composer['time'])) {
|
// specials for github
|
||||||
$resource = $this->getApiUrl() . '/repos/'.$this->owner.'/'.$this->repository.'/commits/'.urlencode($identifier);
|
if (!isset($composer['support']['source'])) {
|
||||||
$commit = JsonFile::parseJson($this->getContents($resource), $resource);
|
$label = array_search($identifier, $this->getTags()) ?: array_search($identifier, $this->getBranches()) ?: $identifier;
|
||||||
$composer['time'] = $commit['commit']['committer']['date'];
|
$composer['support']['source'] = sprintf('https://%s/%s/%s/tree/%s', $this->originUrl, $this->owner, $this->repository, $label);
|
||||||
}
|
}
|
||||||
if (!isset($composer['support']['source'])) {
|
if (!isset($composer['support']['issues']) && $this->hasIssues) {
|
||||||
$label = array_search($identifier, $this->getTags()) ?: array_search($identifier, $this->getBranches()) ?: $identifier;
|
$composer['support']['issues'] = sprintf('https://%s/%s/%s/issues', $this->originUrl, $this->owner, $this->repository);
|
||||||
$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));
|
$this->cache->write($identifier, json_encode($composer));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -199,6 +172,54 @@ class GitHubDriver extends VcsDriver
|
||||||
return $this->infoCache[$identifier];
|
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}
|
* {@inheritDoc}
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -32,9 +32,6 @@ class GitLabDriver extends VcsDriver
|
||||||
private $owner;
|
private $owner;
|
||||||
private $repository;
|
private $repository;
|
||||||
|
|
||||||
private $cache;
|
|
||||||
private $infoCache = array();
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var array Project data returned by GitLab API
|
* @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.
|
* {@inheritdoc}
|
||||||
*
|
|
||||||
* if specific keys arent present it will try and infer them by default values.
|
|
||||||
*
|
|
||||||
* {@inheritDoc}
|
|
||||||
*/
|
*/
|
||||||
public function getComposerInformation($identifier)
|
public function getFileContent($file, $identifier)
|
||||||
{
|
{
|
||||||
// Convert the root identifier to a cachable commit id
|
// Convert the root identifier to a cachable commit id
|
||||||
if (!preg_match('{[a-f0-9]{40}}i', $identifier)) {
|
if (!preg_match('{[a-f0-9]{40}}i', $identifier)) {
|
||||||
|
@ -115,34 +108,33 @@ class GitLabDriver extends VcsDriver
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isset($this->infoCache[$identifier])) {
|
$resource = $this->getApiUrl().'/repository/blobs/'.$identifier.'?filepath=' . $file;
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
$composer = $this->fetchComposerFile($identifier);
|
$content = $this->getContents($resource);
|
||||||
} catch (TransportException $e) {
|
} catch (TransportException $e) {
|
||||||
if ($e->getCode() !== 404) {
|
if ($e->getCode() !== 404) {
|
||||||
throw $e;
|
throw $e;
|
||||||
}
|
}
|
||||||
$composer = false;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($composer && !isset($composer['time']) && isset($this->commits[$identifier])) {
|
return $content;
|
||||||
$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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function getChangeDate($identifier)
|
||||||
|
{
|
||||||
|
if (isset($this->commits[$identifier])) {
|
||||||
|
return new \DateTime($this->commits[$identifier]['committed_date']);
|
||||||
|
}
|
||||||
|
|
||||||
|
return new \DateTime();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* {@inheritDoc}
|
* {@inheritDoc}
|
||||||
*/
|
*/
|
||||||
|
@ -209,20 +201,6 @@ class GitLabDriver extends VcsDriver
|
||||||
return $this->tags;
|
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
|
* @return string Base URL for GitLab API v3
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -12,7 +12,6 @@
|
||||||
|
|
||||||
namespace Composer\Repository\Vcs;
|
namespace Composer\Repository\Vcs;
|
||||||
|
|
||||||
use Composer\Cache;
|
|
||||||
use Composer\Config;
|
use Composer\Config;
|
||||||
use Composer\Json\JsonFile;
|
use Composer\Json\JsonFile;
|
||||||
use Composer\IO\IOInterface;
|
use Composer\IO\IOInterface;
|
||||||
|
@ -20,27 +19,8 @@ use Composer\IO\IOInterface;
|
||||||
/**
|
/**
|
||||||
* @author Per Bernhardt <plb@webfactory.de>
|
* @author Per Bernhardt <plb@webfactory.de>
|
||||||
*/
|
*/
|
||||||
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}
|
* {@inheritDoc}
|
||||||
|
@ -53,6 +33,7 @@ class HgBitbucketDriver extends VcsDriver
|
||||||
if (array() === $repoData || !isset($repoData['tip'])) {
|
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');
|
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'];
|
$this->rootIdentifier = $repoData['tip']['raw_node'];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -85,46 +66,6 @@ class HgBitbucketDriver extends VcsDriver
|
||||||
return array('type' => 'zip', 'url' => $url, 'reference' => $identifier, 'shasum' => '');
|
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}
|
* {@inheritDoc}
|
||||||
*/
|
*/
|
||||||
|
@ -177,4 +118,24 @@ class HgBitbucketDriver extends VcsDriver
|
||||||
|
|
||||||
return true;
|
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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,6 +17,7 @@ use Composer\Json\JsonFile;
|
||||||
use Composer\Util\ProcessExecutor;
|
use Composer\Util\ProcessExecutor;
|
||||||
use Composer\Util\Filesystem;
|
use Composer\Util\Filesystem;
|
||||||
use Composer\IO\IOInterface;
|
use Composer\IO\IOInterface;
|
||||||
|
use Symfony\Component\Process\Process;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Per Bernhardt <plb@webfactory.de>
|
* @author Per Bernhardt <plb@webfactory.de>
|
||||||
|
@ -114,28 +115,34 @@ class HgDriver extends VcsDriver
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* {@inheritDoc}
|
* {@inheritdoc}
|
||||||
*/
|
*/
|
||||||
public function getComposerInformation($identifier)
|
public function getFileContent($file, $identifier)
|
||||||
{
|
{
|
||||||
if (!isset($this->infoCache[$identifier])) {
|
$resource = sprintf('hg cat -r %s %s', ProcessExecutor::escape($identifier), ProcessExecutor::escape($file));
|
||||||
$this->process->execute(sprintf('hg cat -r %s composer.json', ProcessExecutor::escape($identifier)), $composer, $this->repoDir);
|
$this->process->execute(sprintf('hg cat -r %s', $resource), $content, $this->repoDir);
|
||||||
|
|
||||||
if (!trim($composer)) {
|
if (!trim($content)) {
|
||||||
return;
|
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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'));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -24,10 +24,8 @@ class PerforceDriver extends VcsDriver
|
||||||
{
|
{
|
||||||
protected $depot;
|
protected $depot;
|
||||||
protected $branch;
|
protected $branch;
|
||||||
|
/** @var Perforce */
|
||||||
protected $perforce;
|
protected $perforce;
|
||||||
protected $composerInfo;
|
|
||||||
protected $composerInfoIdentifier;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* {@inheritDoc}
|
* {@inheritDoc}
|
||||||
*/
|
*/
|
||||||
|
@ -59,19 +57,21 @@ class PerforceDriver extends VcsDriver
|
||||||
$this->perforce = Perforce::create($repoConfig, $this->getUrl(), $repoDir, $this->process, $this->io);
|
$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)
|
public function hasComposerFile($identifier)
|
||||||
{
|
{
|
||||||
$this->composerInfo = $this->perforce->getComposerInformation('//' . $this->depot . '/' . $identifier);
|
$composerInfo = $this->perforce->getComposerInformation('//' . $this->depot . '/' . $identifier);
|
||||||
$this->composerInfoIdentifier = $identifier;
|
$composerInfoIdentifier = $identifier;
|
||||||
|
|
||||||
return !empty($this->composerInfo);
|
return !empty($composerInfo);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -116,56 +116,82 @@ class SvnDriver extends VcsDriver
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* {@inheritDoc}
|
* {@inheritdoc}
|
||||||
*/
|
*/
|
||||||
public function getComposerInformation($identifier)
|
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])) {
|
if (!isset($this->infoCache[$identifier])) {
|
||||||
preg_match('{^(.+?)(@\d+)?/$}', $identifier, $match);
|
if ($res = $this->cache->read($identifier.'.json')) {
|
||||||
if (!empty($match[2])) {
|
return $this->infoCache[$identifier] = JsonFile::parseJson($res);
|
||||||
$path = $match[1];
|
|
||||||
$rev = $match[2];
|
|
||||||
} else {
|
|
||||||
$path = $identifier;
|
|
||||||
$rev = '';
|
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
$composer = $this->getBaseComposerInformation($identifier);
|
||||||
$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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->cache->write($identifier.'.json', json_encode($composer));
|
$this->cache->write($identifier.'.json', json_encode($composer));
|
||||||
|
|
||||||
$this->infoCache[$identifier] = $composer;
|
$this->infoCache[$identifier] = $composer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
return $this->infoCache[$identifier];
|
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}
|
* {@inheritDoc}
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -12,10 +12,12 @@
|
||||||
|
|
||||||
namespace Composer\Repository\Vcs;
|
namespace Composer\Repository\Vcs;
|
||||||
|
|
||||||
|
use Composer\Cache;
|
||||||
use Composer\Downloader\TransportException;
|
use Composer\Downloader\TransportException;
|
||||||
use Composer\Config;
|
use Composer\Config;
|
||||||
use Composer\Factory;
|
use Composer\Factory;
|
||||||
use Composer\IO\IOInterface;
|
use Composer\IO\IOInterface;
|
||||||
|
use Composer\Json\JsonFile;
|
||||||
use Composer\Util\ProcessExecutor;
|
use Composer\Util\ProcessExecutor;
|
||||||
use Composer\Util\RemoteFilesystem;
|
use Composer\Util\RemoteFilesystem;
|
||||||
use Composer\Util\Filesystem;
|
use Composer\Util\Filesystem;
|
||||||
|
@ -41,6 +43,10 @@ abstract class VcsDriver implements VcsDriverInterface
|
||||||
protected $process;
|
protected $process;
|
||||||
/** @var RemoteFilesystem */
|
/** @var RemoteFilesystem */
|
||||||
protected $remoteFilesystem;
|
protected $remoteFilesystem;
|
||||||
|
/** @var array */
|
||||||
|
protected $infoCache = array();
|
||||||
|
/** @var Cache */
|
||||||
|
protected $cache;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor.
|
* Constructor.
|
||||||
|
@ -66,6 +72,57 @@ abstract class VcsDriver implements VcsDriverInterface
|
||||||
$this->remoteFilesystem = $remoteFilesystem ?: Factory::createRemoteFilesystem($this->io, $config);
|
$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}
|
* {@inheritDoc}
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -33,6 +33,23 @@ interface VcsDriverInterface
|
||||||
*/
|
*/
|
||||||
public function getComposerInformation($identifier);
|
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 ..)
|
* Return the root identifier (trunk, master, default/tip ..)
|
||||||
*
|
*
|
||||||
|
|
|
@ -304,7 +304,9 @@ class Perforce
|
||||||
|
|
||||||
public function connectClient()
|
public function connectClient()
|
||||||
{
|
{
|
||||||
$p4CreateClientCommand = $this->generateP4Command('client -i < ' . str_replace(" ", "\\ ", $this->getP4ClientSpec()));
|
$p4CreateClientCommand = $this->generateP4Command(
|
||||||
|
'client -i < ' . str_replace(" ", "\\ ", $this->getP4ClientSpec())
|
||||||
|
);
|
||||||
$this->executeCommand($p4CreateClientCommand);
|
$this->executeCommand($p4CreateClientCommand);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -396,56 +398,55 @@ class Perforce
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getComposerInformation($identifier)
|
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, '@');
|
$index = strpos($identifier, '@');
|
||||||
if ($index === false) {
|
if ($index === false) {
|
||||||
$composerJson = $identifier. '/composer.json';
|
$path = $identifier. '/' . $file;
|
||||||
|
|
||||||
return $this->getComposerInformationFromPath($composerJson);
|
return $path;
|
||||||
}
|
} else {
|
||||||
|
$path = substr($identifier, 0, $index) . '/' . $file . substr($identifier, $index);
|
||||||
return $this->getComposerInformationFromLabel($identifier, $index);
|
$command = $this->generateP4Command(' files ' . $path, false);
|
||||||
}
|
$this->executeCommand($command);
|
||||||
|
$result = $this->commandResult;
|
||||||
public function getComposerInformationFromPath($composerJson)
|
$index2 = strpos($result, 'no such file(s).');
|
||||||
{
|
if ($index2 === false) {
|
||||||
$command = $this->generateP4Command(' print ' . $composerJson);
|
$index3 = strpos($result, 'change');
|
||||||
$this->executeCommand($command);
|
if ($index3 !== false) {
|
||||||
$result = $this->commandResult;
|
$phrase = trim(substr($result, $index3));
|
||||||
$index = strpos($result, '{');
|
$fields = explode(' ', $phrase);
|
||||||
if ($index === false) {
|
return substr($identifier, 0, $index) . '/' . $file . '@' . $fields[1];
|
||||||
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 "";
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getBranches()
|
public function getBranches()
|
||||||
|
|
Loading…
Reference in New Issue