From b92a7cbde6238561752ad6c3ba2c9feb5465656a Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 13 May 2026 17:17:53 +0000 Subject: [PATCH 1/3] Initial plan From 9b0474e81f31893851c7c50182fc20e141e58d72 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 13 May 2026 17:22:42 +0000 Subject: [PATCH 2/3] Refuse merge requests when Release Please workflow is enabled Agent-Logs-Url: https://github.com/jekyll/jekyllbot/sessions/9f367c71-3e5e-44b6-a7b4-bd5121fc09d1 Co-authored-by: parkr <237985+parkr@users.noreply.github.com> --- chlog/merge_and_label.go | 69 +++++++++++++++++++++++++++++++---- chlog/merge_and_label_test.go | 46 +++++++++++++++++++++++ 2 files changed, 107 insertions(+), 8 deletions(-) diff --git a/chlog/merge_and_label.go b/chlog/merge_and_label.go index 233f515..cf95824 100644 --- a/chlog/merge_and_label.go +++ b/chlog/merge_and_label.go @@ -5,6 +5,7 @@ import ( "errors" "fmt" "log" + "net/http" "os" "regexp" "strings" @@ -19,6 +20,11 @@ import ( const changeSectionLabelNone = "none" +const ( + releasePleaseWorkflowPath = ".github/workflows/release-please.yml" + releasePleaseMergeReply = "This repository uses Release Please, so `@jekyllbot: merge` is disabled. Please follow the maintainer docs: https://jekyllrb.com/docs/maintaining/merging-a-pull-request/" +) + // changelogCategory is a changelog category, like "Site Enhancements" and such. type changelogCategory struct { Prefix, Slug, Section string @@ -182,14 +188,6 @@ func MergeAndLabel(context *ctx.Context, payload interface{}) error { return errors.New("commenter isn't allowed to merge") } - // Merge - commitMsg := fmt.Sprintf("Merge pull request %v", number) - _, _, mergeErr := context.GitHub.PullRequests.Merge(context.Context(), owner, repo, number, commitMsg, mergeOptions) - if mergeErr != nil { - return context.NewError("MergeAndLabel: error merging %s: %v", ref, mergeErr) - } - - // Delete branch repoInfo, _, getRepoErr := context.GitHub.PullRequests.Get(context.Context(), owner, repo, number) if getRepoErr != nil { return context.NewError("MergeAndLabel: error getting PR info %s: %v", ref, getRepoErr) @@ -199,6 +197,29 @@ func MergeAndLabel(context *ctx.Context, payload interface{}) error { return context.NewError("MergeAndLabel: tried to get PR, but couldn't. repoInfo was nil.") } + releasePleaseEnabled, releasePleaseErr := hasReleasePleaseWorkflowOnBranch( + context, + owner, + repo, + repoInfo.GetBase().GetRef(), + ) + if releasePleaseErr != nil { + return context.NewError("MergeAndLabel: error checking release-please workflow for %s: %v", ref, releasePleaseErr) + } + if releasePleaseEnabled { + if err := replyWithReleasePleaseMergeRefusal(context, owner, repo, number); err != nil { + return context.NewError("MergeAndLabel: error refusing merge request for %s: %v", ref, err) + } + return nil + } + + // Merge + commitMsg := fmt.Sprintf("Merge pull request %v", number) + _, _, mergeErr := context.GitHub.PullRequests.Merge(context.Context(), owner, repo, number, commitMsg, mergeOptions) + if mergeErr != nil { + return context.NewError("MergeAndLabel: error merging %s: %v", ref, mergeErr) + } + // Delete branch if deletableRef(repoInfo, owner) { wg.Add(1) @@ -258,6 +279,38 @@ func parseMergeRequestComment(commentBody string) (bool, string) { return true, normalizeLabel(label) } +func hasReleasePleaseWorkflowOnBranch(context *ctx.Context, owner, repo, branch string) (bool, error) { + if branch == "" { + return false, nil + } + + contents, _, _, err := context.GitHub.Repositories.GetContents( + context.Context(), + owner, + repo, + releasePleaseWorkflowPath, + &github.RepositoryContentGetOptions{Ref: "heads/" + branch}, + ) + if err != nil { + if ghErr, ok := err.(*github.ErrorResponse); ok && ghErr.Response != nil && ghErr.Response.StatusCode == http.StatusNotFound { + return false, nil + } + return false, err + } + return contents != nil, nil +} + +func replyWithReleasePleaseMergeRefusal(context *ctx.Context, owner, repo string, number int) error { + _, _, err := context.GitHub.Issues.CreateComment( + context.Context(), + owner, + repo, + number, + &github.IssueComment{Body: github.String(releasePleaseMergeReply)}, + ) + return err +} + func downcaseAndHyphenize(label string) string { return strings.Replace(strings.ToLower(label), " ", "-", -1) } diff --git a/chlog/merge_and_label_test.go b/chlog/merge_and_label_test.go index 3c094c5..ab31b2f 100644 --- a/chlog/merge_and_label_test.go +++ b/chlog/merge_and_label_test.go @@ -1,7 +1,11 @@ package chlog import ( + "fmt" "io/ioutil" + "net/http" + "net/http/httptest" + "net/url" "testing" "github.com/google/go-github/v73/github" @@ -241,3 +245,45 @@ func TestAddMergeReference(t *testing.T) { historyFile = addMergeReference(string(jekyllHistory), "Development Fixes", "A marvelous change.", 41526) assert.Contains(t, historyFile, "* A marvelous change. (#41526)\n\n### Site Enhancements") } + +func TestHasReleasePleaseWorkflowOnBranch(t *testing.T) { + server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + assert.Equal(t, "/repos/owner-login/foo/contents/.github/workflows/release-please.yml", r.URL.Path) + assert.Equal(t, "heads/main", r.URL.Query().Get("ref")) + fmt.Fprint(w, `{"name":"release-please.yml","path":".github/workflows/release-please.yml","sha":"abc123","content":"","encoding":"base64"}`) + })) + defer server.Close() + + client := github.NewClient(nil) + baseURL, err := url.Parse(server.URL + "/") + assert.NoError(t, err) + client.BaseURL = baseURL + client.UploadURL = baseURL + + context := ctx.NewTestContext() + context.GitHub = client + + enabled, err := hasReleasePleaseWorkflowOnBranch(context, "owner-login", "foo", "main") + assert.NoError(t, err) + assert.True(t, enabled) +} + +func TestHasReleasePleaseWorkflowOnBranchNotFound(t *testing.T) { + server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + http.NotFound(w, r) + })) + defer server.Close() + + client := github.NewClient(nil) + baseURL, err := url.Parse(server.URL + "/") + assert.NoError(t, err) + client.BaseURL = baseURL + client.UploadURL = baseURL + + context := ctx.NewTestContext() + context.GitHub = client + + enabled, err := hasReleasePleaseWorkflowOnBranch(context, "owner-login", "foo", "main") + assert.NoError(t, err) + assert.False(t, enabled) +} From 527018fe570053fe533e122eabea34be5670d14f Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 13 May 2026 17:27:52 +0000 Subject: [PATCH 3/3] Polish release-please merge refusal checks and tests Agent-Logs-Url: https://github.com/jekyll/jekyllbot/sessions/9f367c71-3e5e-44b6-a7b4-bd5121fc09d1 Co-authored-by: parkr <237985+parkr@users.noreply.github.com> --- chlog/merge_and_label.go | 26 +++++++++++++------------- chlog/merge_and_label_test.go | 8 ++++---- 2 files changed, 17 insertions(+), 17 deletions(-) diff --git a/chlog/merge_and_label.go b/chlog/merge_and_label.go index cf95824..91d74ea 100644 --- a/chlog/merge_and_label.go +++ b/chlog/merge_and_label.go @@ -188,20 +188,20 @@ func MergeAndLabel(context *ctx.Context, payload interface{}) error { return errors.New("commenter isn't allowed to merge") } - repoInfo, _, getRepoErr := context.GitHub.PullRequests.Get(context.Context(), owner, repo, number) - if getRepoErr != nil { - return context.NewError("MergeAndLabel: error getting PR info %s: %v", ref, getRepoErr) + prInfo, _, getPRErr := context.GitHub.PullRequests.Get(context.Context(), owner, repo, number) + if getPRErr != nil { + return context.NewError("MergeAndLabel: error getting PR info %s: %v", ref, getPRErr) } - if repoInfo == nil { - return context.NewError("MergeAndLabel: tried to get PR, but couldn't. repoInfo was nil.") + if prInfo == nil { + return context.NewError("MergeAndLabel: tried to get PR, but couldn't. prInfo was nil.") } releasePleaseEnabled, releasePleaseErr := hasReleasePleaseWorkflowOnBranch( context, owner, repo, - repoInfo.GetBase().GetRef(), + prInfo.GetBase().GetRef(), ) if releasePleaseErr != nil { return context.NewError("MergeAndLabel: error checking release-please workflow for %s: %v", ref, releasePleaseErr) @@ -221,10 +221,10 @@ func MergeAndLabel(context *ctx.Context, payload interface{}) error { } // Delete branch - if deletableRef(repoInfo, owner) { + if deletableRef(prInfo, owner) { wg.Add(1) go func() { - ref := fmt.Sprintf("heads/%s", *repoInfo.Head.Ref) + ref := fmt.Sprintf("heads/%s", *prInfo.Head.Ref) _, deleteBranchErr := context.GitHub.Git.DeleteRef(context.Context(), owner, repo, ref) if deleteBranchErr != nil { fmt.Printf("MergeAndLabel: error deleting branch %v\n", mergeErr) @@ -248,7 +248,7 @@ func MergeAndLabel(context *ctx.Context, payload interface{}) error { historyFileContents, historySHA := getHistoryContents(context, owner, repo) // Add merge reference to history - newHistoryFileContents := addMergeReference(historyFileContents, req.ChangeSectionLabel, *repoInfo.Title, number) + newHistoryFileContents := addMergeReference(historyFileContents, req.ChangeSectionLabel, *prInfo.Title, number) // Commit change to History.markdown commitErr := commitHistoryFile(context, historySHA, owner, repo, number, newHistoryFileContents) @@ -284,20 +284,20 @@ func hasReleasePleaseWorkflowOnBranch(context *ctx.Context, owner, repo, branch return false, nil } - contents, _, _, err := context.GitHub.Repositories.GetContents( + _, _, response, err := context.GitHub.Repositories.GetContents( context.Context(), owner, repo, releasePleaseWorkflowPath, - &github.RepositoryContentGetOptions{Ref: "heads/" + branch}, + &github.RepositoryContentGetOptions{Ref: branch}, ) if err != nil { - if ghErr, ok := err.(*github.ErrorResponse); ok && ghErr.Response != nil && ghErr.Response.StatusCode == http.StatusNotFound { + if response != nil && response.StatusCode == http.StatusNotFound { return false, nil } return false, err } - return contents != nil, nil + return true, nil } func replyWithReleasePleaseMergeRefusal(context *ctx.Context, owner, repo string, number int) error { diff --git a/chlog/merge_and_label_test.go b/chlog/merge_and_label_test.go index ab31b2f..ca27e0d 100644 --- a/chlog/merge_and_label_test.go +++ b/chlog/merge_and_label_test.go @@ -2,10 +2,10 @@ package chlog import ( "fmt" - "io/ioutil" "net/http" "net/http/httptest" "net/url" + "os" "testing" "github.com/google/go-github/v73/github" @@ -215,7 +215,7 @@ func TestParseMergeRequestComment(t *testing.T) { } func TestBase64Decode(t *testing.T) { - encoded, err := ioutil.ReadFile("history_contents.enc") + encoded, err := os.ReadFile("history_contents.enc") assert.NoError(t, err) decoded := base64Decode(string(encoded)) assert.Contains(t, decoded, "### Minor Enhancements") @@ -240,7 +240,7 @@ func TestAddMergeReference(t *testing.T) { "Development Fixes", "Another great change for !!!!!!!", 1) assert.Equal(t, "## HEAD\n\n### Development Fixes\n\n * Some great change (#1)\n * Another great change for <science>!!!!!!! (#1)\n", historyFile) - jekyllHistory, err := ioutil.ReadFile("History.markdown") + jekyllHistory, err := os.ReadFile("History.markdown") assert.NoError(t, err) historyFile = addMergeReference(string(jekyllHistory), "Development Fixes", "A marvelous change.", 41526) assert.Contains(t, historyFile, "* A marvelous change. (#41526)\n\n### Site Enhancements") @@ -249,7 +249,7 @@ func TestAddMergeReference(t *testing.T) { func TestHasReleasePleaseWorkflowOnBranch(t *testing.T) { server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { assert.Equal(t, "/repos/owner-login/foo/contents/.github/workflows/release-please.yml", r.URL.Path) - assert.Equal(t, "heads/main", r.URL.Query().Get("ref")) + assert.Equal(t, "main", r.URL.Query().Get("ref")) fmt.Fprint(w, `{"name":"release-please.yml","path":".github/workflows/release-please.yml","sha":"abc123","content":"","encoding":"base64"}`) })) defer server.Close()