From e6114b2ca7b2eb75920fd03957070045a1ac1bc1 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Fri, 5 Jan 2018 15:20:30 +0100 Subject: [PATCH] Fix support for replacing dist refs in gitlab URLs and add support for gitlab/github enterprise too --- src/Composer/Downloader/ArchiveDownloader.php | 26 -------- src/Composer/Downloader/FileDownloader.php | 5 ++ src/Composer/Util/Url.php | 56 +++++++++++++++++ tests/Composer/Test/Util/UrlTest.php | 60 +++++++++++++++++++ 4 files changed, 121 insertions(+), 26 deletions(-) create mode 100644 src/Composer/Util/Url.php create mode 100644 tests/Composer/Test/Util/UrlTest.php diff --git a/src/Composer/Downloader/ArchiveDownloader.php b/src/Composer/Downloader/ArchiveDownloader.php index 1b93724ba..24256c81f 100644 --- a/src/Composer/Downloader/ArchiveDownloader.php +++ b/src/Composer/Downloader/ArchiveDownloader.php @@ -101,32 +101,6 @@ abstract class ArchiveDownloader extends FileDownloader return rtrim($path.'/'.md5($path.spl_object_hash($package)).'.'.pathinfo(parse_url($package->getDistUrl(), PHP_URL_PATH), PATHINFO_EXTENSION), '.'); } - /** - * {@inheritdoc} - */ - protected function processUrl(PackageInterface $package, $url) - { - if ($package->getDistReference() && strpos($url, 'github.com')) { - if (preg_match('{^https?://(?:www\.)?github\.com/([^/]+)/([^/]+)/(zip|tar)ball/(.+)$}i', $url, $match)) { - // update legacy github archives to API calls with the proper reference - $url = 'https://api.github.com/repos/' . $match[1] . '/'. $match[2] . '/' . $match[3] . 'ball/' . $package->getDistReference(); - } elseif ($package->getDistReference() && preg_match('{^https?://(?:www\.)?github\.com/([^/]+)/([^/]+)/archive/.+\.(zip|tar)(?:\.gz)?$}i', $url, $match)) { - // update current github web archives to API calls with the proper reference - $url = 'https://api.github.com/repos/' . $match[1] . '/'. $match[2] . '/' . $match[3] . 'ball/' . $package->getDistReference(); - } elseif ($package->getDistReference() && preg_match('{^https?://api\.github\.com/repos/([^/]+)/([^/]+)/(zip|tar)ball(?:/.+)?$}i', $url, $match)) { - // update api archives to the proper reference - $url = 'https://api.github.com/repos/' . $match[1] . '/'. $match[2] . '/' . $match[3] . 'ball/' . $package->getDistReference(); - } - } elseif ($package->getDistReference() && strpos($url, 'bitbucket.org')) { - if (preg_match('{^https?://(?:www\.)?bitbucket\.org/([^/]+)/([^/]+)/get/(.+)\.(zip|tar\.gz|tar\.bz2)$}i', $url, $match)) { - // update Bitbucket archives to the proper reference - $url = 'https://bitbucket.org/' . $match[1] . '/'. $match[2] . '/get/' . $package->getDistReference() . '.' . $match[4]; - } - } - - return parent::processUrl($package, $url); - } - /** * Extract file to directory * diff --git a/src/Composer/Downloader/FileDownloader.php b/src/Composer/Downloader/FileDownloader.php index 8bdf0a519..0cc531704 100644 --- a/src/Composer/Downloader/FileDownloader.php +++ b/src/Composer/Downloader/FileDownloader.php @@ -22,6 +22,7 @@ use Composer\Plugin\PreFileDownloadEvent; use Composer\EventDispatcher\EventDispatcher; use Composer\Util\Filesystem; use Composer\Util\RemoteFilesystem; +use Composer\Util\Url as UrlUtil; /** * Base downloader for files @@ -260,6 +261,10 @@ class FileDownloader implements DownloaderInterface throw new \RuntimeException('You must enable the openssl extension to download files via https'); } + if ($package->getDistReference()) { + $url = UrlUtil::updateDistReference($this->config, $url, $package->getDistReference()); + } + return $url; } diff --git a/src/Composer/Util/Url.php b/src/Composer/Util/Url.php new file mode 100644 index 000000000..2fe68c3f1 --- /dev/null +++ b/src/Composer/Util/Url.php @@ -0,0 +1,56 @@ + + * Jordi Boggiano + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Composer\Util; + +use Composer\Config; +use Composer\IO\IOInterface; + +/** + * @author Jordi Boggiano + */ +class Url +{ + public static function updateDistReference(Config $config, $url, $ref) + { + $host = parse_url($url, PHP_URL_HOST); + + if ($host === 'api.github.com' || $host === 'github.com' || $host === 'www.github.com') { + if (preg_match('{^https?://(?:www\.)?github\.com/([^/]+)/([^/]+)/(zip|tar)ball/(.+)$}i', $url, $match)) { + // update legacy github archives to API calls with the proper reference + $url = 'https://api.github.com/repos/' . $match[1] . '/'. $match[2] . '/' . $match[3] . 'ball/' . $ref; + } elseif (preg_match('{^https?://(?:www\.)?github\.com/([^/]+)/([^/]+)/archive/.+\.(zip|tar)(?:\.gz)?$}i', $url, $match)) { + // update current github web archives to API calls with the proper reference + $url = 'https://api.github.com/repos/' . $match[1] . '/'. $match[2] . '/' . $match[3] . 'ball/' . $ref; + } elseif (preg_match('{^https?://api\.github\.com/repos/([^/]+)/([^/]+)/(zip|tar)ball(?:/.+)?$}i', $url, $match)) { + // update api archives to the proper reference + $url = 'https://api.github.com/repos/' . $match[1] . '/'. $match[2] . '/' . $match[3] . 'ball/' . $ref; + } + } elseif ($host === 'bitbucket.org' || $host === 'www.bitbucket.org') { + if (preg_match('{^https?://(?:www\.)?bitbucket\.org/([^/]+)/([^/]+)/get/(.+)\.(zip|tar\.gz|tar\.bz2)$}i', $url, $match)) { + // update Bitbucket archives to the proper reference + $url = 'https://bitbucket.org/' . $match[1] . '/'. $match[2] . '/get/' . $ref . '.' . $match[4]; + } + } elseif ($host === 'gitlab.com' || $host === 'www.gitlab.com') { + if (preg_match('{^https?://(?:www\.)?gitlab\.com/api/v[34]/projects/([^/]+)/repository/archive\.(zip|tar\.gz|tar\.bz2|tar)\?sha=.+$}i', $url, $match)) { + // update Gitlab archives to the proper reference + $url = 'https://gitlab.com/api/v4/projects/' . $match[1] . '/repository/archive.' . $match[2] . '?sha=' . $ref; + } + } elseif (in_array($host, $config->get('github-domains'), true)) { + $url = preg_replace('{(/repos/[^/]+/[^/]+/(zip|tar)ball)(?:/.+)?$}i', '$1/'.$ref, $url); + } elseif (in_array($host, $config->get('gitlab-domains'), true)) { + $url = preg_replace('{(/api/v[34]/projects/[^/]+/repository/archive\.(?:zip|tar\.gz|tar\.bz2|tar)\?sha=).+$}i', '$1'.$ref, $url); + } + + return $url; + } +} diff --git a/tests/Composer/Test/Util/UrlTest.php b/tests/Composer/Test/Util/UrlTest.php new file mode 100644 index 000000000..1a4ccda3b --- /dev/null +++ b/tests/Composer/Test/Util/UrlTest.php @@ -0,0 +1,60 @@ + + * Jordi Boggiano + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Composer\Test\Util; + +use Composer\Util\Url; +use PHPUnit\Framework\TestCase; +use Composer\Config; + +class UrlTest extends TestCase +{ + /** + * @dataProvider distRefsProvider + */ + public function testUpdateDistReference($url, $expectedUrl, $conf = array()) + { + $config = new Config(); + $config->merge(array('config' => $conf)); + + $this->assertSame($expectedUrl, Url::updateDistReference($config, $url, 'newref')); + } + + public static function distRefsProvider() + { + return array( + // github + array('https://github.com/foo/bar/zipball/abcd', 'https://api.github.com/repos/foo/bar/zipball/newref'), + array('https://www.github.com/foo/bar/zipball/abcd', 'https://api.github.com/repos/foo/bar/zipball/newref'), + array('https://github.com/foo/bar/archive/abcd.zip', 'https://api.github.com/repos/foo/bar/zipball/newref'), + array('https://github.com/foo/bar/archive/abcd.tar.gz', 'https://api.github.com/repos/foo/bar/tarball/newref'), + array('https://api.github.com/repos/foo/bar/tarball', 'https://api.github.com/repos/foo/bar/tarball/newref'), + array('https://api.github.com/repos/foo/bar/tarball/abcd', 'https://api.github.com/repos/foo/bar/tarball/newref'), + + // github enterprise + array('https://mygithub.com/api/v3/repos/foo/bar/tarball/abcd', 'https://mygithub.com/api/v3/repos/foo/bar/tarball/newref', array('github-domains' => array('mygithub.com'))), + + // bitbucket + array('https://bitbucket.org/foo/bar/get/abcd.zip', 'https://bitbucket.org/foo/bar/get/newref.zip'), + array('https://www.bitbucket.org/foo/bar/get/abcd.tar.bz2', 'https://bitbucket.org/foo/bar/get/newref.tar.bz2'), + + // gitlab + array('https://gitlab.com/api/v4/projects/foo%2Fbar/repository/archive.zip?sha=abcd', 'https://gitlab.com/api/v4/projects/foo%2Fbar/repository/archive.zip?sha=newref'), + array('https://www.gitlab.com/api/v4/projects/foo%2Fbar/repository/archive.zip?sha=abcd', 'https://gitlab.com/api/v4/projects/foo%2Fbar/repository/archive.zip?sha=newref'), + array('https://gitlab.com/api/v3/projects/foo%2Fbar/repository/archive.tar.gz?sha=abcd', 'https://gitlab.com/api/v4/projects/foo%2Fbar/repository/archive.tar.gz?sha=newref'), + + // gitlab enterprise + array('https://mygitlab.com/api/v4/projects/foo%2Fbar/repository/archive.tar.gz?sha=abcd', 'https://mygitlab.com/api/v4/projects/foo%2Fbar/repository/archive.tar.gz?sha=newref', array('gitlab-domains' => array('mygitlab.com'))), + array('https://mygitlab.com/api/v3/projects/foo%2Fbar/repository/archive.tar.bz2?sha=abcd', 'https://mygitlab.com/api/v3/projects/foo%2Fbar/repository/archive.tar.bz2?sha=newref', array('gitlab-domains' => array('mygitlab.com'))), + ); + } +}