diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml deleted file mode 100644 index d071feff1..000000000 --- a/.github/FUNDING.yml +++ /dev/null @@ -1,3 +0,0 @@ -# These are supported funding model platforms - -custom: https://packagist.com diff --git a/doc/02-libraries.md b/doc/02-libraries.md index e59f505dd..c02756e50 100644 --- a/doc/02-libraries.md +++ b/doc/02-libraries.md @@ -54,7 +54,7 @@ file: ### VCS Versioning Composer uses your VCS's branch and tag features to resolve the version -constraints you specify in your `require` field to specific sets of files. +constraints you specify in your [`require`](04-schema.md#require) field to specific sets of files. When determining valid available versions, Composer looks at all of your tags and branches and translates their names into an internal list of options that it then matches against the version constraint you provided. diff --git a/src/Composer/DependencyResolver/Solver.php b/src/Composer/DependencyResolver/Solver.php index e32fe2478..873f9d778 100644 --- a/src/Composer/DependencyResolver/Solver.php +++ b/src/Composer/DependencyResolver/Solver.php @@ -615,7 +615,6 @@ class Solver $level = 1; $systemLevel = $level + 1; - $installedPos = 0; while (true) { if (1 === $level) { diff --git a/src/Composer/Package/Locker.php b/src/Composer/Package/Locker.php index 2de846359..04210bd7f 100644 --- a/src/Composer/Package/Locker.php +++ b/src/Composer/Package/Locker.php @@ -19,6 +19,7 @@ use Composer\Repository\RepositoryManager; use Composer\Util\ProcessExecutor; use Composer\Package\Dumper\ArrayDumper; use Composer\Package\Loader\ArrayLoader; +use Composer\Plugin\PluginInterface; use Composer\Util\Git as GitUtil; use Composer\IO\IOInterface; use Seld\JsonLint\ParsingException; @@ -344,6 +345,7 @@ class Locker if ($platformOverrides) { $lock['platform-overrides'] = $platformOverrides; } + $lock['plugin-api-version'] = PluginInterface::PLUGIN_API_VERSION; try { $isLocked = $this->isLocked(); diff --git a/src/Composer/Repository/Vcs/GitHubDriver.php b/src/Composer/Repository/Vcs/GitHubDriver.php index 5ead1ee8b..4aac2771f 100644 --- a/src/Composer/Repository/Vcs/GitHubDriver.php +++ b/src/Composer/Repository/Vcs/GitHubDriver.php @@ -168,7 +168,6 @@ class GitHubDriver extends VcsDriver if (!isset($composer['abandoned']) && $this->isArchived) { $composer['abandoned'] = true; } - if (!isset($composer['funding']) && $funding = $this->getFundingInfo()) { $composer['funding'] = $funding; } @@ -194,28 +193,77 @@ class GitHubDriver extends VcsDriver return $this->fundingInfo = false; } - $graphql = 'query{repository(owner:"'.$this->owner.'",name:"'.$this->repository.'"){fundingLinks{platform,url}}}'; - try { - $result = $this->httpDownloader->get('https://api.github.com/graphql', array( - 'http' => array( - 'method' => 'POST', - 'content' => json_encode(array('query' => $graphql)), - 'header' => array('Content-Type: application/json'), - ), - 'retry-auth-failure' => false, - )); - } catch (TransportException $e) { - return $this->fundingInfo = false; - } - $result = json_decode($result, true); + foreach (array($this->getApiUrl() . '/repos/'.$this->owner.'/'.$this->repository.'/contents/.github/FUNDING.yml', $this->getApiUrl() . '/repos/'.$this->owner.'/.github/contents/FUNDING.yml') as $file) { - if (empty($result['data']['repository']['fundingLinks'])) { + try { + $response = $this->httpDownloader->get($file, array( + 'retry-auth-failure' => false, + ))->decodeJson(); + } catch (TransportException $e) { + continue; + } + if (empty($response['content']) || $response['encoding'] !== 'base64' || !($funding = base64_decode($response['content']))) { + continue; + } + } + if (empty($funding)) { return $this->fundingInfo = false; } - return $this->fundingInfo = array_map(function ($link) { - return array('type' => strtolower($link['platform']), 'url' => $link['url']); - }, $result['data']['repository']['fundingLinks']); + $result = array(); + $key = null; + foreach (preg_split('{\r?\n}', $funding) as $line) { + $line = preg_replace('{#.*}', '', $line); + $line = trim($line); + if (preg_match('{^(\w+)\s*:\s*(.+)$}', $line, $match)) { + if (preg_match('{^\[.*\]$}', $match[2])) { + foreach (array_map('trim', preg_split('{[\'"]?\s*,\s*[\'"]?}', substr($match[2], 1, -1))) as $item) { + $result[] = array('type' => $match[1], 'url' => trim($item, '"\'')); + } + } else { + $result[] = array('type' => $match[1], 'url' => $match[2]); + } + $key = null; + } elseif (preg_match('{^(\w+)\s*:$}', $line, $match)) { + $key = $match[1]; + } elseif ($key && preg_match('{^-\s*(.+)$}', $line, $match)) { + $result[] = array('type' => $key, 'url' => $match[1]); + } + } + + foreach ($result as $key => $item) { + switch ($item['type']) { + case 'tidelift': + $result[$key]['url'] = 'https://tidelift.com/funding/github/' . $item['url']; + break; + case 'github': + $result[$key]['url'] = 'https://github.com/' . basename($item['url']); + break; + case 'patreon': + $result[$key]['url'] = 'https://www.patreon.com/' . basename($item['url']); + break; + case 'otechie': + $result[$key]['url'] = 'https://otechie.com/' . basename($item['url']); + break; + case 'open_collective': + $result[$key]['url'] = 'https://opencollective.com/' . basename($item['url']); + break; + case 'liberapay': + $result[$key]['url'] = 'https://liberapay.com/' . basename($item['url']); + break; + case 'ko_fi': + $result[$key]['url'] = 'https://ko-fi.com/' . basename($item['url']); + break; + case 'issuehunt': + $result[$key]['url'] = 'https://issuehunt.io/r/' . $item['url']; + break; + case 'community_bridge': + $result[$key]['url'] = 'https://funding.communitybridge.org/projects/' . basename($item['url']); + break; + } + } + + return $this->fundingInfo = $result; } /** diff --git a/tests/Composer/Test/InstallerTest.php b/tests/Composer/Test/InstallerTest.php index bb1473e71..71a955748 100644 --- a/tests/Composer/Test/InstallerTest.php +++ b/tests/Composer/Test/InstallerTest.php @@ -316,6 +316,7 @@ class InstallerTest extends TestCase unset($actualLock['hash']); unset($actualLock['content-hash']); unset($actualLock['_readme']); + unset($actualLock['plugin-api-version']); $this->assertEquals($expectLock, $actualLock); } diff --git a/tests/Composer/Test/Package/LockerTest.php b/tests/Composer/Test/Package/LockerTest.php index 05aa006f0..29f3e7c2a 100644 --- a/tests/Composer/Test/Package/LockerTest.php +++ b/tests/Composer/Test/Package/LockerTest.php @@ -13,6 +13,7 @@ namespace Composer\Test\Package; use Composer\Package\Locker; +use Composer\Plugin\PluginInterface; use Composer\IO\NullIO; use Composer\Test\TestCase; @@ -150,6 +151,7 @@ class LockerTest extends TestCase 'platform-overrides' => array('foo/bar' => '1.0'), 'prefer-stable' => false, 'prefer-lowest' => false, + 'plugin-api-version' => PluginInterface::PLUGIN_API_VERSION, )); $locker->setLockData(array($package1, $package2), array(), array(), array(), array(), 'dev', array(), false, false, array('foo/bar' => '1.0')); diff --git a/tests/Composer/Test/Repository/Vcs/GitHubDriverTest.php b/tests/Composer/Test/Repository/Vcs/GitHubDriverTest.php index 8af7b73b7..29c153d4b 100644 --- a/tests/Composer/Test/Repository/Vcs/GitHubDriverTest.php +++ b/tests/Composer/Test/Repository/Vcs/GitHubDriverTest.php @@ -191,6 +191,11 @@ class GitHubDriverTest extends TestCase ->with($this->equalTo($url = 'https://api.github.com/repos/composer/packagist/commits/feature%2F3.2-foo')) ->will($this->returnValue(new Response(array('url' => $url), 200, array(), '{"commit": {"committer":{ "date": "2012-09-10"}}}'))); + $httpDownloader->expects($this->at(3)) + ->method('get') + ->with($this->equalTo($url = 'https://api.github.com/repos/composer/packagist/contents/.github/FUNDING.yml')) + ->will($this->returnValue(new Response(array('url' => $url), 200, array(), '{"encoding": "base64", "content": "'.base64_encode("custom: https://example.com").'"}'))); + $repoConfig = array( 'url' => $repoUrl, ); @@ -257,6 +262,11 @@ class GitHubDriverTest extends TestCase ->with($this->equalTo($url = 'https://api.github.com/repos/composer/packagist/commits/'.$sha)) ->will($this->returnValue(new Response(array('url' => $url), 200, array(), '{"commit": {"committer":{ "date": "2012-09-10"}}}'))); + $httpDownloader->expects($this->at(3)) + ->method('get') + ->with($this->equalTo($url = 'https://api.github.com/repos/composer/packagist/contents/.github/FUNDING.yml')) + ->will($this->returnValue(new Response(array('url' => $url), 200, array(), '{"encoding": "base64", "content": "'.base64_encode("custom: https://example.com").'"}'))); + $repoConfig = array( 'url' => $repoUrl, );