diff --git a/src/main/java/io/papermc/fill/discord/DiscordNotifier.java b/src/main/java/io/papermc/fill/discord/DiscordNotifier.java index 52e467a..251ee29 100644 --- a/src/main/java/io/papermc/fill/discord/DiscordNotifier.java +++ b/src/main/java/io/papermc/fill/discord/DiscordNotifier.java @@ -43,6 +43,7 @@ import io.papermc.fill.util.BuildPublishListener; import io.papermc.fill.util.discord.Components; import io.papermc.fill.util.discord.DiscordNotificationChannel; +import io.papermc.fill.util.discord.PrToMdHyperLinkUtil; import io.papermc.fill.util.git.GitRepository; import java.net.URI; import java.util.List; @@ -147,7 +148,7 @@ private Container createContent(final ProjectEntity project, final Version versi repository.name(), commit.sha() ), - commit.summary() + PrToMdHyperLinkUtil.hyperlinkTrailingPrMention(commit.summary(), repository.owner(), repository.name()) )).collect(Collectors.joining("\n")) )); }, switch (build.channel()) { diff --git a/src/main/java/io/papermc/fill/util/discord/PrToMdHyperLinkUtil.java b/src/main/java/io/papermc/fill/util/discord/PrToMdHyperLinkUtil.java new file mode 100644 index 0000000..1ad28a6 --- /dev/null +++ b/src/main/java/io/papermc/fill/util/discord/PrToMdHyperLinkUtil.java @@ -0,0 +1,32 @@ +package io.papermc.fill.util.discord; + +import java.util.Objects; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import org.jspecify.annotations.NullMarked; + +@NullMarked +public class PrToMdHyperLinkUtil { + private static final Pattern TRAILING_PR = Pattern.compile(" \\(#(\\d+)\\)\\z"); + + private PrToMdHyperLinkUtil() { + } + + + public static String hyperlinkTrailingPrMention(final String commitSummary, final String repoOwner, final String repoName) { + Objects.requireNonNull(commitSummary); + Objects.requireNonNull(repoOwner); + Objects.requireNonNull(repoName); + + if (commitSummary.isEmpty()) return commitSummary; + + final Matcher matcher = TRAILING_PR.matcher(commitSummary); + if (!matcher.find()) return commitSummary; + + final String prNumber = matcher.group(1); + + return matcher.replaceFirst( + String.format(" ([#%s](https://github.com/%s/%s/pull/%s))", + prNumber, repoOwner, repoName, prNumber)); + } +} diff --git a/src/test/java/io/papermc/fill/model/util/discord/PrToMdHyperLinkUtilTest.java b/src/test/java/io/papermc/fill/model/util/discord/PrToMdHyperLinkUtilTest.java new file mode 100644 index 0000000..e34ca0e --- /dev/null +++ b/src/test/java/io/papermc/fill/model/util/discord/PrToMdHyperLinkUtilTest.java @@ -0,0 +1,54 @@ +package io.papermc.fill.model.util.discord; + +import io.papermc.fill.util.discord.PrToMdHyperLinkUtil; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.CsvSource; +import org.junit.jupiter.params.provider.ValueSource; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + +class PrToMdHyperLinkUtilTest { + @ParameterizedTest + @ValueSource(strings = { + "no mention", + "(#36) not trailing", + "missing space(#72)", + "not in brackets #72", + "" + }) + void returnsUnchangedForInvalidCases(String commitSummary) { + String actual = PrToMdHyperLinkUtil.hyperlinkTrailingPrMention(commitSummary, "myOrg", "myRepo"); + assertEquals(commitSummary, actual); + } + + @ParameterizedTest + @CsvSource({ + "Rework broken xyz (#123), myOrg, myRepo, Rework broken xyz ([#123](https://github.com/myOrg/myRepo/pull/123))", + "Don't run player loot table for spectators (#11801), PaperMC, Paper, Don't run player loot table for spectators ([#11801](https://github.com/PaperMC/Paper/pull/11801))", + "feat: add new xyz API (#12), myOrg, myRepo, feat: add new xyz API ([#12](https://github.com/myOrg/myRepo/pull/12))", + "Fix (#1234) via xyz (#4125), myOrg, myRepo, Fix (#1234) via xyz ([#4125](https://github.com/myOrg/myRepo/pull/4125))" + }) + void replacesTrailingPrMentionWithMarkdownLink(String commitSummary, String repoOwner, String repoName, String expected) { + String actual = PrToMdHyperLinkUtil.hyperlinkTrailingPrMention(commitSummary, repoOwner, repoName); + assertEquals(expected, actual); + } + + @ParameterizedTest + @CsvSource({ + "commitSummary, null, myOrg, myRepo", + "repoOwner, working summary (#1), null, myRepo", + "repoName, working summary (#1), myOrg, null" + }) + @SuppressWarnings("DataFlowIssue") + void nullParameterThrowsNPE(String parameter, String commitSummary, String repoOwner, String repoName) { + assertThrows(NullPointerException.class, () -> + PrToMdHyperLinkUtil.hyperlinkTrailingPrMention( + "null".equals(commitSummary) ? null : commitSummary, + "null".equals(repoOwner) ? null : repoOwner, + "null".equals(repoName) ? null : repoName + ), + () -> "Expected NullPointerException for parameter " + parameter + ); + } +}