From e1e7299bd9a07c568a857ea346f446ffa67f8809 Mon Sep 17 00:00:00 2001
From: Kemal Zebari <60799661+kemzeb@users.noreply.github.com>
Date: Mon, 23 Sep 2024 20:38:08 -0700
Subject: [PATCH] Truncate commit message during Discord webhook push events
 (#31970)

Resolves #31668.

(cherry picked from commit aadbe0488f454b9f7f5a56765f4530f9d1e2c6ec)
---
 services/webhook/discord.go      | 11 +++++++++--
 services/webhook/discord_test.go | 14 ++++++++++++++
 services/webhook/general_test.go | 10 +++++++++-
 3 files changed, 32 insertions(+), 3 deletions(-)

diff --git a/services/webhook/discord.go b/services/webhook/discord.go
index b342b45690..af1dd79927 100644
--- a/services/webhook/discord.go
+++ b/services/webhook/discord.go
@@ -12,6 +12,7 @@ import (
 	"net/url"
 	"strconv"
 	"strings"
+	"unicode/utf8"
 
 	webhook_model "code.gitea.io/gitea/models/webhook"
 	"code.gitea.io/gitea/modules/git"
@@ -179,8 +180,14 @@ func (d discordConvertor) Push(p *api.PushPayload) (DiscordPayload, error) {
 	var text string
 	// for each commit, generate attachment text
 	for i, commit := range p.Commits {
-		text += fmt.Sprintf("[%s](%s) %s - %s", commit.ID[:7], commit.URL,
-			strings.TrimRight(commit.Message, "\r\n"), commit.Author.Name)
+		// limit the commit message display to just the summary, otherwise it would be hard to read
+		message := strings.TrimRight(strings.SplitN(commit.Message, "\n", 1)[0], "\r")
+
+		// a limit of 50 is set because GitHub does the same
+		if utf8.RuneCountInString(message) > 50 {
+			message = fmt.Sprintf("%.47s...", message)
+		}
+		text += fmt.Sprintf("[%s](%s) %s - %s", commit.ID[:7], commit.URL, message, commit.Author.Name)
 		// add linebreak to each commit but the last
 		if i < len(p.Commits)-1 {
 			text += "\n"
diff --git a/services/webhook/discord_test.go b/services/webhook/discord_test.go
index 73be143f46..cc5ec8a3e0 100644
--- a/services/webhook/discord_test.go
+++ b/services/webhook/discord_test.go
@@ -80,6 +80,20 @@ func TestDiscordPayload(t *testing.T) {
 		assert.Equal(t, p.Sender.AvatarURL, pl.Embeds[0].Author.IconURL)
 	})
 
+	t.Run("PushWithLongCommitMessage", func(t *testing.T) {
+		p := pushTestMultilineCommitMessagePayload()
+
+		pl, err := dc.Push(p)
+		require.NoError(t, err)
+
+		assert.Len(t, pl.Embeds, 1)
+		assert.Equal(t, "[test/repo:test] 2 new commits", pl.Embeds[0].Title)
+		assert.Equal(t, "[2020558](http://localhost:3000/test/repo/commit/2020558fe2e34debb818a514715839cabd25e778) This is a commit summary ⚠️⚠️⚠️⚠️ containing 你好... - user1\n[2020558](http://localhost:3000/test/repo/commit/2020558fe2e34debb818a514715839cabd25e778) This is a commit summary ⚠️⚠️⚠️⚠️ containing 你好... - user1", pl.Embeds[0].Description)
+		assert.Equal(t, p.Sender.UserName, pl.Embeds[0].Author.Name)
+		assert.Equal(t, setting.AppURL+p.Sender.UserName, pl.Embeds[0].Author.URL)
+		assert.Equal(t, p.Sender.AvatarURL, pl.Embeds[0].Author.IconURL)
+	})
+
 	t.Run("Issue", func(t *testing.T) {
 		p := issueTestPayload()
 
diff --git a/services/webhook/general_test.go b/services/webhook/general_test.go
index 5b9bfdc1b2..2d991f8300 100644
--- a/services/webhook/general_test.go
+++ b/services/webhook/general_test.go
@@ -64,9 +64,17 @@ func forkTestPayload() *api.ForkPayload {
 }
 
 func pushTestPayload() *api.PushPayload {
+	return pushTestPayloadWithCommitMessage("commit message")
+}
+
+func pushTestMultilineCommitMessagePayload() *api.PushPayload {
+	return pushTestPayloadWithCommitMessage("This is a commit summary ⚠️⚠️⚠️⚠️ containing 你好 ⚠️⚠️️\n\nThis is the message body.")
+}
+
+func pushTestPayloadWithCommitMessage(message string) *api.PushPayload {
 	commit := &api.PayloadCommit{
 		ID:      "2020558fe2e34debb818a514715839cabd25e778",
-		Message: "commit message",
+		Message: message,
 		URL:     "http://localhost:3000/test/repo/commit/2020558fe2e34debb818a514715839cabd25e778",
 		Author: &api.PayloadUser{
 			Name:     "user1",