Skip to content

Commit ff262ba

Browse files
alamiraultweaverryan
authored andcommitted
Add PR message if branch desc mismatch branch target
1 parent 76eccf5 commit ff262ba

File tree

4 files changed

+286
-0
lines changed

4 files changed

+286
-0
lines changed

README.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,11 @@ the first 20 hours.
9999
If a PR is opened towards a branch that is not maintained anymore, Carson will
100100
kindly explain to the author what to do.
101101

102+
### Add a warning if pull request description mismatch the targeted branch.
103+
104+
If a PR is opened towards a branch but the description does not match, Carson will
105+
post a nice comment to explain to the author what to do.
106+
102107
### Open issues when docs for config reference is incomplete
103108

104109
The Symfony documentation includes some pages with "configuration reference", to

config/services.yaml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ parameters:
1414
- 'App\Subscriber\WelcomeFirstTimeContributorSubscriber'
1515
- 'App\Subscriber\CloseDraftPRSubscriber'
1616
- 'App\Subscriber\UnsupportedBranchSubscriber'
17+
- 'App\Subscriber\MismatchBranchDescriptionSubscriber'
1718
- 'App\Subscriber\RemoveStalledLabelOnCommentSubscriber'
1819
- 'App\Subscriber\RewriteUnwantedPhrasesSubscriber'
1920
- 'App\Subscriber\AllowEditFromMaintainerSubscriber'
@@ -55,6 +56,7 @@ parameters:
5556
- 'App\Subscriber\WelcomeFirstTimeContributorSubscriber'
5657
- 'App\Subscriber\CloseDraftPRSubscriber'
5758
- 'App\Subscriber\UnsupportedBranchSubscriber'
59+
- 'App\Subscriber\MismatchBranchDescriptionSubscriber'
5860
- 'App\Subscriber\RemoveStalledLabelOnCommentSubscriber'
5961
- 'App\Subscriber\RewriteUnwantedPhrasesSubscriber'
6062
- 'App\Subscriber\UpdateMilestoneWhenLabeledWaitingCodeMergeSubscriber'
Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
<?php
2+
3+
namespace App\Subscriber;
4+
5+
use App\Api\Issue\IssueApi;
6+
use App\Event\GitHubEvent;
7+
use App\GitHubEvents;
8+
use Psr\Log\LoggerInterface;
9+
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
10+
use Symfony\Component\String\UnicodeString;
11+
12+
/**
13+
* @author Antoine Makdessi <[email protected]>
14+
* @author Antoine Lamirault <[email protected]>
15+
*/
16+
class MismatchBranchDescriptionSubscriber implements EventSubscriberInterface
17+
{
18+
private IssueApi $issueApi;
19+
private LoggerInterface $logger;
20+
21+
public function __construct(IssueApi $issueApi, LoggerInterface $logger)
22+
{
23+
$this->issueApi = $issueApi;
24+
$this->logger = $logger;
25+
}
26+
27+
public function onPullRequest(GitHubEvent $event): void
28+
{
29+
$data = $event->getData();
30+
if (!in_array($data['action'], ['opened', 'ready_for_review']) || ($data['pull_request']['draft'] ?? false)) {
31+
return;
32+
}
33+
34+
$number = $data['pull_request']['number'];
35+
36+
$descriptionBranch = $this->extractDescriptionBranchFromBody($data['pull_request']['body']);
37+
if (null === $descriptionBranch) {
38+
$this->logger->notice('Pull Request without default template.', ['pull_request_number' => $number]);
39+
40+
return;
41+
}
42+
43+
$targetBranch = $data['pull_request']['base']['ref'];
44+
if ($targetBranch === $descriptionBranch) {
45+
return;
46+
}
47+
48+
$this->issueApi->commentOnIssue($event->getRepository(), $number, <<<TXT
49+
Hey!
50+
51+
Thanks for your PR. You are targeting branch "$targetBranch" but it seems your PR description refers to branch "$descriptionBranch".
52+
Could you update the PR description or change target branch? This helps core maintainers a lot.
53+
54+
Cheers!
55+
56+
Carsonbot
57+
TXT
58+
);
59+
60+
$event->setResponseData([
61+
'pull_request' => $number,
62+
]);
63+
}
64+
65+
public static function getSubscribedEvents(): array
66+
{
67+
return [
68+
GitHubEvents::PULL_REQUEST => 'onPullRequest',
69+
];
70+
}
71+
72+
private function extractDescriptionBranchFromBody(string $body): ?string
73+
{
74+
$s = new UnicodeString($body);
75+
76+
// @see symfony/symfony/.github/PULL_REQUEST_TEMPLATE.md
77+
if (!$s->containsAny('Branch?')) {
78+
return null;
79+
}
80+
81+
$rowsDescriptionBranch = $s->match('/.*Branch.*/');
82+
83+
$rowDescriptionBranch = $rowsDescriptionBranch[0]; // row matching
84+
85+
$descriptionBranchParts = \explode('|', $rowDescriptionBranch);
86+
if (false === array_key_exists(2, $descriptionBranchParts)) { // Branch description is in second Markdown table column
87+
return null;
88+
}
89+
90+
$descriptionBranch = $descriptionBranchParts[2]; // get the version
91+
92+
return \trim($descriptionBranch);
93+
}
94+
}
Lines changed: 185 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,185 @@
1+
<?php
2+
3+
namespace App\Tests\Subscriber;
4+
5+
use App\Api\Issue\IssueApi;
6+
use App\Api\Issue\NullIssueApi;
7+
use App\Event\GitHubEvent;
8+
use App\GitHubEvents;
9+
use App\Model\Repository;
10+
use App\Subscriber\MismatchBranchDescriptionSubscriber;
11+
use PHPUnit\Framework\TestCase;
12+
use Psr\Log\NullLogger;
13+
use Symfony\Component\EventDispatcher\EventDispatcher;
14+
15+
final class MismatchBranchDescriptionSubscriberTest extends TestCase
16+
{
17+
/**
18+
* @var IssueApi
19+
*/
20+
private $issueApi;
21+
22+
/**
23+
* @var Repository
24+
*/
25+
private $repository;
26+
27+
/**
28+
* @var EventDispatcher
29+
*/
30+
private $dispatcher;
31+
32+
protected function setUp(): void
33+
{
34+
$this->issueApi = $this->createMock(NullIssueApi::class);
35+
36+
$subscriber = new MismatchBranchDescriptionSubscriber($this->issueApi, new NullLogger());
37+
$this->repository = new Repository('carsonbot-playground', 'symfony', null);
38+
39+
$this->dispatcher = new EventDispatcher();
40+
$this->dispatcher->addSubscriber($subscriber);
41+
}
42+
43+
public function testOnPullRequestOpenMatch()
44+
{
45+
$this->issueApi->expects($this->never())
46+
->method('commentOnIssue');
47+
48+
$body = <<<TXT
49+
| Q | A
50+
| ------------- | ---
51+
| Branch? | 6.2
52+
| Bug fix? | yes/no
53+
TXT;
54+
55+
$event = new GitHubEvent([
56+
'action' => 'opened',
57+
'pull_request' => [
58+
'number' => 1234,
59+
'body' => $body,
60+
'base' => [
61+
'ref' => '6.2',
62+
],
63+
],
64+
], $this->repository);
65+
66+
$this->dispatcher->dispatch($event, GitHubEvents::PULL_REQUEST);
67+
$responseData = $event->getResponseData();
68+
69+
$this->assertCount(0, $responseData);
70+
}
71+
72+
public function testOnPullRequestOpenNotMatch()
73+
{
74+
$this->issueApi->expects($this->once())
75+
->method('commentOnIssue');
76+
77+
$body = <<<TXT
78+
| Q | A
79+
| ------------- | ---
80+
| Branch? | 6.1
81+
| Bug fix? | yes/no
82+
TXT;
83+
84+
$event = new GitHubEvent([
85+
'action' => 'opened',
86+
'pull_request' => [
87+
'number' => 1234,
88+
'body' => $body,
89+
'base' => [
90+
'ref' => '6.2',
91+
],
92+
],
93+
], $this->repository);
94+
95+
$this->dispatcher->dispatch($event, GitHubEvents::PULL_REQUEST);
96+
$responseData = $event->getResponseData();
97+
98+
$this->assertCount(1, $responseData);
99+
$this->assertSame(1234, $responseData['pull_request']);
100+
}
101+
102+
public function testOnPullRequestOpenWithoutBranchRow()
103+
{
104+
$this->issueApi->expects($this->never())
105+
->method('commentOnIssue');
106+
107+
$body = <<<TXT
108+
| Q | A
109+
| ------------- | ---
110+
| Bug fix? | yes/no
111+
TXT;
112+
113+
$event = new GitHubEvent([
114+
'action' => 'opened',
115+
'pull_request' => [
116+
'number' => 1234,
117+
'body' => $body,
118+
'base' => [
119+
'ref' => '6.2',
120+
],
121+
],
122+
], $this->repository);
123+
124+
$this->dispatcher->dispatch($event, GitHubEvents::PULL_REQUEST);
125+
$responseData = $event->getResponseData();
126+
127+
$this->assertCount(0, $responseData);
128+
}
129+
130+
public function testOnPullRequestOpenBadBranchFormat()
131+
{
132+
$this->issueApi->expects($this->once())
133+
->method('commentOnIssue');
134+
135+
$body = <<<TXT
136+
| Q | A
137+
| ------------- | ---
138+
| Branch? | 6.2 for features / 4.4, 5.4, 6.0 or 6.1 for bug fixes <!-- see below -->
139+
| Bug fix? | yes/no
140+
TXT;
141+
142+
$event = new GitHubEvent([
143+
'action' => 'opened',
144+
'pull_request' => [
145+
'number' => 1234,
146+
'body' => $body,
147+
'base' => [
148+
'ref' => '6.2',
149+
],
150+
],
151+
], $this->repository);
152+
153+
$this->dispatcher->dispatch($event, GitHubEvents::PULL_REQUEST);
154+
$responseData = $event->getResponseData();
155+
156+
$this->assertCount(1, $responseData);
157+
$this->assertSame(1234, $responseData['pull_request']);
158+
}
159+
160+
public function testOnPullRequestOpenBranchNotInTable()
161+
{
162+
$this->issueApi->expects($this->never())
163+
->method('commentOnIssue');
164+
165+
$body = <<<TXT
166+
Branch? 6.2
167+
TXT;
168+
169+
$event = new GitHubEvent([
170+
'action' => 'opened',
171+
'pull_request' => [
172+
'number' => 1234,
173+
'body' => $body,
174+
'base' => [
175+
'ref' => '6.2',
176+
],
177+
],
178+
], $this->repository);
179+
180+
$this->dispatcher->dispatch($event, GitHubEvents::PULL_REQUEST);
181+
$responseData = $event->getResponseData();
182+
183+
$this->assertCount(0, $responseData);
184+
}
185+
}

0 commit comments

Comments
 (0)