From a6e5ee940f6495cbab38276c730908f9812e5478 Mon Sep 17 00:00:00 2001 From: Divya Jain Date: Fri, 23 May 2025 00:02:04 +0530 Subject: [PATCH 1/2] patchutil: add ability to add trailers to commit message of a format-patch --- patchutil/patchutil.go | 80 +++++++++++ patchutil/patchutil_test.go | 279 ++++++++++++++++++++++++++++++++++++ 2 files changed, 359 insertions(+) diff --git a/patchutil/patchutil.go b/patchutil/patchutil.go index 6920f97..f33e070 100644 --- a/patchutil/patchutil.go +++ b/patchutil/patchutil.go @@ -316,3 +316,83 @@ func AsNiceDiff(patch, targetBranch string) types.NiceDiff { return nd } + +func extractTrailersFromCommitMessage(commitMsg string) (string, []string) { + lines := strings.Split(strings.TrimSpace(commitMsg), "\n") + if len(lines) == 0 { + return "", []string{} + } + + trailerStart := len(lines) + trailers := []string{} + + // Loop over commit message lines in reverse order to find trailers + for i := len(lines) - 1; i >= 0; i-- { + line := strings.TrimSpace(lines[i]) + if line == "" { + continue + } + + // Check if line looks like a trailer (has a colon) + if colonIndex := strings.Index(line, ":"); colonIndex <= 0 { + // Not a trailer, stop scanning + break + } + + trailers = append([]string{line}, trailers...) + trailerStart = i + } + + // Extract message body (everything before trailers) + messageBody := "" + if trailerStart > 0 { + bodyLines := lines[:trailerStart] + for len(bodyLines) > 0 && strings.TrimSpace(bodyLines[len(bodyLines)-1]) == "" { + bodyLines = bodyLines[:len(bodyLines)-1] + } + messageBody = strings.Join(bodyLines, "\n") + } + + return messageBody, trailers +} + +func AddCommitMessageTrailers(patch string, trailers []string) string { + re := regexp.MustCompile(`(?s)\n(Subject: )(.*?)(---\n|\ndiff --git )`) + + allMatches := re.FindAllStringSubmatch(patch, -1) + if len(allMatches) == 0 { + return patch + } + + result := patch + for _, matches := range allMatches { + if len(matches) < 4 { + continue + } + + commitMsg := matches[2] + messageBody, existingTrailers := extractTrailersFromCommitMessage(commitMsg) + + allTrailers := append(existingTrailers, trailers...) + + var newCommitMsg string + if messageBody != "" { + newCommitMsg = messageBody + if len(allTrailers) > 0 { + newCommitMsg += "\n\n" + newCommitMsg += strings.Join(allTrailers, "\n") + newCommitMsg += "\n" + } + } else { + if len(allTrailers) > 0 { + newCommitMsg = "\n" + strings.Join(allTrailers, "\n") + "\n" + } else { + newCommitMsg = "" + } + } + + result = strings.Replace(result, matches[0], "\n"+matches[1]+newCommitMsg+matches[3], 1) + } + + return result +} diff --git a/patchutil/patchutil_test.go b/patchutil/patchutil_test.go index d0d8c1f..a735b46 100644 --- a/patchutil/patchutil_test.go +++ b/patchutil/patchutil_test.go @@ -322,3 +322,282 @@ content }) } } + +func TestAddCommmitMessageTrailers(t *testing.T) { + tests := []struct { + name string + input string + trailers []string + expected string + }{ + { + name: "Single patch with existing trailers", + input: `From 3c5035488318164b81f60fe3adcd6c9199d76331 Mon Sep 17 00:00:00 2001 +From: Author +Date: Wed, 16 Apr 2025 11:01:00 +0300 +Subject: [PATCH] Example patch + +more message body + +Signed-off-by: Author +Reviewed-by: Reviewer +--- + file.txt | 2 +- + 1 file changed, 1 insertions(+), 1 deletions(-) + +diff --git a/file.txt b/file.txt +index 123456..789012 100644 +--- a/file.txt ++++ b/file.txt +@@ -1 +1 @@ +-old content ++new content +-- +2.48.1`, + trailers: []string{`Pull-Id: Author `, `Merge-Request: 123`}, + expected: `From 3c5035488318164b81f60fe3adcd6c9199d76331 Mon Sep 17 00:00:00 2001 +From: Author +Date: Wed, 16 Apr 2025 11:01:00 +0300 +Subject: [PATCH] Example patch + +more message body + +Signed-off-by: Author +Reviewed-by: Reviewer +Pull-Id: Author +Merge-Request: 123 +--- + file.txt | 2 +- + 1 file changed, 1 insertions(+), 1 deletions(-) + +diff --git a/file.txt b/file.txt +index 123456..789012 100644 +--- a/file.txt ++++ b/file.txt +@@ -1 +1 @@ +-old content ++new content +-- +2.48.1`, + }, + { + name: "Single patch with existing trailers and no stat", + input: `From 3c5035488318164b81f60fe3adcd6c9199d76331 Mon Sep 17 00:00:00 2001 +From: Author +Date: Wed, 16 Apr 2025 11:01:00 +0300 +Subject: [PATCH] Example patch + +Signed-off-by: Author +Reviewed-by: Reviewer + +diff --git a/file.txt b/file.txt +index 123456..789012 100644 +--- a/file.txt ++++ b/file.txt +@@ -1 +1 @@ +-old content ++new content +-- +2.48.1`, + trailers: []string{`Pull-Id: Author `, `Merge-Request: 123`}, + expected: `From 3c5035488318164b81f60fe3adcd6c9199d76331 Mon Sep 17 00:00:00 2001 +From: Author +Date: Wed, 16 Apr 2025 11:01:00 +0300 +Subject: [PATCH] Example patch + +Signed-off-by: Author +Reviewed-by: Reviewer +Pull-Id: Author +Merge-Request: 123 + +diff --git a/file.txt b/file.txt +index 123456..789012 100644 +--- a/file.txt ++++ b/file.txt +@@ -1 +1 @@ +-old content ++new content +-- +2.48.1`, + }, + { + name: "Multiple patches with existing trailers", + input: `From 3c5035488318164b81f60fe3adcd6c9199d76331 Mon Sep 17 00:00:00 2001 +From: Author +Date: Wed, 16 Apr 2025 11:01:00 +0300 +Subject: [PATCH 1/2] First patch + +more message body + +--- + file.txt | 2 +- + 1 file changed, 1 insertions(+), 1 deletions(-) + +diff --git a/file1.txt b/file1.txt +index 123456..789012 100644 +--- a/file1.txt ++++ b/file1.txt +@@ -1 +1 @@ +-old content ++new content +-- +2.48.1 +From a9529f3b3a653329a5268f0f4067225480207e3c Mon Sep 17 00:00:00 2001 +From: Author +Date: Wed, 16 Apr 2025 11:03:11 +0300 +Subject: [PATCH 2/2] Second patch + +more message body + +Signed-off-by: Author +Reviewed-by: Reviewer +--- + file.txt | 2 +- + 1 file changed, 1 insertions(+), 1 deletions(-) + +diff --git a/file2.txt b/file2.txt +index abcdef..ghijkl 100644 +--- a/file2.txt ++++ b/file2.txt +@@ -1 +1 @@ +-foo bar ++baz qux +-- +2.48.1`, + trailers: []string{`Pull-Id: Author `, `Merge-Request: 123`}, + expected: `From 3c5035488318164b81f60fe3adcd6c9199d76331 Mon Sep 17 00:00:00 2001 +From: Author +Date: Wed, 16 Apr 2025 11:01:00 +0300 +Subject: [PATCH 1/2] First patch + +more message body + +Pull-Id: Author +Merge-Request: 123 +--- + file.txt | 2 +- + 1 file changed, 1 insertions(+), 1 deletions(-) + +diff --git a/file1.txt b/file1.txt +index 123456..789012 100644 +--- a/file1.txt ++++ b/file1.txt +@@ -1 +1 @@ +-old content ++new content +-- +2.48.1 +From a9529f3b3a653329a5268f0f4067225480207e3c Mon Sep 17 00:00:00 2001 +From: Author +Date: Wed, 16 Apr 2025 11:03:11 +0300 +Subject: [PATCH 2/2] Second patch + +more message body + +Signed-off-by: Author +Reviewed-by: Reviewer +Pull-Id: Author +Merge-Request: 123 +--- + file.txt | 2 +- + 1 file changed, 1 insertions(+), 1 deletions(-) + +diff --git a/file2.txt b/file2.txt +index abcdef..ghijkl 100644 +--- a/file2.txt ++++ b/file2.txt +@@ -1 +1 @@ +-foo bar ++baz qux +-- +2.48.1`, + }, + { + name: "Multiple patches with existing trailers and no stat", + input: `From 3c5035488318164b81f60fe3adcd6c9199d76331 Mon Sep 17 00:00:00 2001 +From: Author +Date: Wed, 16 Apr 2025 11:01:00 +0300 +Subject: [PATCH 1/2] First patch + +with long message body + +Signed-off-by: Author + +diff --git a/file1.txt b/file1.txt +index 123456..789012 100644 +--- a/file1.txt ++++ b/file1.txt +@@ -1 +1 @@ +-old content ++new content +-- +2.48.1 +From a9529f3b3a653329a5268f0f4067225480207e3c Mon Sep 17 00:00:00 2001 +From: Author +Date: Wed, 16 Apr 2025 11:03:11 +0300 +Subject: [PATCH 2/2] Second patch + +Signed-off-by: Author +Reviewed-by: Reviewer + +diff --git a/file2.txt b/file2.txt +index abcdef..ghijkl 100644 +--- a/file2.txt ++++ b/file2.txt +@@ -1 +1 @@ +-foo bar ++baz qux +-- +2.48.1`, + trailers: []string{`Pull-Id: Author `, `Merge-Request: 123`}, + expected: `From 3c5035488318164b81f60fe3adcd6c9199d76331 Mon Sep 17 00:00:00 2001 +From: Author +Date: Wed, 16 Apr 2025 11:01:00 +0300 +Subject: [PATCH 1/2] First patch + +with long message body + +Signed-off-by: Author +Pull-Id: Author +Merge-Request: 123 + +diff --git a/file1.txt b/file1.txt +index 123456..789012 100644 +--- a/file1.txt ++++ b/file1.txt +@@ -1 +1 @@ +-old content ++new content +-- +2.48.1 +From a9529f3b3a653329a5268f0f4067225480207e3c Mon Sep 17 00:00:00 2001 +From: Author +Date: Wed, 16 Apr 2025 11:03:11 +0300 +Subject: [PATCH 2/2] Second patch + +Signed-off-by: Author +Reviewed-by: Reviewer +Pull-Id: Author +Merge-Request: 123 + +diff --git a/file2.txt b/file2.txt +index abcdef..ghijkl 100644 +--- a/file2.txt ++++ b/file2.txt +@@ -1 +1 @@ +-foo bar ++baz qux +-- +2.48.1`, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + result := AddCommitMessageTrailers(tt.input, tt.trailers) + if result != tt.expected { + t.Errorf("Got:\n========\n%v\n========\nExpected:\n========\n%v\n========\n", result, tt.expected) + } + }) + } +} -- 2.47.2 From 7d05c4f366318e1b7c701527c6c1d9f79c074650 Mon Sep 17 00:00:00 2001 From: Divya Jain Date: Thu, 15 May 2025 23:26:00 +0530 Subject: [PATCH 2/2] appview: state: add Pull-id and Merged-by commit trailers --- appview/pulls/pulls.go | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/appview/pulls/pulls.go b/appview/pulls/pulls.go index ab80aff..a2f64bc 100644 --- a/appview/pulls/pulls.go +++ b/appview/pulls/pulls.go @@ -1915,8 +1915,18 @@ func (s *Pulls) MergePull(w http.ResponseWriter, r *http.Request) { return } + actor := s.oauth.GetUser(r) // no need to check for nil as this is an authenticated request + + trailers := []string{ + fmt.Sprintf("Pull-id: %s", pull.PullAt()), + fmt.Sprintf("Merged-by: %s", actor.Did), + } + + pullBody := pull.Body + "\n\n" + strings.Join(trailers, "\n") + patch = patchutil.AddCommitMessageTrailers(patch, trailers) + // Merge the pull request - resp, err := ksClient.Merge([]byte(patch), f.OwnerDid(), f.RepoName, pull.TargetBranch, pull.Title, pull.Body, ident.Handle.String(), email.Address) + resp, err := ksClient.Merge([]byte(patch), f.OwnerDid(), f.RepoName, pull.TargetBranch, pull.Title, pullBody, ident.Handle.String(), email.Address) if err != nil { log.Printf("failed to merge pull request: %s", err) s.pages.Notice(w, "pull-merge-error", "Failed to merge pull request. Try again later.") -- 2.47.2