Skip to content

Commit 509277e

Browse files
Add RELEASE_HEADING_FRAGMENT output (#20)
* Add ExtractPermalinkFragmentFromHeading Action * Update Tests * Add ext-dom to composer.json * Rename CreateNewReleaseHeading * Add CreateNewReleaseHeading * Use CreateNewReleaseHeading in PasteReleaseNotesAtTheTop * Use Fragment Extraction in NewReleaseHeading Classes * Add DocBlocks
1 parent 48faf57 commit 509277e

8 files changed

+190
-49
lines changed
Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace App\Actions;
6+
7+
use App\Support\GitHubActionsOutput;
8+
use DOMDocument;
9+
use League\CommonMark\Environment\Environment;
10+
use League\CommonMark\Event\DocumentParsedEvent;
11+
use League\CommonMark\Extension\CommonMark\CommonMarkCoreExtension;
12+
use League\CommonMark\Extension\CommonMark\Node\Block\Heading;
13+
use League\CommonMark\Extension\HeadingPermalink\HeadingPermalinkExtension;
14+
use League\CommonMark\Extension\HeadingPermalink\HeadingPermalinkProcessor;
15+
use League\CommonMark\Extension\HeadingPermalink\HeadingPermalinkRenderer;
16+
use League\CommonMark\Node\Block\Document;
17+
use League\CommonMark\Renderer\HtmlRenderer;
18+
19+
class ExtractPermalinkFragmentFromHeading
20+
{
21+
private GitHubActionsOutput $gitHubActionsOutput;
22+
23+
public function __construct(GitHubActionsOutput $gitHubActionsOutput)
24+
{
25+
$this->gitHubActionsOutput = $gitHubActionsOutput;
26+
}
27+
28+
public function execute(Heading $releaseHeading): string
29+
{
30+
$releaseHeading = clone $releaseHeading;
31+
32+
$renderedHtml = $this->attachPermalinkAndRenderAsHtml($releaseHeading);
33+
$linkFragment = $this->extractLinkFragmentFromRenderedHtml($renderedHtml);
34+
35+
return tap($linkFragment, function (string $linkFragment) {
36+
$this->gitHubActionsOutput->add('RELEASE_URL_FRAGMENT', $linkFragment);
37+
});
38+
}
39+
40+
protected function attachPermalinkAndRenderAsHtml(Heading $releaseHeading): string
41+
{
42+
$environment = $this->prepareCommonmarkEnvironment();
43+
44+
$document = $this->attachPermalinkToHeading($releaseHeading, $environment);
45+
46+
$renderer = new HtmlRenderer($environment);
47+
48+
return $renderer->renderDocument($document)->getContent();
49+
}
50+
51+
protected function prepareCommonmarkEnvironment(): Environment
52+
{
53+
$config = [
54+
'heading_permalink' => [
55+
'html_class' => '',
56+
'id_prefix' => '',
57+
'fragment_prefix' => '',
58+
'insert' => 'before',
59+
'min_heading_level' => 1,
60+
'max_heading_level' => 6,
61+
'title' => '',
62+
'symbol' => HeadingPermalinkRenderer::DEFAULT_SYMBOL,
63+
'aria_hidden' => false,
64+
],
65+
];
66+
67+
$environment = new Environment($config);
68+
$environment->addExtension(new CommonMarkCoreExtension());
69+
$environment->addExtension(new HeadingPermalinkExtension());
70+
71+
return $environment;
72+
}
73+
74+
/**
75+
* Attach Heading Permalink to given ReleaseHeading using
76+
* the Commonmark Heading Permalink Extension.
77+
* @param Heading $releaseHeading
78+
* @param Environment $environment
79+
* @return Document
80+
*/
81+
protected function attachPermalinkToHeading(Heading $releaseHeading, Environment $environment): Document
82+
{
83+
$document = new Document();
84+
$document->appendChild($releaseHeading);
85+
86+
$documentParsedEvent = new DocumentParsedEvent($document);
87+
88+
$processor = (new HeadingPermalinkProcessor());
89+
$processor->setEnvironment($environment);
90+
$processor->__invoke($documentParsedEvent);
91+
92+
return $document;
93+
}
94+
95+
/**
96+
* Parse the rendered HTML as a DOM Document and extract the
97+
* href attribute from the generated a-tag.
98+
* @param string $html
99+
* @return string|null
100+
*/
101+
protected function extractLinkFragmentFromRenderedHtml(string $html): ?string
102+
{
103+
$domDocument = new DOMDocument();
104+
$domDocument->loadHTML($html);
105+
106+
return $domDocument
107+
->getElementsByTagName('a')
108+
->item(0)
109+
->attributes
110+
->getNamedItem('href')
111+
->value;
112+
}
113+
}

app/Actions/PasteReleaseNotesAtTheTop.php

Lines changed: 5 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -4,23 +4,24 @@
44

55
namespace App\Actions;
66

7+
use App\CreateNewReleaseHeading;
78
use App\Exceptions\ReleaseNotesNotProvidedException;
89
use App\MarkdownParser;
910
use App\Queries\FindFirstSecondLevelHeading;
10-
use League\CommonMark\Extension\CommonMark\Node\Block\Heading;
1111
use League\CommonMark\Node\Block\Document;
12-
use League\CommonMark\Node\Inline\Text;
1312
use Throwable;
1413

1514
class PasteReleaseNotesAtTheTop
1615
{
1716
private FindFirstSecondLevelHeading $findFirstSecondLevelHeading;
1817
private MarkdownParser $parser;
18+
private CreateNewReleaseHeading $createNewReleaseHeading;
1919

20-
public function __construct(FindFirstSecondLevelHeading $findFirstSecondLevelHeading, MarkdownParser $markdownParser)
20+
public function __construct(FindFirstSecondLevelHeading $findFirstSecondLevelHeading, MarkdownParser $markdownParser, CreateNewReleaseHeading $createNewReleaseHeading)
2121
{
2222
$this->findFirstSecondLevelHeading = $findFirstSecondLevelHeading;
2323
$this->parser = $markdownParser;
24+
$this->createNewReleaseHeading = $createNewReleaseHeading;
2425
}
2526

2627
/**
@@ -30,8 +31,7 @@ public function execute(string $latestVersion, ?string $releaseNotes, string $re
3031
{
3132
throw_if(empty($releaseNotes), ReleaseNotesNotProvidedException::class);
3233

33-
// Create new Heading containing the new version and date
34-
$newReleaseHeading = $this->createNewReleaseHeading($latestVersion, $releaseDate);
34+
$newReleaseHeading = $this->createNewReleaseHeading->create($latestVersion, $releaseDate);
3535

3636
// Prepend the new Release Heading to the Release Notes
3737
$parsedReleaseNotes = $this->parser->parse($releaseNotes);
@@ -49,12 +49,4 @@ public function execute(string $latestVersion, ?string $releaseNotes, string $re
4949

5050
return $changelog;
5151
}
52-
53-
protected function createNewReleaseHeading(string $latestVersion, string $releaseDate): Heading
54-
{
55-
return tap(new Heading(2), function (Heading $heading) use ($latestVersion, $releaseDate) {
56-
$heading->appendChild(new Text($latestVersion));
57-
$heading->appendChild(new Text(" - {$releaseDate}"));
58-
});
59-
}
6052
}

app/Actions/PasteReleaseNotesBelowUnreleasedHeading.php

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55
namespace App\Actions;
66

7-
use App\CreateNewReleaseHeading;
7+
use App\CreateNewReleaseHeadingWithCompareUrl;
88
use App\GenerateCompareUrl;
99
use App\MarkdownParser;
1010
use App\Queries\FindSecondLevelHeadingWithText;
@@ -21,15 +21,15 @@ class PasteReleaseNotesBelowUnreleasedHeading
2121
private MarkdownParser $parser;
2222
private GenerateCompareUrl $generateCompareUrl;
2323
private FindSecondLevelHeadingWithText $findPreviousVersionHeading;
24-
private CreateNewReleaseHeading $createNewReleaseHeading;
24+
private CreateNewReleaseHeadingWithCompareUrl $createNewReleaseHeading;
2525
private GitHubActionsOutput $gitHubActionsOutput;
2626

2727
public function __construct(
28-
MarkdownParser $markdownParser,
29-
GenerateCompareUrl $generateCompareUrl,
30-
FindSecondLevelHeadingWithText $findPreviousVersionHeading,
31-
CreateNewReleaseHeading $createNewReleaseHeading,
32-
GitHubActionsOutput $gitHubActionsOutput
28+
MarkdownParser $markdownParser,
29+
GenerateCompareUrl $generateCompareUrl,
30+
FindSecondLevelHeadingWithText $findPreviousVersionHeading,
31+
CreateNewReleaseHeadingWithCompareUrl $createNewReleaseHeading,
32+
GitHubActionsOutput $gitHubActionsOutput
3333
) {
3434
$this->parser = $markdownParser;
3535
$this->generateCompareUrl = $generateCompareUrl;

app/CreateNewReleaseHeading.php

Lines changed: 9 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -4,44 +4,26 @@
44

55
namespace App;
66

7-
use App\Support\GitHubActionsOutput;
7+
use App\Actions\ExtractPermalinkFragmentFromHeading;
88
use League\CommonMark\Extension\CommonMark\Node\Block\Heading;
9-
use League\CommonMark\Extension\CommonMark\Node\Inline\Link;
109
use League\CommonMark\Node\Inline\Text;
1110

1211
class CreateNewReleaseHeading
1312
{
14-
private GenerateCompareUrl $generateCompareUrl;
15-
private GitHubActionsOutput $gitHubActionsOutput;
13+
private ExtractPermalinkFragmentFromHeading $extractPermalinkFragmentFromHeading;
1614

17-
public function __construct(GenerateCompareUrl $generateCompareUrl, GitHubActionsOutput $gitHubActionsOutput)
15+
public function __construct(ExtractPermalinkFragmentFromHeading $extractPermalinkFragmentFromHeading)
1816
{
19-
$this->generateCompareUrl = $generateCompareUrl;
20-
$this->gitHubActionsOutput = $gitHubActionsOutput;
17+
$this->extractPermalinkFragmentFromHeading = $extractPermalinkFragmentFromHeading;
2118
}
2219

23-
public function create(string $repositoryUrl, string $previousVersion, string $latestVersion, string $releaseDate): Heading
20+
public function create(string $latestVersion, string $releaseDate): Heading
2421
{
25-
$url = $this->generateCompareUrl->generate($repositoryUrl, $previousVersion, $latestVersion);
22+
return tap(new Heading(2), function (Heading $heading) use ($latestVersion, $releaseDate) {
23+
$heading->appendChild(new Text($latestVersion));
24+
$heading->appendChild(new Text(" - {$releaseDate}"));
2625

27-
$this->gitHubActionsOutput->add('RELEASE_COMPARE_URL', $url);
28-
29-
return tap(new Heading(2), function (Heading $heading) use ($url, $latestVersion, $releaseDate) {
30-
$heading->appendChild($this->createLinkNode($latestVersion, $url));
31-
$heading->appendChild($this->createDateNode($releaseDate));
32-
});
33-
}
34-
35-
protected function createLinkNode(string $latestVersion, string $url): Link
36-
{
37-
return tap(new Link($url), function (Link $link) use ($latestVersion) {
38-
$linkText = new Text($latestVersion);
39-
$link->appendChild($linkText);
26+
$this->extractPermalinkFragmentFromHeading->execute($heading);
4027
});
4128
}
42-
43-
protected function createDateNode(string $releaseDate): Text
44-
{
45-
return new Text(" - {$releaseDate}");
46-
}
4729
}
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace App;
6+
7+
use App\Actions\ExtractPermalinkFragmentFromHeading;
8+
use App\Support\GitHubActionsOutput;
9+
use League\CommonMark\Extension\CommonMark\Node\Block\Heading;
10+
use League\CommonMark\Extension\CommonMark\Node\Inline\Link;
11+
use League\CommonMark\Node\Inline\Text;
12+
13+
class CreateNewReleaseHeadingWithCompareUrl
14+
{
15+
private GenerateCompareUrl $generateCompareUrl;
16+
private GitHubActionsOutput $gitHubActionsOutput;
17+
private ExtractPermalinkFragmentFromHeading $extractPermalinkFragmentFromHeading;
18+
19+
public function __construct(GenerateCompareUrl $generateCompareUrl, GitHubActionsOutput $gitHubActionsOutput, ExtractPermalinkFragmentFromHeading $extractPermalinkFragmentFromHeading)
20+
{
21+
$this->generateCompareUrl = $generateCompareUrl;
22+
$this->gitHubActionsOutput = $gitHubActionsOutput;
23+
$this->extractPermalinkFragmentFromHeading = $extractPermalinkFragmentFromHeading;
24+
}
25+
26+
public function create(string $repositoryUrl, string $previousVersion, string $latestVersion, string $releaseDate): Heading
27+
{
28+
$url = $this->generateCompareUrl->generate($repositoryUrl, $previousVersion, $latestVersion);
29+
30+
$this->gitHubActionsOutput->add('RELEASE_COMPARE_URL', $url);
31+
32+
return tap(new Heading(2), function (Heading $heading) use ($url, $latestVersion, $releaseDate) {
33+
$heading->appendChild($this->createLinkNode($latestVersion, $url));
34+
$heading->appendChild($this->createDateNode($releaseDate));
35+
36+
$this->extractPermalinkFragmentFromHeading->execute($heading);
37+
});
38+
}
39+
40+
protected function createLinkNode(string $latestVersion, string $url): Link
41+
{
42+
return tap(new Link($url), function (Link $link) use ($latestVersion) {
43+
$linkText = new Text($latestVersion);
44+
$link->appendChild($linkText);
45+
});
46+
}
47+
48+
protected function createDateNode(string $releaseDate): Text
49+
{
50+
return new Text(" - {$releaseDate}");
51+
}
52+
}

composer.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
],
1616
"require": {
1717
"php": "^8.0",
18+
"ext-dom": "*",
1819
"league/commonmark": "^2.0",
1920
"webmozart/assert": "^1.10",
2021
"wnx/commonmark-markdown-renderer": "^1.0"

tests/Feature/UpdateCommandTest.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@
4242
'--github-actions-output' => true,
4343
])
4444
->expectsOutputToContain(sprintf("::set-output name=%s::%s", 'RELEASE_COMPARE_URL', 'https://github.com/org/repo/compare/v0.1.0...v1.0.0'))
45+
->expectsOutputToContain(sprintf("::set-output name=%s::%s", 'RELEASE_URL_FRAGMENT', '#v100---2021-02-01'))
4546
->expectsOutputToContain(sprintf("::set-output name=%s::%s", 'UNRELEASED_COMPARE_URL', 'https://github.com/org/repo/compare/v1.0.0...HEAD'))
4647
->assertSuccessful();
4748
});

tests/Unit/CreateNewReleaseHeadingTest.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
declare(strict_types=1);
44

5-
use App\CreateNewReleaseHeading;
5+
use App\CreateNewReleaseHeadingWithCompareUrl;
66
use League\CommonMark\Environment\Environment;
77
use League\CommonMark\Extension\CommonMark\Node\Block\Heading;
88
use League\CommonMark\Extension\CommonMark\Node\Inline\Link;
@@ -22,7 +22,7 @@
2222
$markdownRenderer = new MarkdownRenderer($environment);
2323

2424
/** @var Document $result */
25-
$result = app(CreateNewReleaseHeading::class)->create(
25+
$result = app(CreateNewReleaseHeadingWithCompareUrl::class)->create(
2626
$repositoryUrl,
2727
$previousVersion,
2828
$latestVersion,

0 commit comments

Comments
 (0)