{"action":"created","comment":{"url":"https://api.github.com/repos/composer/composer/pulls/comments/25122927","id":25122927,"diff_hunk":"@@ -0,0 +1,278 @@\n+<?php\n+\n+namespace Composer\\Repository\\Vcs;\n+\n+use Composer\\Config;\n+use Composer\\Cache;\n+use Composer\\IO\\IOInterface;\n+use Composer\\Json\\JsonFile;\n+use Composer\\Downloader\\TransportException;\n+use Composer\\Util\\RemoteFilesystem;\n+\n+/**\n+ * Driver for GitLab API, use Git driver for not local checkouts.\n+ *\n+ * @author Henrik Bjørnskov <henrik@bjrnskov.dk>\n+ * @author Jérôme Tamarelle <jerome@tamarelle.net>\n+ */\n+class GitLabDriver extends VcsDriver\n+{\n+    protected $scheme;\n+    protected $owner;\n+    protected $repository;\n+    protected $originUrl;\n+    protected $cache;\n+    protected $infoCache = array();\n+\n+    protected $project;\n+    protected $commits = array();\n+    protected $tags;\n+    protected $branches;\n+\n+    /**\n+     * Extracts information from the repository url.\n+     * SSH urls are not supported in order to know the HTTP sheme to use.\n+     *\n+     * {@inheritDoc}\n+     */\n+    public function initialize()\n+    {\n+        if (!preg_match('#^(https?)://([^/]+)/([^/]+)/([^/]+)(?:\\.git|/)?$#', $this->url, $matches)) {\n+            throw new \\InvalidArgumentException('The URL provided is invalid. It must be the HTTP URL of a GitLab project.');\n+        }\n+\n+        $this->scheme       = $match[1];\n+        $this->originUrl    = $match[2];\n+        $this->owner        = $match[3];\n+        $this->repository   = $match[4];\n+        $this->cache        = new Cache($this->io, $this->config->get('cache-repo-dir').'/'.$this->originUrl.'/'.$this->owner.'/'.$this->repository);\n+\n+        $this->fetchProject();\n+    }\n+\n+    /**\n+     * Updates the RemoteFilesystem instance.\n+     * Mainly useful for tests.\n+     *\n+     * @internal\n+     */\n+    public function setRemoteFilesystem(RemoteFilesystem $remoteFilesystem)\n+    {\n+        $this->remoteFilesystem = $remoteFilesystem;\n+    }\n+\n+    /**\n+     * Fetches the composer.json file from the project by a identifier.\n+     *\n+     * if specific keys arent present it will try and infer them by default values.\n+     *\n+     * {@inheritDoc}\n+     */\n+    public function getComposerInformation($identifier)\n+    {\n+        // Convert the root identifier to a cachable commit id\n+        if (!preg_match('{[a-f0-9]{40}}i', $identifier)) {\n+            foreach ($this->getBranches() as $ref => $id) {\n+                if ($ref === $identifier) {\n+                    $identifier = $id;\n+                }\n+            }\n+        }\n+\n+        if (isset($this->infoCache[$identifier])) {\n+            return $this->infoCache[$identifier];\n+        }\n+\n+        if (preg_match('{[a-f0-9]{40}}i', $identifier) && $res = $this->cache->read($identifier)) {\n+            return $this->infoCache[$identifier] = JsonFile::parseJson($res, $res);\n+        }\n+\n+        try {\n+            $composer = $this->fetchComposerFile($identifier);\n+        } catch (TransportException $e) {\n+            if ($e->getCode() !== 404) {\n+                throw $e;\n+            }\n+            $composer = false;\n+        }\n+\n+        if ($composer && !isset($composer['time']) && isset($this->commits[$identifier])) {\n+            $composer['time'] = $this->commits[$identifier]['committed_date'];\n+        }\n+\n+        if (preg_match('{[a-f0-9]{40}}i', $identifier)) {\n+            $this->cache->write($identifier, json_encode($composer));\n+        }\n+\n+        return $this->infoCache[$identifier] = $composer;\n+    }\n+\n+    /**\n+     * {@inheritDoc}\n+     */\n+    public function hasComposerFile($identifier)\n+    {\n+        try {\n+            $this->getComposerInformation($identifier);\n+\n+            return true;\n+        } catch (TransportException $e) {\n+            if ($e->getCode() !== 404) {\n+                throw $e;\n+            }\n+        }\n+\n+        return false;\n+    }\n+\n+    /**\n+     * {@inheritDoc}\n+     */\n+    public function getRepositoryUrl()\n+    {\n+        return $this->project['ssh_url_to_repo'];\n+    }\n+\n+    /**\n+     * {@inheritDoc}\n+     */\n+    public function getUrl()\n+    {\n+        return $this->project['web_url'];\n+    }\n+\n+    /**\n+     * {@inheritDoc}\n+     */\n+    public function getDist($identifier)\n+    {\n+        $url = $this->getApiUrl().'/repository/archive.zip?sha='.$identifier;\n+\n+        return array('type' => 'zip', 'url' => $url, 'reference' => $identifier, 'shasum' => '');\n+    }\n+\n+    /**\n+     * {@inheritDoc}\n+     */\n+    public function getSource($identifier)\n+    {\n+        return array('type' => 'git', 'url' => $this->getRepositoryUrl(), 'reference' => $identifier);\n+    }\n+\n+    /**\n+     * {@inheritDoc}\n+     */\n+    public function getRootIdentifier()\n+    {\n+        return $this->project['default_branch'];\n+    }\n+\n+    /**\n+     * {@inheritDoc}\n+     */\n+    public function getBranches()\n+    {\n+        if (!$this->branches) {\n+            $this->branches = $this->getReferences('branches');\n+        }\n+\n+        return $this->branches;\n+    }\n+\n+    /**\n+     * {@inheritDoc}\n+     */\n+    public function getTags()\n+    {\n+        if (!$this->tags) {\n+            $this->tags = $this->getReferences('tags');\n+        }\n+\n+        return $this->tags;\n+    }\n+\n+    /**\n+     * Fetches composer.json file from the repository through api\n+     *\n+     * @param string $identifier\n+     * @return array\n+     */\n+    protected function fetchComposerFile($identifier)\n+    {\n+        $resource = $this->getApiUrl() . '/repository/blobs/'.$identifier.'?filepath=composer.json';\n+\n+        return JsonFile::parseJson($this->getContents($resource), $resource);\n+    }\n+\n+    /**\n+     * Root url\n+     *\n+     * {@inheritDoc}\n+     */\n+    public function getApiUrl()\n+    {\n+        // this needs to be https, but our install is running http","path":"src/Composer/Repository/Vcs/GitLabDriver.php","position":null,"original_position":214,"commit_id":"73e8a6ab360978d553514c800cb8652d29074774","original_commit_id":"7ac9929c00c463cadbe0625a2ffcdde7da582aee","user":{"login":"GromNaN","id":400034,"avatar_url":"https://avatars.githubusercontent.com/u/400034?v=3","gravatar_id":"","url":"https://api.github.com/users/GromNaN","html_url":"https://github.com/GromNaN","followers_url":"https://api.github.com/users/GromNaN/followers","following_url":"https://api.github.com/users/GromNaN/following{/other_user}","gists_url":"https://api.github.com/users/GromNaN/gists{/gist_id}","starred_url":"https://api.github.com/users/GromNaN/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/GromNaN/subscriptions","organizations_url":"https://api.github.com/users/GromNaN/orgs","repos_url":"https://api.github.com/users/GromNaN/repos","events_url":"https://api.github.com/users/GromNaN/events{/privacy}","received_events_url":"https://api.github.com/users/GromNaN/received_events","type":"User","site_admin":false},"body":"Indeed. I've removed this relic and fixed CS.","created_at":"2015-02-21T15:38:59Z","updated_at":"2015-02-21T15:38:59Z","html_url":"https://github.com/composer/composer/pull/3765#discussion_r25122927","pull_request_url":"https://api.github.com/repos/composer/composer/pulls/3765","_links":{"self":{"href":"https://api.github.com/repos/composer/composer/pulls/comments/25122927"},"html":{"href":"https://github.com/composer/composer/pull/3765#discussion_r25122927"},"pull_request":{"href":"https://api.github.com/repos/composer/composer/pulls/3765"}}},"pull_request":{"url":"https://api.github.com/repos/composer/composer/pulls/3765","id":29742708,"html_url":"https://github.com/composer/composer/pull/3765","diff_url":"https://github.com/composer/composer/pull/3765.diff","patch_url":"https://github.com/composer/composer/pull/3765.patch","issue_url":"https://api.github.com/repos/composer/composer/issues/3765","number":3765,"state":"open","locked":false,"title":"Introduce GitLab Driver","user":{"login":"GromNaN","id":400034,"avatar_url":"https://avatars.githubusercontent.com/u/400034?v=3","gravatar_id":"","url":"https://api.github.com/users/GromNaN","html_url":"https://github.com/GromNaN","followers_url":"https://api.github.com/users/GromNaN/followers","following_url":"https://api.github.com/users/GromNaN/following{/other_user}","gists_url":"https://api.github.com/users/GromNaN/gists{/gist_id}","starred_url":"https://api.github.com/users/GromNaN/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/GromNaN/subscriptions","organizations_url":"https://api.github.com/users/GromNaN/orgs","repos_url":"https://api.github.com/users/GromNaN/repos","events_url":"https://api.github.com/users/GromNaN/events{/privacy}","received_events_url":"https://api.github.com/users/GromNaN/received_events","type":"User","site_admin":false},"body":"Following the work of @henrikbjorn on https://github.com/composer/composer/pull/3369 to build a GitLab repository driver.\r\n\r\nI've tested with a simple project like the following, and with Satis instance with 150 large repositories. The performance impact is rather huge! \r\n\r\n```json\r\n# composer.json\r\n{\r\n    \"repositories\": [\r\n        { \"type\": \"gitlab\", \"url\": \"http://gitlab.example.com/mirror/symfony_http_foundation\" }\r\n    ],\r\n    \"config\": {\r\n        \"gitlab-domains\": [\"gitlab.example.com\"]\r\n    },\r\n    \"require\": {\r\n        \"symfony/http-foundation\": \"2.6.*\"\r\n    }\r\n}\r\n```\r\n\r\n* The config `gitlab-domains` is required for custom GitLab servers. It is set to [**gitlab.com**](https://about.gitlab.com/gitlab-com/) by default for people using this host.\r\n* The repository type \"gitlab\" is optional here. If the the type is `vcs`, the repository is properly detected based on the gitlab domains configuration.\r\n* The authentication token is asked interactively and stored in `auth.json`. This token is required to scan the repository and to download archives.\r\n\r\n```\r\n# ~/.composer/auth.json\r\n{\r\n    \"gitlab-tokens\": {\r\n        \"gitlab.example.com\": \"abcdjefghijklmnopqrstuvwxyz\"\r\n    }\r\n}\r\n```\r\n\r\n* It makes a real boost in terms of repository scanning as it makes only 3 API calls when everything is already in the cache : one for the project infos, and 2 others to list branches and tags. All the `composer.json` files are stored in the cache.\r\n\r\n**Side note:** I've used [Prophecy](https://github.com/phpspec/prophecy) for unit tests, [it comes out-of-the box with PHPUnit 4.5](http://thephp.cc/news/2015/02/phpunit-4-5-and-prophecy). But that imposes to upgrade the required version of `phpunit/phpunit`. https://github.com/composer/composer/pull/3766","created_at":"2015-02-21T00:09:14Z","updated_at":"2015-02-21T15:38:59Z","closed_at":null,"merged_at":null,"merge_commit_sha":"f12cc0a96d1a15de0cd46ed93f82d066e3680c74","assignee":null,"milestone":null,"commits_url":"https://api.github.com/repos/composer/composer/pulls/3765/commits","review_comments_url":"https://api.github.com/repos/composer/composer/pulls/3765/comments","review_comment_url":"https://api.github.com/repos/composer/composer/pulls/comments/{number}","comments_url":"https://api.github.com/repos/composer/composer/issues/3765/comments","statuses_url":"https://api.github.com/repos/composer/composer/statuses/73e8a6ab360978d553514c800cb8652d29074774","head":{"label":"GromNaN:gitlab","ref":"gitlab","sha":"73e8a6ab360978d553514c800cb8652d29074774","user":{"login":"GromNaN","id":400034,"avatar_url":"https://avatars.githubusercontent.com/u/400034?v=3","gravatar_id":"","url":"https://api.github.com/users/GromNaN","html_url":"https://github.com/GromNaN","followers_url":"https://api.github.com/users/GromNaN/followers","following_url":"https://api.github.com/users/GromNaN/following{/other_user}","gists_url":"https://api.github.com/users/GromNaN/gists{/gist_id}","starred_url":"https://api.github.com/users/GromNaN/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/GromNaN/subscriptions","organizations_url":"https://api.github.com/users/GromNaN/orgs","repos_url":"https://api.github.com/users/GromNaN/repos","events_url":"https://api.github.com/users/GromNaN/events{/privacy}","received_events_url":"https://api.github.com/users/GromNaN/received_events","type":"User","site_admin":false},"repo":{"id":4100245,"name":"composer","full_name":"GromNaN/composer","owner":{"login":"GromNaN","id":400034,"avatar_url":"https://avatars.githubusercontent.com/u/400034?v=3","gravatar_id":"","url":"https://api.github.com/users/GromNaN","html_url":"https://github.com/GromNaN","followers_url":"https://api.github.com/users/GromNaN/followers","following_url":"https://api.github.com/users/GromNaN/following{/other_user}","gists_url":"https://api.github.com/users/GromNaN/gists{/gist_id}","starred_url":"https://api.github.com/users/GromNaN/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/GromNaN/subscriptions","organizations_url":"https://api.github.com/users/GromNaN/orgs","repos_url":"https://api.github.com/users/GromNaN/repos","events_url":"https://api.github.com/users/GromNaN/events{/privacy}","received_events_url":"https://api.github.com/users/GromNaN/received_events","type":"User","site_admin":false},"private":false,"html_url":"https://github.com/GromNaN/composer","description":"Dependency Manager for PHP","fork":true,"url":"https://api.github.com/repos/GromNaN/composer","forks_url":"https://api.github.com/repos/GromNaN/composer/forks","keys_url":"https://api.github.com/repos/GromNaN/composer/keys{/key_id}","collaborators_url":"https://api.github.com/repos/GromNaN/composer/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/GromNaN/composer/teams","hooks_url":"https://api.github.com/repos/GromNaN/composer/hooks","issue_events_url":"https://api.github.com/repos/GromNaN/composer/issues/events{/number}","events_url":"https://api.github.com/repos/GromNaN/composer/events","assignees_url":"https://api.github.com/repos/GromNaN/composer/assignees{/user}","branches_url":"https://api.github.com/repos/GromNaN/composer/branches{/branch}","tags_url":"https://api.github.com/repos/GromNaN/composer/tags","blobs_url":"https://api.github.com/repos/GromNaN/composer/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/GromNaN/composer/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/GromNaN/composer/git/refs{/sha}","trees_url":"https://api.github.com/repos/GromNaN/composer/git/trees{/sha}","statuses_url":"https://api.github.com/repos/GromNaN/composer/statuses/{sha}","languages_url":"https://api.github.com/repos/GromNaN/composer/languages","stargazers_url":"https://api.github.com/repos/GromNaN/composer/stargazers","contributors_url":"https://api.github.com/repos/GromNaN/composer/contributors","subscribers_url":"https://api.github.com/repos/GromNaN/composer/subscribers","subscription_url":"https://api.github.com/repos/GromNaN/composer/subscription","commits_url":"https://api.github.com/repos/GromNaN/composer/commits{/sha}","git_commits_url":"https://api.github.com/repos/GromNaN/composer/git/commits{/sha}","comments_url":"https://api.github.com/repos/GromNaN/composer/comments{/number}","issue_comment_url":"https://api.github.com/repos/GromNaN/composer/issues/comments/{number}","contents_url":"https://api.github.com/repos/GromNaN/composer/contents/{+path}","compare_url":"https://api.github.com/repos/GromNaN/composer/compare/{base}...{head}","merges_url":"https://api.github.com/repos/GromNaN/composer/merges","archive_url":"https://api.github.com/repos/GromNaN/composer/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/GromNaN/composer/downloads","issues_url":"https://api.github.com/repos/GromNaN/composer/issues{/number}","pulls_url":"https://api.github.com/repos/GromNaN/composer/pulls{/number}","milestones_url":"https://api.github.com/repos/GromNaN/composer/milestones{/number}","notifications_url":"https://api.github.com/repos/GromNaN/composer/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/GromNaN/composer/labels{/name}","releases_url":"https://api.github.com/repos/GromNaN/composer/releases{/id}","created_at":"2012-04-21T23:08:34Z","updated_at":"2013-12-01T21:21:41Z","pushed_at":"2015-02-21T15:35:35Z","git_url":"git://github.com/GromNaN/composer.git","ssh_url":"git@github.com:GromNaN/composer.git","clone_url":"https://github.com/GromNaN/composer.git","svn_url":"https://github.com/GromNaN/composer","homepage":"http://getcomposer.org/","size":10521,"stargazers_count":1,"watchers_count":1,"language":"PHP","has_issues":false,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":0,"mirror_url":null,"open_issues_count":0,"forks":0,"open_issues":0,"watchers":1,"default_branch":"master"}},"base":{"label":"composer:master","ref":"master","sha":"833ce984264204e7d6576ab082660105c7d8f04c","user":{"login":"composer","id":837015,"avatar_url":"https://avatars.githubusercontent.com/u/837015?v=3","gravatar_id":"","url":"https://api.github.com/users/composer","html_url":"https://github.com/composer","followers_url":"https://api.github.com/users/composer/followers","following_url":"https://api.github.com/users/composer/following{/other_user}","gists_url":"https://api.github.com/users/composer/gists{/gist_id}","starred_url":"https://api.github.com/users/composer/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/composer/subscriptions","organizations_url":"https://api.github.com/users/composer/orgs","repos_url":"https://api.github.com/users/composer/repos","events_url":"https://api.github.com/users/composer/events{/privacy}","received_events_url":"https://api.github.com/users/composer/received_events","type":"Organization","site_admin":false},"repo":{"id":1864363,"name":"composer","full_name":"composer/composer","owner":{"login":"composer","id":837015,"avatar_url":"https://avatars.githubusercontent.com/u/837015?v=3","gravatar_id":"","url":"https://api.github.com/users/composer","html_url":"https://github.com/composer","followers_url":"https://api.github.com/users/composer/followers","following_url":"https://api.github.com/users/composer/following{/other_user}","gists_url":"https://api.github.com/users/composer/gists{/gist_id}","starred_url":"https://api.github.com/users/composer/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/composer/subscriptions","organizations_url":"https://api.github.com/users/composer/orgs","repos_url":"https://api.github.com/users/composer/repos","events_url":"https://api.github.com/users/composer/events{/privacy}","received_events_url":"https://api.github.com/users/composer/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/composer/composer","description":"Dependency Manager for PHP","fork":false,"url":"https://api.github.com/repos/composer/composer","forks_url":"https://api.github.com/repos/composer/composer/forks","keys_url":"https://api.github.com/repos/composer/composer/keys{/key_id}","collaborators_url":"https://api.github.com/repos/composer/composer/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/composer/composer/teams","hooks_url":"https://api.github.com/repos/composer/composer/hooks","issue_events_url":"https://api.github.com/repos/composer/composer/issues/events{/number}","events_url":"https://api.github.com/repos/composer/composer/events","assignees_url":"https://api.github.com/repos/composer/composer/assignees{/user}","branches_url":"https://api.github.com/repos/composer/composer/branches{/branch}","tags_url":"https://api.github.com/repos/composer/composer/tags","blobs_url":"https://api.github.com/repos/composer/composer/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/composer/composer/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/composer/composer/git/refs{/sha}","trees_url":"https://api.github.com/repos/composer/composer/git/trees{/sha}","statuses_url":"https://api.github.com/repos/composer/composer/statuses/{sha}","languages_url":"https://api.github.com/repos/composer/composer/languages","stargazers_url":"https://api.github.com/repos/composer/composer/stargazers","contributors_url":"https://api.github.com/repos/composer/composer/contributors","subscribers_url":"https://api.github.com/repos/composer/composer/subscribers","subscription_url":"https://api.github.com/repos/composer/composer/subscription","commits_url":"https://api.github.com/repos/composer/composer/commits{/sha}","git_commits_url":"https://api.github.com/repos/composer/composer/git/commits{/sha}","comments_url":"https://api.github.com/repos/composer/composer/comments{/number}","issue_comment_url":"https://api.github.com/repos/composer/composer/issues/comments/{number}","contents_url":"https://api.github.com/repos/composer/composer/contents/{+path}","compare_url":"https://api.github.com/repos/composer/composer/compare/{base}...{head}","merges_url":"https://api.github.com/repos/composer/composer/merges","archive_url":"https://api.github.com/repos/composer/composer/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/composer/composer/downloads","issues_url":"https://api.github.com/repos/composer/composer/issues{/number}","pulls_url":"https://api.github.com/repos/composer/composer/pulls{/number}","milestones_url":"https://api.github.com/repos/composer/composer/milestones{/number}","notifications_url":"https://api.github.com/repos/composer/composer/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/composer/composer/labels{/name}","releases_url":"https://api.github.com/repos/composer/composer/releases{/id}","created_at":"2011-06-08T08:53:00Z","updated_at":"2015-02-21T15:17:44Z","pushed_at":"2015-02-17T21:55:44Z","git_url":"git://github.com/composer/composer.git","ssh_url":"git@github.com:composer/composer.git","clone_url":"https://github.com/composer/composer.git","svn_url":"https://github.com/composer/composer","homepage":"http://getcomposer.org/","size":24346,"stargazers_count":5310,"watchers_count":5310,"language":"PHP","has_issues":true,"has_downloads":true,"has_wiki":false,"has_pages":false,"forks_count":1409,"mirror_url":null,"open_issues_count":534,"forks":1409,"open_issues":534,"watchers":5310,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/composer/composer/pulls/3765"},"html":{"href":"https://github.com/composer/composer/pull/3765"},"issue":{"href":"https://api.github.com/repos/composer/composer/issues/3765"},"comments":{"href":"https://api.github.com/repos/composer/composer/issues/3765/comments"},"review_comments":{"href":"https://api.github.com/repos/composer/composer/pulls/3765/comments"},"review_comment":{"href":"https://api.github.com/repos/composer/composer/pulls/comments/{number}"},"commits":{"href":"https://api.github.com/repos/composer/composer/pulls/3765/commits"},"statuses":{"href":"https://api.github.com/repos/composer/composer/statuses/73e8a6ab360978d553514c800cb8652d29074774"}}}}