Skip to content

Commit

Permalink
Merge branch 'hotfix/22'
Browse files Browse the repository at this point in the history
Close #22
  • Loading branch information
weierophinney committed Apr 26, 2018
2 parents d214f97 + cf24cb0 commit 852d020
Show file tree
Hide file tree
Showing 5 changed files with 157 additions and 7 deletions.
9 changes: 7 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,20 @@

All notable changes to this project will be documented in this file, in reverse chronological order by release.

## 1.4.4 - TBD
## 1.4.4 - 2018-04-26

### Added

- Nothing.

### Changed

- Nothing.
- [#22](https://github.com/phly/keep-a-changelog/pull/22) modifies how PR links are generated in several ways:
- If the provided package name does not result in a valid PR link, it raises an excepion.
- If the package name discovered in the `composer.json` does not result in a valid PR link, it then
- Probes the git remotes to find the first that results in a valid package link.
In each case, it performs a `HEAD` request on the generated link to determine if it is
valid, following redirects as encountered.

### Deprecated

Expand Down
78 changes: 76 additions & 2 deletions src/EntryCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -134,16 +134,90 @@ private function prepareEntry(InputInterface $input) : string

private function preparePullRequestLink(int $pr, ?string $package) : string
{
$package = $package ?: (new ComposerPackage())->getName(realpath(getcwd()));
if (null !== $package) {
$link = $this->generatePullRequestLink($pr, $package);

if (null === $link) {
throw Exception\InvalidPullRequestLinkException::forPackage($package, $pr);
}

return $link;
}

$link = $this->generatePullRequestLink($pr, (new ComposerPackage())->getName(realpath(getcwd())));

if (null !== $link) {
return $link;
}

foreach ($this->getGithubPackageNames() as $package) {
$link = $this->generatePullRequestLink($pr, $package);

if (null !== $link) {
return $link;
}
}

throw Exception\InvalidPullRequestLinkException::noValidLinks($pr);
}

private function getGithubPackageNames() : array
{
exec('git remote', $remotes, $return);

if (0 !== $return) {
return [];
}

$packages = [];

foreach ($remotes as $remote) {
$url = [];
exec(sprintf('git remote get-url %s', escapeshellarg($remote)), $url, $return);

if (0 !== $return) {
continue;
}

if (0 === preg_match('(github.com[:/](.*?)\.git)', $url[0], $matches)) {
continue;
}

$packages[] = $matches[1];
}

return $packages;
}

private function generatePullRequestLink(int $pr, string $package) : ?string
{
if (! preg_match('#^[a-z0-9]+[a-z0-9_-]*/[a-z0-9]+[a-z0-9_-]*$#i', $package)) {
throw Exception\InvalidPackageNameException::forPackage($package);
}

return sprintf(
$link = sprintf(
'https://github.com/%s/pull/%d',
$package,
$pr
);

return $this->probeLink($link) ? $link : null;
}

private function probeLink(string $link) : bool
{
$headers = get_headers($link, 1, stream_context_create(['http' => ['method' => 'HEAD']]));
$statusLine = explode(' ', $headers[0]);
$statusCode = (int) $statusLine[1];

if ($statusCode < 300) {
return true;
}

if ($statusCode >= 300 && $statusCode <= 399 && array_key_exists('Location', $headers)) {
return $this->probeLink($headers['Location']);
}

return false;
}
}
23 changes: 23 additions & 0 deletions src/Exception/InvalidPullRequestException.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<?php
/**
* @see https://github.com/phly/keep-a-changelog-tagger for the canonical source repository
* @copyright Copyright (c) 2018 Matthew Weier O'Phinney
* @license https://github.com/phly/keep-a-changelog-tagger/blob/master/LICENSE.md New BSD License
*/

declare(strict_types=1);

namespace Phly\KeepAChangelog\Exception;

use RuntimeException;

class InvalidPullRequestException extends RuntimeException
{
public static function for(int $pr) : self
{
return new self(sprintf(
'PR %d is not valid',
$pr
));
}
}
32 changes: 32 additions & 0 deletions src/Exception/InvalidPullRequestLinkException.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
<?php
/**
* @see https://github.com/phly/keep-a-changelog-tagger for the canonical source repository
* @copyright Copyright (c) 2018 Matthew Weier O'Phinney
* @license https://github.com/phly/keep-a-changelog-tagger/blob/master/LICENSE.md New BSD License
*/

declare(strict_types=1);

namespace Phly\KeepAChangelog\Exception;

use RuntimeException;

class InvalidPullRequestLinkException extends RuntimeException
{
public static function forPackage(string $package, int $pr) : self
{
return new self(sprintf(
'The pull request package %s has no PR %d',
$package,
$pr
));
}

public static function noValidLinks(int $pr) : self
{
return new self(sprintf(
'No valid pull request link could be found for PR %d',
$pr
));
}
}
22 changes: 19 additions & 3 deletions test/EntryCommandTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ public function testPrepareEntryReturnsEntryVerbatimIfNoPrOptionProvided()
public function testPrepareEntryWithPrOptionRaisesExceptionIfPackageOptionIsInvalid()
{
$entry = 'This is the entry';
$pr = 42;
$pr = 1;
$package = 'not-a-valid-package-name';

$this->input->getArgument('entry')->willReturn($entry);
Expand All @@ -99,10 +99,26 @@ public function testPrepareEntryWithPrOptionRaisesExceptionIfPackageOptionIsInva
$this->reflectMethod($command, 'prepareEntry')->invoke($command, $this->input->reveal());
}

public function testPrepareEntryWithPrOptionRaisesExceptionIfLinkIsInvalid()
{
$entry = 'This is the entry';
$pr = 9999999999;
$package = 'phly/keep-a-changelog';

$this->input->getArgument('entry')->willReturn($entry);
$this->input->getOption('pr')->willReturn($pr);
$this->input->getOption('package')->willReturn($package);

$command = new EntryCommand('entry:added');

$this->expectException(Exception\InvalidPullRequestLinkException::class);
$this->reflectMethod($command, 'prepareEntry')->invoke($command, $this->input->reveal());
}

public function testPrepareEntryReturnsEntryWithPrLinkPrefixedWhenPackageOptionPresentAndValid()
{
$entry = 'This is the entry';
$pr = 42;
$pr = 1;
$package = 'phly/keep-a-changelog';

$this->input->getArgument('entry')->willReturn($entry);
Expand All @@ -111,7 +127,7 @@ public function testPrepareEntryReturnsEntryWithPrLinkPrefixedWhenPackageOptionP

$command = new EntryCommand('entry:added');

$expected = '[#42](https://github.com/phly/keep-a-changelog/pull/42) ' . $entry;
$expected = '[#1](https://github.com/phly/keep-a-changelog/pull/1) ' . $entry;

$this->assertSame(
$expected,
Expand Down

0 comments on commit 852d020

Please sign in to comment.