Skip to content

Commit 1bbfa9b

Browse files
committed
Commit enhancing WIP
1 parent 626d457 commit 1bbfa9b

5 files changed

Lines changed: 245 additions & 8 deletions

File tree

composer.json

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,12 @@
22
"name": "phpwatch/php-commitlog-builder",
33
"type": "library",
44
"require": {
5-
"php": "^8.2"
5+
"php": "^8.2",
6+
"ext-curl": "*",
7+
"ayesh/curl-fetcher": "^1.0.2"
68
},
79
"require-dev": {
8-
"phpunit/phpunit": "^10.3"
10+
"phpunit/phpunit": "^10.3.1"
911
},
1012
"license": "MIT",
1113
"autoload": {

composer.lock

Lines changed: 45 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/CommitFetcher.php

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
<?php
2+
3+
namespace PHPWatch\PHPCommitBuilder;
4+
use Ayesh\CurlFetcher\CurlFetcher;
5+
6+
class CommitFetcher {
7+
private const API_ENDPOINT_COMPARE = 'https://api.github.com/repos/php/php-src/compare/';
8+
private const API_ENDPOINT_COMMIT_LIST = 'https://api.github.com/repos/php/php-src/commits';
9+
private ?string $apiKey = null;
10+
private CurlFetcher $curlFetcher;
11+
12+
public function __construct(string $apiKey = null) {
13+
$this->apiKey = $apiKey;
14+
$this->curlFetcher = new CurlFetcher();
15+
}
16+
17+
public function getCommitListForPastDays(int $pastDays): array {
18+
return $this->getListDateRange(date('c', time() - ($pastDays * 86400)), date('c'));
19+
}
20+
21+
private function getListDateRange(string $since, string $until): array {
22+
$params = [
23+
'since' => $since,
24+
'until' => $until,
25+
'per_page' => 100,
26+
'page' => 1,
27+
];
28+
29+
$return = [];
30+
$hardLimits = 50;
31+
32+
do {
33+
$paramsUrl = http_build_query($params);
34+
$url = static::API_ENDPOINT_COMMIT_LIST . '?' .$paramsUrl;
35+
36+
$headers = [];
37+
if ($this->apiKey) {
38+
$headers[] = 'Authorization: Bearer '. $this->apiKey;
39+
}
40+
41+
$data = $this->curlFetcher->getJson($url, $headers);
42+
/** @noinspection SlowArrayOperationsInLoopInspection */
43+
$return = array_merge($return, $data);
44+
45+
$params['page']++;
46+
$hardLimits--;
47+
48+
} while (count($data) >= 100 && $hardLimits > 0);
49+
50+
return $return;
51+
}
52+
}

src/CommitFormatter.php

Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
<?php
2+
3+
namespace PHPWatch\PHPCommitBuilder;
4+
5+
class CommitFormatter {
6+
private array $commitsList = [];
7+
8+
private array $commitsGroupedByAuthor = [];
9+
private const EOL = "\r\n";
10+
11+
public function __construct(array $inputCommits) {
12+
$this->process($inputCommits);
13+
}
14+
15+
private function process(array $inputCommits): void {
16+
$formattedCommits = [];
17+
$i = 0;
18+
19+
foreach ($inputCommits as $commit) {
20+
$commitArray = $this->splitCommit($commit);
21+
if ($this->shouldSkip($commitArray['subject'])) {
22+
continue;
23+
}
24+
25+
$commitArray['formatted'] = KeywordEnhancer::enhanceCommit($commitArray['subject'], substr($commitArray['hash'], 0, 10));
26+
$formattedCommits[$i] = $commitArray;
27+
28+
if (!isset($this->commitsGroupedByAuthor[$commitArray['author']])) {
29+
$this->commitsGroupedByAuthor[$commitArray['author']] = [];
30+
}
31+
32+
$this->commitsGroupedByAuthor[$commitArray['author']][] = $i;
33+
34+
$i++;
35+
}
36+
37+
$this->commitsList = $formattedCommits;
38+
ksort($this->commitsGroupedByAuthor);
39+
}
40+
41+
private function splitCommit(\stdClass $commit): array {
42+
$commitMessage = $commit->commit->message;
43+
$commitMessageParts = explode("\n", $commitMessage, 2);
44+
45+
return [
46+
'subject' => trim(trim($commitMessageParts[0]), '.'),
47+
'author' => trim($commit->commit->author->name),
48+
'hash' => trim($commit->sha),
49+
'message' => trim($commitMessage)
50+
];
51+
}
52+
53+
private function shouldSkip(string $commitMessage): bool {
54+
if ($commitMessage === '') {
55+
return true;
56+
}
57+
58+
// Skip merge commits
59+
if (str_starts_with($commitMessage, 'Merge branch')) {
60+
return true;
61+
}
62+
63+
// Skip merge commits
64+
if (str_starts_with($commitMessage, 'Merge remote-tracking branch')) {
65+
return true;
66+
}
67+
68+
// Skip "[ci skip]" messages
69+
if (str_contains($commitMessage, '[ci skip]') || str_contains($commitMessage, '[skip ci]')) {
70+
return true;
71+
}
72+
73+
if (str_contains($commitMessage, 'is now for PHP 8') || str_contains($commitMessage, 'is now for PHP-8')) {
74+
return true;
75+
}
76+
77+
if (str_starts_with($commitMessage, 'Update NEWS for ')) {
78+
return true;
79+
}
80+
81+
return false;
82+
}
83+
84+
public function getFormattedCommitList(): array {
85+
return $this->commitsList;
86+
}
87+
88+
public function getFormattedCommitListMarkup(): string {
89+
$output = '';
90+
foreach ($this->getFormattedCommitList() as $commit) {
91+
$output .= $this->getFormattedCommitListMarkup($commit['formatted'] . ' by ' . $commit['author']);
92+
}
93+
94+
return $output;
95+
}
96+
97+
public function getFormattedCommitListGroupedByAuthor(): array {
98+
$return = $this->commitsGroupedByAuthor;
99+
foreach ($return as $author => &$commitList) {
100+
foreach ($commitList as $key => &$commitI) {
101+
$commitI = $this->commitsList[$commitI];
102+
}
103+
}
104+
105+
return $return;
106+
}
107+
108+
public function getFormattedCommitListGroupedByAuthorMarkup(): string {
109+
$commitsByAuthor = $this->getFormattedCommitListGroupedByAuthor();
110+
111+
$output = '';
112+
foreach ($commitsByAuthor as $author => $commitList) {
113+
$output .= self::markdownTitle($author);
114+
foreach ($commitList as $commit) {
115+
$output .= self::markdownListItem($commit['formatted']);
116+
}
117+
}
118+
119+
return $output;
120+
}
121+
122+
private static function markdownTitle(string $title): string {
123+
return '### ' . self::plainText($title) . static::EOL;
124+
}
125+
126+
private static function markdownListItem(string $listItem): string {
127+
return ' - ' . self::plainText($listItem) . static::EOL;
128+
}
129+
130+
private static function plainText(string $text): string {
131+
return htmlspecialchars($text);
132+
}
133+
}

src/KeywordEnhancer.php

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,13 @@ public static function enhance(string $inputText): string {
2020
return static::format($inputText);
2121
}
2222

23-
private static function format(string $inputText): string {
23+
public static function enhanceCommit(string $commitSubject, string $shortHash): string {
24+
return static::format($commitSubject, $shortHash);
25+
}
26+
27+
private static function format(string $inputText, ?string $shortHash = null): string {
2428
$inputText = static::linkToBug($inputText);
25-
$inputText = static::linkToGitHub($inputText);
29+
$inputText = static::linkToGitHub($inputText, $shortHash);
2630
$inputText = static::codifyText($inputText);
2731
$inputText = static::linkToSecurityAnnouncements($inputText);
2832

@@ -37,7 +41,7 @@ private static function linkToBug(string $subject): string {
3741
return $subject;
3842
}
3943

40-
private static function linkToGitHub(string $subject): string {
44+
private static function linkToGitHub(string $subject, ?string $shortHash = null): string {
4145
$subject = preg_replace('/\bGH-(\d{3,6})\b/', "[GH-$1](https://github.com/php/php-src/issues/$1)", $subject);
4246
$subject = preg_replace(
4347
'/\b(([a-fA-F\d]){8})([a-fA-F\d]){4,32}\b/',
@@ -54,6 +58,10 @@ private static function linkToGitHub(string $subject): string {
5458
return $subject;
5559
}
5660

61+
if ($shortHash) {
62+
return $subject . sprintf(' in [%s](https://github.com/php/php-src/commit/%s)', $shortHash, $shortHash);
63+
}
64+
5765
return $subject;
5866
}
5967

0 commit comments

Comments
 (0)