Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
79 changes: 66 additions & 13 deletions chlog/merge_and_label.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"errors"
"fmt"
"log"
"net/http"
"os"
"regexp"
"strings"
Expand All @@ -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
Expand Down Expand Up @@ -182,6 +188,31 @@ func MergeAndLabel(context *ctx.Context, payload interface{}) error {
return errors.New("commenter isn't allowed to merge")
}

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 prInfo == nil {
return context.NewError("MergeAndLabel: tried to get PR, but couldn't. prInfo was nil.")
}

releasePleaseEnabled, releasePleaseErr := hasReleasePleaseWorkflowOnBranch(
context,
owner,
repo,
prInfo.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)
Expand All @@ -190,20 +221,10 @@ func MergeAndLabel(context *ctx.Context, payload interface{}) error {
}

// 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)
}

if repoInfo == nil {
return context.NewError("MergeAndLabel: tried to get PR, but couldn't. repoInfo was nil.")
}

// 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)
Expand All @@ -227,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)
Expand Down Expand Up @@ -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
}

_, _, response, err := context.GitHub.Repositories.GetContents(
context.Context(),
owner,
repo,
releasePleaseWorkflowPath,
&github.RepositoryContentGetOptions{Ref: branch},
)
if err != nil {
if response != nil && response.StatusCode == http.StatusNotFound {
return false, nil
}
return false, err
}
return true, 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)
}
Expand Down
52 changes: 49 additions & 3 deletions chlog/merge_and_label_test.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
package chlog

import (
"io/ioutil"
"fmt"
"net/http"
"net/http/httptest"
"net/url"
"os"
"testing"

"github.com/google/go-github/v73/github"
Expand Down Expand Up @@ -211,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")
Expand All @@ -236,8 +240,50 @@ func TestAddMergeReference(t *testing.T) {
"Development Fixes", "Another great change for <science>!!!!!!!", 1)
assert.Equal(t, "## HEAD\n\n### Development Fixes\n\n * Some great change (#1)\n * Another great change for &lt;science&gt;!!!!!!! (#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")
}

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, "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)
}