Skip to content

Commit 13a84c6

Browse files
Add new option to parse and linkify GitHub Usernames (#41)
* Add parse-github-usernames option * Add test * Add implementation * Add docs for --parse-github-usernames
1 parent d4453e8 commit 13a84c6

File tree

6 files changed

+123
-1
lines changed

6 files changed

+123
-1
lines changed

README.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,14 @@ Optional. Tell the CLI explicitly to use the content between an "Unreleased Head
8585
### `--hide-release-date`
8686
Optional. Don't add the release date to the release heading.
8787

88+
### `--parse-github-usernames` (experimental 🧪)
89+
Optional. Look for GitHub usernames in `--release-notes` and linkify them. Currently not supported for release notes that are located in the existing changelog.
90+
91+
```diff
92+
- Added a new feature @stefanzweifel
93+
+ Added a new feature [@stefanzweifel](https://github.com/stefanzweifel)
94+
```
95+
8896
## Expected Changelog Formats
8997

9098
The CLI does its best to place the given release notes in the right place.
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace App\Actions;
6+
7+
class ParseAndLinkifyGitHubUsernamesAction
8+
{
9+
public function execute(?string $releaseNotes): ?string
10+
{
11+
if ($releaseNotes === null) {
12+
return null;
13+
}
14+
15+
// Pattern explanation:
16+
// - `(?<!\[)` and `(?!\])` are negative lookbehind and lookahead assertions, respectively.
17+
// They ensure that the GitHub username is not preceded or followed by a square
18+
// bracket [ or ], which indicates that the username is already wrapped in a link.
19+
// - `@([A-Za-z0-9_]+)` matches the GitHub username itself. It starts with
20+
// the @ symbol and consists of alphanumeric characters and underscores.
21+
$pattern = '/(?<!\[)@([A-Za-z0-9_]+)(?!\])/';
22+
23+
$replacement = '[@$1](https://github.com/$1)';
24+
25+
return preg_replace($pattern, $replacement, $releaseNotes);
26+
}
27+
}

app/Commands/UpdateCommand.php

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
namespace App\Commands;
66

77
use App\Actions\AddReleaseNotesToChangelogAction;
8+
use App\Actions\ParseAndLinkifyGitHubUsernamesAction;
89
use App\Exceptions\ReleaseAlreadyExistsInChangelogException;
910
use App\Exceptions\ReleaseNotesCanNotBeplacedException;
1011
use App\Exceptions\ReleaseNotesNotProvidedException;
@@ -26,6 +27,7 @@ class UpdateCommand extends Command
2627
{--compare-url-target-revision=HEAD : Target revision used in the compare URL of possible "Unreleased" heading.}
2728
{--github-actions-output : Display GitHub Actions related output}
2829
{--hide-release-date : Hide release date in the new release heading.}
30+
{--parse-github-usernames : Experimental: Find GitHub usernames in release notes and link to their profile.}
2931
{-w\--write : Write changes to file}
3032
';
3133

@@ -34,7 +36,7 @@ class UpdateCommand extends Command
3436
/**
3537
* @throws Throwable
3638
*/
37-
public function handle(AddReleaseNotesToChangelogAction $addReleaseNotesToChangelog, GitHubActionsOutput $gitHubActionsOutput)
39+
public function handle(AddReleaseNotesToChangelogAction $addReleaseNotesToChangelog, GitHubActionsOutput $gitHubActionsOutput, ParseAndLinkifyGitHubUsernamesAction $parseAndLinkifyGitHubUsernames)
3840
{
3941
$latestVersion = $this->option('latest-version') ?: $this->ask('What version should the CHANGELOG be updated too?');
4042
$releaseNotes = $this->getReleaseNotes();
@@ -55,8 +57,13 @@ public function handle(AddReleaseNotesToChangelogAction $addReleaseNotesToChange
5557
$headingText = $latestVersion;
5658
}
5759

60+
if ($this->option('parse-github-usernames')) {
61+
$releaseNotes = $parseAndLinkifyGitHubUsernames->execute($releaseNotes);
62+
}
63+
5864
$changelog = $this->getChangelogContent($pathToChangelog);
5965

66+
6067
try {
6168
$updatedChangelog = $addReleaseNotesToChangelog->execute(
6269
originalChangelog: $changelog,

tests/Feature/UpdateCommandTest.php

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -487,3 +487,25 @@
487487
->expectsOutput(file_get_contents(__DIR__ . '/../Stubs/expected-changelog-without-unreleased-without-date.md'))
488488
->assertSuccessful();
489489
});
490+
491+
it('parses github usernames and links to their github user profiles', function () {
492+
$this->artisan(UpdateCommand::class, [
493+
'--release-notes' => <<<MD
494+
### Added
495+
- New Feature A @stefanzweifel
496+
- New Feature B [@stefanzweifel](https://github.com/stefanzweifel)
497+
498+
### Changed
499+
- Update Feature C
500+
501+
### Removes
502+
- Remove Feature D
503+
MD,
504+
'--latest-version' => 'v1.0.0',
505+
'--path-to-changelog' => __DIR__ . '/../Stubs/base-changelog.md',
506+
'--release-date' => '2021-02-01',
507+
'--parse-github-usernames' => true,
508+
])
509+
->expectsOutput(file_get_contents(__DIR__ . '/../Stubs/expected-changelog-with-parsed-github-usernames.md'))
510+
->assertSuccessful();
511+
});
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
# Changelog
2+
3+
All notable changes to this project will be documented in this file.
4+
5+
The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
6+
and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
7+
8+
## [Unreleased](https://github.com/org/repo/compare/v1.0.0...HEAD)
9+
10+
Please do not update the unreleased notes.
11+
12+
<!-- Content should be placed here -->
13+
## [v1.0.0](https://github.com/org/repo/compare/v0.1.0...v1.0.0) - 2021-02-01
14+
15+
### Added
16+
17+
- New Feature A [@stefanzweifel](https://github.com/stefanzweifel)
18+
- New Feature B [@stefanzweifel](https://github.com/stefanzweifel)
19+
20+
### Changed
21+
22+
- Update Feature C
23+
24+
### Removes
25+
26+
- Remove Feature D
27+
28+
## v0.1.0 - 2021-01-01
29+
30+
### Added
31+
32+
- Initial Release
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
use App\Actions\ParseAndLinkifyGitHubUsernamesAction;
6+
7+
it('returns null if release notes are null also', function () {
8+
9+
$result = app(ParseAndLinkifyGitHubUsernamesAction::class)->execute(null);
10+
11+
expect($result)->toBeNull();
12+
});
13+
14+
it('does not replace anything if there are no GitHub usernames in string', function () {
15+
16+
$result = app(ParseAndLinkifyGitHubUsernamesAction::class)->execute('This is a string with no GitHub usernames.');
17+
18+
expect($result)->toEqual('This is a string with no GitHub usernames.');
19+
});
20+
21+
it('replaces GitHub usernames with links', function () {
22+
23+
$result = app(ParseAndLinkifyGitHubUsernamesAction::class)->execute('This is a string with a @username in it.');
24+
25+
expect($result)->toEqual('This is a string with a [@username](https://github.com/username) in it.');
26+
});

0 commit comments

Comments
 (0)