Skip to content

Commit

Permalink
feat: add matrix subcommand + move bot to check
Browse files Browse the repository at this point in the history
  • Loading branch information
aeddi committed Nov 27, 2024
1 parent d19edbe commit 00bc576
Show file tree
Hide file tree
Showing 12 changed files with 553 additions and 126 deletions.
49 changes: 27 additions & 22 deletions contribs/github-bot/bot.go → contribs/github-bot/check.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,31 @@ import (
"github.com/gnolang/gno/contribs/github-bot/internal/logger"
p "github.com/gnolang/gno/contribs/github-bot/internal/params"
"github.com/gnolang/gno/contribs/github-bot/internal/utils"
"github.com/gnolang/gno/tm2/pkg/commands"
"github.com/google/go-github/v64/github"
"github.com/sethvargo/go-githubactions"
"github.com/xlab/treeprint"
)

func execBot(params *p.Params) error {
func newCheckCmd() *commands.Command {
params := &p.Params{}

return commands.NewCommand(
commands.Metadata{
Name: "check",
ShortUsage: "github-bot check [flags]",
ShortHelp: "checks requirements for a pull request to be merged",
LongHelp: "This tool checks if the requirements for a pull request to be merged are satisfied (defined in config.go) and displays PR status checks accordingly.\nA valid GitHub Token must be provided by setting the GITHUB_TOKEN environment variable.",
},
params,
func(_ context.Context, _ []string) error {
params.ValidateFlags()
return execCheck(params)
},

Check warning on line 35 in contribs/github-bot/check.go

View check run for this annotation

Codecov / codecov/patch

contribs/github-bot/check.go#L21-L35

Added lines #L21 - L35 were not covered by tests
)
}

func execCheck(params *p.Params) error {
// Create context with timeout if specified in the parameters.
ctx := context.Background()
if params.Timeout > 0 {
Expand Down Expand Up @@ -51,27 +70,9 @@ func execBot(params *p.Params) error {

// If requested, retrieve all open pull requests.
if params.PRAll {
opts := &github.PullRequestListOptions{
State: "open",
Sort: "updated",
Direction: "desc",
ListOptions: github.ListOptions{
PerPage: client.PageSize,
},
}

for {
prsPage, response, err := gh.Client.PullRequests.List(gh.Ctx, gh.Owner, gh.Repo, opts)
if err != nil {
return fmt.Errorf("unable to retrieve all open pull requests: %w", err)
}

prs = append(prs, prsPage...)

if response.NextPage == 0 {
break
}
opts.Page = response.NextPage
prs, err = gh.ListPR(utils.PRStateOpen)
if err != nil {
return fmt.Errorf("unable to list all PR: %w", err)
}
} else {
// Otherwise, retrieve only specified pull request(s)
Expand All @@ -86,6 +87,10 @@ func execBot(params *p.Params) error {
}
}

return processPRList(gh, prs)

Check warning on line 90 in contribs/github-bot/check.go

View check run for this annotation

Codecov / codecov/patch

contribs/github-bot/check.go#L90

Added line #L90 was not covered by tests
}

func processPRList(gh *client.GitHub, prs []*github.PullRequest) error {
if len(prs) > 1 {
prNums := make([]int, len(prs))
for i, pr := range prs {
Expand Down
30 changes: 7 additions & 23 deletions contribs/github-bot/comment.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
"text/template"

"github.com/gnolang/gno/contribs/github-bot/internal/client"
"github.com/gnolang/gno/contribs/github-bot/internal/utils"

"github.com/google/go-github/v64/github"
"github.com/sethvargo/go-githubactions"
Expand Down Expand Up @@ -71,23 +72,6 @@ func getCommentManualChecks(commentBody string) map[string]manualCheckDetails {
return checks
}

// Recursively search for nested values using the keys provided.
func indexMap(m map[string]any, keys ...string) any {
if len(keys) == 0 {
return m
}

if val, ok := m[keys[0]]; ok {
if keys = keys[1:]; len(keys) == 0 {
return val
}
subMap, _ := val.(map[string]any)
return indexMap(subMap, keys...)
}

return nil
}

// handleCommentUpdate checks if:
// - the current run was triggered by GitHub Actions
// - the triggering event is an edit of the bot comment
Expand All @@ -96,7 +80,7 @@ func indexMap(m map[string]any, keys ...string) any {
// - the actor / comment editor has permission to modify this checkbox (or restore it)
func handleCommentUpdate(gh *client.GitHub, actionCtx *githubactions.GitHubContext) error {
// Ignore if it's not a comment related event.
if actionCtx.EventName != "issue_comment" {
if actionCtx.EventName != utils.EventIssueComment {
gh.Logger.Debugf("Event is not issue comment related (%s)", actionCtx.EventName)
return nil
}
Expand All @@ -123,7 +107,7 @@ func handleCommentUpdate(gh *client.GitHub, actionCtx *githubactions.GitHubConte
}

// Get login of the author of the edited comment.
login, ok := indexMap(actionCtx.Event, "comment", "user", "login").(string)
login, ok := utils.IndexMap(actionCtx.Event, "comment", "user", "login").(string)
if !ok {
return errors.New("unable to get comment user login on issue comment event")
}
Expand All @@ -134,19 +118,19 @@ func handleCommentUpdate(gh *client.GitHub, actionCtx *githubactions.GitHubConte
}

// Get comment updated body.
current, ok := indexMap(actionCtx.Event, "comment", "body").(string)
current, ok := utils.IndexMap(actionCtx.Event, "comment", "body").(string)
if !ok {
return errors.New("unable to get comment body on issue comment event")
}

// Get comment previous body.
previous, ok := indexMap(actionCtx.Event, "changes", "body", "from").(string)
previous, ok := utils.IndexMap(actionCtx.Event, "changes", "body", "from").(string)
if !ok {
return errors.New("unable to get changes body content on issue comment event")
}

// Get PR number from GitHub Actions context.
prNum, ok := indexMap(actionCtx.Event, "issue", "number").(float64)
prNum, ok := utils.IndexMap(actionCtx.Event, "issue", "number").(float64)
if !ok || prNum <= 0 {
return errors.New("unable to get issue number on issue comment event")
}
Expand Down Expand Up @@ -200,7 +184,7 @@ func handleCommentUpdate(gh *client.GitHub, actionCtx *githubactions.GitHubConte
}

// This regex capture only the line of the current check.
specificManualCheck := regexp.MustCompile(fmt.Sprintf(`(?m:^- \[%s\] %s.*$)`, currentChecks[key].status, key))
specificManualCheck := regexp.MustCompile(fmt.Sprintf(`(?m:^- \[%s\] %s.*$)`, currentChecks[key].status, regexp.QuoteMeta(key)))

// If the box is checked, append the username of the user who checked it.
if strings.TrimSpace(currentChecks[key].status) == "x" {
Expand Down
38 changes: 1 addition & 37 deletions contribs/github-bot/comment_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -71,42 +71,6 @@ func TestGeneratedComment(t *testing.T) {
}
}

func TestIndexMap(t *testing.T) {
t.Parallel()

m := map[string]any{
"Key1": map[string]any{
"Key2": map[string]any{
"Key3": 1,
},
},
}

test := indexMap(m)
assert.NotNil(t, test, "should return m")
_, ok := test.(map[string]any)
assert.True(t, ok, "returned m should be a map")

test = indexMap(m, "Key1")
assert.NotNil(t, test, "should return Key1 value")
_, ok = test.(map[string]any)
assert.True(t, ok, "Key1 value type should be a map")

test = indexMap(m, "Key1", "Key2")
assert.NotNil(t, test, "should return Key2 value")
_, ok = test.(map[string]any)
assert.True(t, ok, "Key2 value type should be a map")

test = indexMap(m, "Key1", "Key2", "Key3")
assert.NotNil(t, test, "should return Key3 value")
val, ok := test.(int)
assert.True(t, ok, "Key3 value type should be an int")
assert.Equal(t, 1, val, "Key3 value should be a 1")

test = indexMap(m, "Key1", "Key2", "Key3", "Key4")
assert.Nil(t, test, "Key4 value should not exist")
}

func setValue(t *testing.T, m map[string]any, value any, keys ...string) map[string]any {
t.Helper()

Expand Down Expand Up @@ -146,7 +110,7 @@ func TestCommentUpdateHandler(t *testing.T) {

// Exit without error because EventName is empty
assert.NoError(t, handleCommentUpdate(gh, actionCtx))
actionCtx.EventName = "issue_comment"
actionCtx.EventName = utils.EventIssueComment

// Exit with error because Event.action is not set
assert.Error(t, handleCommentUpdate(gh, actionCtx))
Expand Down
30 changes: 30 additions & 0 deletions contribs/github-bot/internal/client/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -237,6 +237,36 @@ func (gh *GitHub) ListPRReviews(prNum int) ([]*github.PullRequestReview, error)
return allReviews, nil
}

// ListPR returns the list of pull requests in the specified state.
func (gh *GitHub) ListPR(state string) ([]*github.PullRequest, error) {
var prs []*github.PullRequest

opts := &github.PullRequestListOptions{
State: state,
Sort: "updated",
Direction: "desc",
ListOptions: github.ListOptions{
PerPage: PageSize,
},
}

for {
prsPage, response, err := gh.Client.PullRequests.List(gh.Ctx, gh.Owner, gh.Repo, opts)
if err != nil {
return nil, fmt.Errorf("unable to list pull requests with state %s: %w", state, err)
}

Check warning on line 257 in contribs/github-bot/internal/client/client.go

View check run for this annotation

Codecov / codecov/patch

contribs/github-bot/internal/client/client.go#L256-L257

Added lines #L256 - L257 were not covered by tests

prs = append(prs, prsPage...)

if response.NextPage == 0 {
break
}
opts.Page = response.NextPage

Check warning on line 264 in contribs/github-bot/internal/client/client.go

View check run for this annotation

Codecov / codecov/patch

contribs/github-bot/internal/client/client.go#L264

Added line #L264 was not covered by tests
}

return prs, nil
}

// New initializes the API client, the logger, and creates an instance of GitHub.
func New(ctx context.Context, params *p.Params) (*GitHub, error) {
gh := &GitHub{
Expand Down
39 changes: 10 additions & 29 deletions contribs/github-bot/internal/params/params.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"os"
"time"

"github.com/gnolang/gno/contribs/github-bot/internal/utils"
"github.com/sethvargo/go-githubactions"
)

Expand Down Expand Up @@ -83,55 +84,35 @@ func (p *Params) ValidateFlags() {

// Check if flags are coherent.
if p.PRAll && len(p.PRNums) != 0 {
errorUsage("You can specify only one of the '-pr-all' and '-pr-numbers' flags")
errorUsage("You can specify only one of the '-pr-all' and '-pr-numbers' flags.")
}

Check warning on line 88 in contribs/github-bot/internal/params/params.go

View check run for this annotation

Codecov / codecov/patch

contribs/github-bot/internal/params/params.go#L86-L88

Added lines #L86 - L88 were not covered by tests

// If one of these values is empty, it must be retrieved
// from GitHub Actions context.
if p.Owner == "" || p.Repo == "" || (len(p.PRNums) == 0 && !p.PRAll) {
actionCtx, err := githubactions.Context()
if err != nil {
errorUsage(fmt.Sprintf("Unable to get GitHub Actions context: %v", err))
errorUsage(fmt.Sprintf("Unable to get GitHub Actions context: %v.", err))
}

Check warning on line 96 in contribs/github-bot/internal/params/params.go

View check run for this annotation

Codecov / codecov/patch

contribs/github-bot/internal/params/params.go#L92-L96

Added lines #L92 - L96 were not covered by tests

if p.Owner == "" {
if p.Owner, _ = actionCtx.Repo(); p.Owner == "" {
errorUsage("Unable to retrieve owner from GitHub Actions context, you may want to set it using -onwer flag")
errorUsage("Unable to retrieve owner from GitHub Actions context, you may want to set it using -onwer flag.")
}

Check warning on line 101 in contribs/github-bot/internal/params/params.go

View check run for this annotation

Codecov / codecov/patch

contribs/github-bot/internal/params/params.go#L98-L101

Added lines #L98 - L101 were not covered by tests
}
if p.Repo == "" {
if _, p.Repo = actionCtx.Repo(); p.Repo == "" {
errorUsage("Unable to retrieve repo from GitHub Actions context, you may want to set it using -repo flag")
errorUsage("Unable to retrieve repo from GitHub Actions context, you may want to set it using -repo flag.")
}

Check warning on line 106 in contribs/github-bot/internal/params/params.go

View check run for this annotation

Codecov / codecov/patch

contribs/github-bot/internal/params/params.go#L103-L106

Added lines #L103 - L106 were not covered by tests
}

if len(p.PRNums) == 0 && !p.PRAll {
const errMsg = "Unable to retrieve pull request number from GitHub Actions context, you may want to set it using -pr-numbers flag"
var num float64

switch actionCtx.EventName {
case "issue_comment":
issue, ok := actionCtx.Event["issue"].(map[string]any)
if !ok {
errorUsage(errMsg)
}
num, ok = issue["number"].(float64)
if !ok || num <= 0 {
errorUsage(errMsg)
}
case "pull_request":
pr, ok := actionCtx.Event["pull_request"].(map[string]any)
if !ok {
errorUsage(errMsg)
}
num, ok = pr["number"].(float64)
if !ok || num <= 0 {
errorUsage(errMsg)
}
default:
errorUsage(errMsg)
prNum, err := utils.GetPRNumFromActionsCtx(actionCtx)
if err != nil {
errorUsage(fmt.Sprintf("Unable to retrieve pull request number from GitHub Actions context: %s\nYou may want to set it using -pr-numbers flag.", err.Error()))
}

Check warning on line 113 in contribs/github-bot/internal/params/params.go

View check run for this annotation

Codecov / codecov/patch

contribs/github-bot/internal/params/params.go#L109-L113

Added lines #L109 - L113 were not covered by tests

p.PRNums = PRList([]int{int(num)})
p.PRNums = PRList{prNum}

Check warning on line 115 in contribs/github-bot/internal/params/params.go

View check run for this annotation

Codecov / codecov/patch

contribs/github-bot/internal/params/params.go#L115

Added line #L115 was not covered by tests
}
}
}
12 changes: 8 additions & 4 deletions contribs/github-bot/internal/params/prlist.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,18 +28,22 @@ func (p PRList) MarshalText() (text []byte, err error) {

// UnmarshalText implements encoding.TextUnmarshaler.
func (p *PRList) UnmarshalText(text []byte) error {
for _, prNumStr := range strings.Split(string(text), ",") {
prNum, err := strconv.Atoi(strings.TrimSpace(prNumStr))
prNumsStr := strings.Split(string(text), ",")
prNums := make([]int, len(prNumsStr))

for i := range prNumsStr {
prNum, err := strconv.Atoi(strings.TrimSpace(prNumsStr[i]))
if err != nil {
return err
}

if prNum <= 0 {
return fmt.Errorf("invalid pull request number (<= 0): original(%s) parsed(%d)", prNumStr, prNum)
return fmt.Errorf("invalid pull request number (<= 0): original(%s) parsed(%d)", prNumsStr[i], prNum)
}

Check warning on line 42 in contribs/github-bot/internal/params/prlist.go

View check run for this annotation

Codecov / codecov/patch

contribs/github-bot/internal/params/prlist.go#L41-L42

Added lines #L41 - L42 were not covered by tests

*p = append(*p, prNum)
prNums[i] = prNum
}
*p = prNums

return nil
}
45 changes: 45 additions & 0 deletions contribs/github-bot/internal/utils/actions.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package utils

import (
"fmt"

"github.com/sethvargo/go-githubactions"
)

// Recursively search for nested values using the keys provided.
func IndexMap(m map[string]any, keys ...string) any {
if len(keys) == 0 {
return m
}

if val, ok := m[keys[0]]; ok {
if keys = keys[1:]; len(keys) == 0 {
return val
}
subMap, _ := val.(map[string]any)
return IndexMap(subMap, keys...)
}

return nil
}

// Retrieve PR number from GitHub Actions context
func GetPRNumFromActionsCtx(actionCtx *githubactions.GitHubContext) (int, error) {
firstKey := ""

switch actionCtx.EventName {
case EventIssueComment:
firstKey = "issue"
case EventPullRequest, EventPullRequestTarget:
firstKey = "pull_request"
default:
return 0, fmt.Errorf("unsupported event: %s", actionCtx.EventName)

Check warning on line 36 in contribs/github-bot/internal/utils/actions.go

View check run for this annotation

Codecov / codecov/patch

contribs/github-bot/internal/utils/actions.go#L27-L36

Added lines #L27 - L36 were not covered by tests
}

num, ok := IndexMap(actionCtx.Event, firstKey, "number").(float64)
if !ok || num <= 0 {
return 0, fmt.Errorf("invalid value: %d", int(num))
}

Check warning on line 42 in contribs/github-bot/internal/utils/actions.go

View check run for this annotation

Codecov / codecov/patch

contribs/github-bot/internal/utils/actions.go#L39-L42

Added lines #L39 - L42 were not covered by tests

return int(num), nil

Check warning on line 44 in contribs/github-bot/internal/utils/actions.go

View check run for this annotation

Codecov / codecov/patch

contribs/github-bot/internal/utils/actions.go#L44

Added line #L44 was not covered by tests
}
Loading

0 comments on commit 00bc576

Please sign in to comment.