support for gitlab subgroups, closes #6349
parent
e3a23c0047
commit
e2eb8f2201
|
@ -29,7 +29,7 @@ use Composer\Util\GitLab;
|
||||||
class GitLabDriver extends VcsDriver
|
class GitLabDriver extends VcsDriver
|
||||||
{
|
{
|
||||||
private $scheme;
|
private $scheme;
|
||||||
private $owner;
|
private $namespace;
|
||||||
private $repository;
|
private $repository;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -66,7 +66,7 @@ class GitLabDriver extends VcsDriver
|
||||||
*/
|
*/
|
||||||
private $isPrivate = true;
|
private $isPrivate = true;
|
||||||
|
|
||||||
const URL_REGEX = '#^(?:(?P<scheme>https?)://(?P<domain>.+?)/|git@(?P<domain2>[^:]+):)(?P<owner>[^/]+)/(?P<repo>[^/]+?)(?:\.git|/)?$#';
|
const URL_REGEX = '#^(?:(?P<scheme>https?)://(?P<domain>.+?)/|git@(?P<domain2>[^:]+):)(?P<parts>.+)/(?P<repo>[^/]+?)(?:\.git|/)?$#';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Extracts information from the repository url.
|
* Extracts information from the repository url.
|
||||||
|
@ -81,12 +81,19 @@ class GitLabDriver extends VcsDriver
|
||||||
throw new \InvalidArgumentException('The URL provided is invalid. It must be the HTTP URL of a GitLab project.');
|
throw new \InvalidArgumentException('The URL provided is invalid. It must be the HTTP URL of a GitLab project.');
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->scheme = !empty($match['scheme']) ? $match['scheme'] : (isset($this->repoConfig['secure-http']) && $this->repoConfig['secure-http'] === false ? 'http' : 'https');
|
$guessedDomain = !empty($match['domain']) ? $match['domain'] : $match['domain2'];
|
||||||
$this->originUrl = !empty($match['domain']) ? $match['domain'] : $match['domain2'];
|
$configuredDomains = $this->config->get('gitlab-domains');
|
||||||
$this->owner = $match['owner'];
|
$urlParts = explode('/', $match['parts']);
|
||||||
|
|
||||||
|
$this->scheme = !empty($match['scheme'])
|
||||||
|
? $match['scheme']
|
||||||
|
: (isset($this->repoConfig['secure-http']) && $this->repoConfig['secure-http'] === false ? 'http' : 'https')
|
||||||
|
;
|
||||||
|
$this->originUrl = $this->determineOrigin($configuredDomains, $guessedDomain, $urlParts);
|
||||||
|
$this->namespace = implode('/', $urlParts);
|
||||||
$this->repository = preg_replace('#(\.git)$#', '', $match['repo']);
|
$this->repository = preg_replace('#(\.git)$#', '', $match['repo']);
|
||||||
|
|
||||||
$this->cache = new Cache($this->io, $this->config->get('cache-repo-dir').'/'.$this->originUrl.'/'.$this->owner.'/'.$this->repository);
|
$this->cache = new Cache($this->io, $this->config->get('cache-repo-dir').'/'.$this->originUrl.'/'.$this->namespace.'/'.$this->repository);
|
||||||
|
|
||||||
$this->fetchProject();
|
$this->fetchProject();
|
||||||
}
|
}
|
||||||
|
@ -241,7 +248,7 @@ class GitLabDriver extends VcsDriver
|
||||||
*/
|
*/
|
||||||
public function getApiUrl()
|
public function getApiUrl()
|
||||||
{
|
{
|
||||||
return $this->scheme.'://'.$this->originUrl.'/api/v3/projects/'.$this->urlEncodeAll($this->owner).'%2F'.$this->urlEncodeAll($this->repository);
|
return $this->scheme.'://'.$this->originUrl.'/api/v3/projects/'.$this->urlEncodeAll($this->namespace).'%2F'.$this->urlEncodeAll($this->repository);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -326,12 +333,12 @@ class GitLabDriver extends VcsDriver
|
||||||
*/
|
*/
|
||||||
protected function generateSshUrl()
|
protected function generateSshUrl()
|
||||||
{
|
{
|
||||||
return 'git@' . $this->originUrl . ':'.$this->owner.'/'.$this->repository.'.git';
|
return 'git@' . $this->originUrl . ':'.$this->namespace.'/'.$this->repository.'.git';
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function generatePublicUrl()
|
protected function generatePublicUrl()
|
||||||
{
|
{
|
||||||
return 'https://' . $this->originUrl . '/'.$this->owner.'/'.$this->repository.'.git';
|
return 'https://' . $this->originUrl . '/'.$this->namespace.'/'.$this->repository.'.git';
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function setupGitDriver($url)
|
protected function setupGitDriver($url)
|
||||||
|
@ -386,7 +393,7 @@ class GitLabDriver extends VcsDriver
|
||||||
if (!$this->io->isInteractive()) {
|
if (!$this->io->isInteractive()) {
|
||||||
return $this->attemptCloneFallback();
|
return $this->attemptCloneFallback();
|
||||||
}
|
}
|
||||||
$this->io->writeError('<warning>Failed to download ' . $this->owner . '/' . $this->repository . ':' . $e->getMessage() . '</warning>');
|
$this->io->writeError('<warning>Failed to download ' . $this->namespace . '/' . $this->repository . ':' . $e->getMessage() . '</warning>');
|
||||||
$gitLabUtil->authorizeOAuthInteractively($this->scheme, $this->originUrl, 'Your credentials are required to fetch private repository metadata (<info>'.$this->url.'</info>)');
|
$gitLabUtil->authorizeOAuthInteractively($this->scheme, $this->originUrl, 'Your credentials are required to fetch private repository metadata (<info>'.$this->url.'</info>)');
|
||||||
|
|
||||||
return parent::getContents($url);
|
return parent::getContents($url);
|
||||||
|
@ -421,9 +428,10 @@ class GitLabDriver extends VcsDriver
|
||||||
}
|
}
|
||||||
|
|
||||||
$scheme = !empty($match['scheme']) ? $match['scheme'] : null;
|
$scheme = !empty($match['scheme']) ? $match['scheme'] : null;
|
||||||
$originUrl = !empty($match['domain']) ? $match['domain'] : $match['domain2'];
|
$guessedDomain = !empty($match['domain']) ? $match['domain'] : $match['domain2'];
|
||||||
|
$urlParts = explode('/', $match['parts']);
|
||||||
|
|
||||||
if (!in_array($originUrl, (array) $config->get('gitlab-domains'))) {
|
if (false === self::determineOrigin((array) $config->get('gitlab-domains'), $guessedDomain, $urlParts)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -435,4 +443,27 @@ class GitLabDriver extends VcsDriver
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param array $configuredDomains
|
||||||
|
* @param string $guessedDomain
|
||||||
|
* @param array $urlParts
|
||||||
|
* @return bool|string
|
||||||
|
*/
|
||||||
|
private static function determineOrigin(array $configuredDomains, $guessedDomain, array &$urlParts)
|
||||||
|
{
|
||||||
|
if (in_array($guessedDomain, $configuredDomains)) {
|
||||||
|
return $guessedDomain;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (null !== ($part = array_shift($urlParts))) {
|
||||||
|
$guessedDomain .= '/' . $part;
|
||||||
|
|
||||||
|
if (in_array($guessedDomain, $configuredDomains)) {
|
||||||
|
return $guessedDomain;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -35,7 +35,11 @@ class GitLabDriverTest extends TestCase
|
||||||
$this->config->merge(array(
|
$this->config->merge(array(
|
||||||
'config' => array(
|
'config' => array(
|
||||||
'home' => $this->home,
|
'home' => $this->home,
|
||||||
'gitlab-domains' => array('mycompany.com/gitlab', 'gitlab.com'),
|
'gitlab-domains' => array(
|
||||||
|
'mycompany.com/gitlab',
|
||||||
|
'othercompany.com/nested/gitlab',
|
||||||
|
'gitlab.com',
|
||||||
|
),
|
||||||
),
|
),
|
||||||
));
|
));
|
||||||
|
|
||||||
|
@ -285,6 +289,10 @@ JSON;
|
||||||
array('http://example.com/foo/bar', false),
|
array('http://example.com/foo/bar', false),
|
||||||
array('http://mycompany.com/gitlab/mygroup/myproject', true),
|
array('http://mycompany.com/gitlab/mygroup/myproject', true),
|
||||||
array('https://mycompany.com/gitlab/mygroup/myproject', extension_loaded('openssl')),
|
array('https://mycompany.com/gitlab/mygroup/myproject', extension_loaded('openssl')),
|
||||||
|
array('http://othercompany.com/nested/gitlab/mygroup/myproject', true),
|
||||||
|
array('https://othercompany.com/nested/gitlab/mygroup/myproject', extension_loaded('openssl')),
|
||||||
|
array('http://gitlab.com/mygroup/mysubgroup/mysubsubgroup/myproject', true),
|
||||||
|
array('https://gitlab.com/mygroup/mysubgroup/mysubsubgroup/myproject', extension_loaded('openssl')),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -298,14 +306,80 @@ JSON;
|
||||||
"id": 17,
|
"id": 17,
|
||||||
"default_branch": "mymaster",
|
"default_branch": "mymaster",
|
||||||
"public": false,
|
"public": false,
|
||||||
"http_url_to_repo": "https://gitlab.com/mygroup/my-pro.ject",
|
"http_url_to_repo": "https://gitlab.com/gitlab/mygroup/my-pro.ject",
|
||||||
"ssh_url_to_repo": "git@gitlab.com:mygroup/my-pro.ject.git",
|
"ssh_url_to_repo": "git@gitlab.com:mygroup/my-pro.ject.git",
|
||||||
"last_activity_at": "2014-12-01T09:17:51.000+01:00",
|
"last_activity_at": "2014-12-01T09:17:51.000+01:00",
|
||||||
"name": "My Project",
|
"name": "My Project",
|
||||||
"name_with_namespace": "My Group / My Project",
|
"name_with_namespace": "My Group / My Project",
|
||||||
"path": "myproject",
|
"path": "myproject",
|
||||||
"path_with_namespace": "mygroup/my-pro.ject",
|
"path_with_namespace": "mygroup/my-pro.ject",
|
||||||
"web_url": "https://gitlab.com/mygroup/my-pro.ject"
|
"web_url": "https://gitlab.com/gitlab/mygroup/my-pro.ject"
|
||||||
|
}
|
||||||
|
JSON;
|
||||||
|
|
||||||
|
$this->remoteFilesystem
|
||||||
|
->getContents('mycompany.com/gitlab', $apiUrl, false)
|
||||||
|
->willReturn($projectData)
|
||||||
|
->shouldBeCalledTimes(1)
|
||||||
|
;
|
||||||
|
|
||||||
|
$driver = new GitLabDriver(array('url' => $url), $this->io->reveal(), $this->config, $this->process->reveal(), $this->remoteFilesystem->reveal());
|
||||||
|
$driver->initialize();
|
||||||
|
|
||||||
|
$this->assertEquals($apiUrl, $driver->getApiUrl(), 'API URL is derived from the repository URL');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testGitlabSubGroup()
|
||||||
|
{
|
||||||
|
$url = 'https://gitlab.com/mygroup/mysubgroup/myproject';
|
||||||
|
$apiUrl = 'https://gitlab.com/api/v3/projects/mygroup%2Fmysubgroup%2Fmyproject';
|
||||||
|
|
||||||
|
$projectData = <<<JSON
|
||||||
|
{
|
||||||
|
"id": 17,
|
||||||
|
"default_branch": "mymaster",
|
||||||
|
"public": false,
|
||||||
|
"http_url_to_repo": "https://gitlab.com/mygroup/mysubgroup/my-pro.ject",
|
||||||
|
"ssh_url_to_repo": "git@gitlab.com:mygroup/mysubgroup/my-pro.ject.git",
|
||||||
|
"last_activity_at": "2014-12-01T09:17:51.000+01:00",
|
||||||
|
"name": "My Project",
|
||||||
|
"name_with_namespace": "My Group / My Project",
|
||||||
|
"path": "myproject",
|
||||||
|
"path_with_namespace": "mygroup/mysubgroup/my-pro.ject",
|
||||||
|
"web_url": "https://gitlab.com/mygroup/mysubgroup/my-pro.ject"
|
||||||
|
}
|
||||||
|
JSON;
|
||||||
|
|
||||||
|
$this->remoteFilesystem
|
||||||
|
->getContents('gitlab.com', $apiUrl, false)
|
||||||
|
->willReturn($projectData)
|
||||||
|
->shouldBeCalledTimes(1)
|
||||||
|
;
|
||||||
|
|
||||||
|
$driver = new GitLabDriver(array('url' => $url), $this->io->reveal(), $this->config, $this->process->reveal(), $this->remoteFilesystem->reveal());
|
||||||
|
$driver->initialize();
|
||||||
|
|
||||||
|
$this->assertEquals($apiUrl, $driver->getApiUrl(), 'API URL is derived from the repository URL');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testGitlabSubDirectorySubGroup()
|
||||||
|
{
|
||||||
|
$url = 'https://mycompany.com/gitlab/mygroup/mysubgroup/myproject';
|
||||||
|
$apiUrl = 'https://mycompany.com/gitlab/api/v3/projects/mygroup%2Fmysubgroup%2Fmyproject';
|
||||||
|
|
||||||
|
$projectData = <<<JSON
|
||||||
|
{
|
||||||
|
"id": 17,
|
||||||
|
"default_branch": "mymaster",
|
||||||
|
"public": false,
|
||||||
|
"http_url_to_repo": "https://mycompany.com/gitlab/mygroup/mysubgroup/my-pro.ject",
|
||||||
|
"ssh_url_to_repo": "git@mycompany.com:mygroup/mysubgroup/my-pro.ject.git",
|
||||||
|
"last_activity_at": "2014-12-01T09:17:51.000+01:00",
|
||||||
|
"name": "My Project",
|
||||||
|
"name_with_namespace": "My Group / My Project",
|
||||||
|
"path": "myproject",
|
||||||
|
"path_with_namespace": "mygroup/mysubgroup/my-pro.ject",
|
||||||
|
"web_url": "https://mycompany.com/gitlab/mygroup/mysubgroup/my-pro.ject"
|
||||||
}
|
}
|
||||||
JSON;
|
JSON;
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue