add flexible accepted keywords
This commit is contained in:
parent
a1f523c503
commit
a55d8b93fb
4 changed files with 213 additions and 10 deletions
69
approval.go
69
approval.go
|
|
@ -3,6 +3,7 @@ package main
|
|||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"github.com/google/go-github/v43/github"
|
||||
|
|
@ -47,8 +48,12 @@ URL: %s
|
|||
|
||||
Required approvers: %s
|
||||
|
||||
Respond '%s' to continue workflow or '%s' to cancel.
|
||||
`, a.runURL(), a.approvers, approvalStatusApproved, approvalStatusDenied)
|
||||
Respond '%s' to continue workflow or '%s' to cancel.`,
|
||||
a.runURL(),
|
||||
a.approvers,
|
||||
formatAcceptedWords(approvedWords),
|
||||
formatAcceptedWords(deniedWords),
|
||||
)
|
||||
var err error
|
||||
a.approvalIssue, _, err = a.client.Issues.Create(ctx, a.repoOwner, a.repo, &github.IssueRequest{
|
||||
Title: &issueTitle,
|
||||
|
|
@ -59,7 +64,7 @@ Respond '%s' to continue workflow or '%s' to cancel.
|
|||
return err
|
||||
}
|
||||
|
||||
func approvalFromComments(comments []*github.IssueComment, approvers []string) approvalStatus {
|
||||
func approvalFromComments(comments []*github.IssueComment, approvers []string) (approvalStatus, error) {
|
||||
remainingApprovers := make([]string, len(approvers))
|
||||
copy(remainingApprovers, approvers)
|
||||
|
||||
|
|
@ -71,19 +76,29 @@ func approvalFromComments(comments []*github.IssueComment, approvers []string) a
|
|||
}
|
||||
|
||||
commentBody := comment.GetBody()
|
||||
if commentBody == string(approvalStatusApproved) {
|
||||
isApprovalComment, err := isApproved(commentBody)
|
||||
if err != nil {
|
||||
return approvalStatusPending, err
|
||||
}
|
||||
if isApprovalComment {
|
||||
if len(remainingApprovers) == 1 {
|
||||
return approvalStatusApproved
|
||||
return approvalStatusApproved, nil
|
||||
}
|
||||
remainingApprovers[approverIdx] = remainingApprovers[len(remainingApprovers)-1]
|
||||
remainingApprovers = remainingApprovers[:len(remainingApprovers)-1]
|
||||
continue
|
||||
} else if commentBody == string(approvalStatusDenied) {
|
||||
return approvalStatusDenied
|
||||
}
|
||||
|
||||
isDenialComment, err := isDenied(commentBody)
|
||||
if err != nil {
|
||||
return approvalStatusPending, err
|
||||
}
|
||||
if isDenialComment {
|
||||
return approvalStatusDenied, nil
|
||||
}
|
||||
}
|
||||
|
||||
return approvalStatusPending
|
||||
return approvalStatusPending, nil
|
||||
}
|
||||
|
||||
func approversIndex(approvers []string, name string) int {
|
||||
|
|
@ -94,3 +109,41 @@ func approversIndex(approvers []string, name string) int {
|
|||
}
|
||||
return -1
|
||||
}
|
||||
|
||||
func isApproved(commentBody string) (bool, error) {
|
||||
for _, approvedWord := range approvedWords {
|
||||
matched, err := regexp.MatchString(fmt.Sprintf("(?i)^%s[.!]?$", approvedWord), commentBody)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
if matched {
|
||||
return true, nil
|
||||
}
|
||||
}
|
||||
|
||||
return false, nil
|
||||
}
|
||||
|
||||
func isDenied(commentBody string) (bool, error) {
|
||||
for _, deniedWord := range deniedWords {
|
||||
matched, err := regexp.MatchString(fmt.Sprintf("(?i)^%s[.!]?$", deniedWord), commentBody)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
if matched {
|
||||
return true, nil
|
||||
}
|
||||
}
|
||||
|
||||
return false, nil
|
||||
}
|
||||
|
||||
func formatAcceptedWords(words []string) string {
|
||||
var quotedWords []string
|
||||
|
||||
for _, word := range words {
|
||||
quotedWords = append(quotedWords, fmt.Sprintf("\"%s\"", word))
|
||||
}
|
||||
|
||||
return strings.Join(quotedWords, ",")
|
||||
}
|
||||
|
|
|
|||
143
approval_test.go
143
approval_test.go
|
|
@ -116,10 +116,151 @@ func TestApprovalFromComments(t *testing.T) {
|
|||
|
||||
for _, testCase := range testCases {
|
||||
t.Run(testCase.name, func(t *testing.T) {
|
||||
actual := approvalFromComments(testCase.comments, testCase.approvers)
|
||||
actual, err := approvalFromComments(testCase.comments, testCase.approvers)
|
||||
if err != nil {
|
||||
t.Fatalf("error getting approval from comments: %v", err)
|
||||
}
|
||||
|
||||
if actual != testCase.expectedStatus {
|
||||
t.Fatalf("actual %s, expected %s", actual, testCase.expectedStatus)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestApprovedCommentBody(t *testing.T) {
|
||||
testCases := []struct {
|
||||
name string
|
||||
commentBody string
|
||||
isSuccess bool
|
||||
}{
|
||||
{
|
||||
name: "approved_lowercase_no_punctuation",
|
||||
commentBody: "approved",
|
||||
isSuccess: true,
|
||||
},
|
||||
{
|
||||
name: "approve_lowercase_no_punctuation",
|
||||
commentBody: "approve",
|
||||
isSuccess: true,
|
||||
},
|
||||
{
|
||||
name: "lgtm_lowercase_no_punctuation",
|
||||
commentBody: "lgtm",
|
||||
isSuccess: true,
|
||||
},
|
||||
{
|
||||
name: "yes_lowercase_no_punctuation",
|
||||
commentBody: "yes",
|
||||
isSuccess: true,
|
||||
},
|
||||
{
|
||||
name: "approve_uppercase_no_punctuation",
|
||||
commentBody: "APPROVE",
|
||||
isSuccess: true,
|
||||
},
|
||||
{
|
||||
name: "approved_titlecase_period",
|
||||
commentBody: "Approved.",
|
||||
isSuccess: true,
|
||||
},
|
||||
{
|
||||
name: "approved_titlecase_exclamation",
|
||||
commentBody: "Approved!",
|
||||
isSuccess: true,
|
||||
},
|
||||
{
|
||||
name: "approved_titlecase_question",
|
||||
commentBody: "Approved?",
|
||||
isSuccess: false,
|
||||
},
|
||||
{
|
||||
name: "sentence_with_keyword",
|
||||
commentBody: "should i approve this",
|
||||
isSuccess: false,
|
||||
},
|
||||
{
|
||||
name: "sentence_without_keyword",
|
||||
commentBody: "this is just some random comment",
|
||||
isSuccess: false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, testCase := range testCases {
|
||||
t.Run(testCase.name, func(t *testing.T) {
|
||||
actual, err := isApproved(testCase.commentBody)
|
||||
if err != nil {
|
||||
t.Fatalf("error getting approval: %v", err)
|
||||
}
|
||||
if actual != testCase.isSuccess {
|
||||
t.Fatalf("expected %v but got %v", testCase.isSuccess, actual)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestDeniedCommentBody(t *testing.T) {
|
||||
testCases := []struct {
|
||||
name string
|
||||
commentBody string
|
||||
isSuccess bool
|
||||
}{
|
||||
{
|
||||
name: "denied_lowercase_no_punctuation",
|
||||
commentBody: "denied",
|
||||
isSuccess: true,
|
||||
},
|
||||
{
|
||||
name: "deny_lowercase_no_punctuation",
|
||||
commentBody: "deny",
|
||||
isSuccess: true,
|
||||
},
|
||||
{
|
||||
name: "no_lowercase_no_punctuation",
|
||||
commentBody: "no",
|
||||
isSuccess: true,
|
||||
},
|
||||
{
|
||||
name: "deny_uppercase_no_punctuation",
|
||||
commentBody: "DENY",
|
||||
isSuccess: true,
|
||||
},
|
||||
{
|
||||
name: "denied_titlecase_period",
|
||||
commentBody: "Denied.",
|
||||
isSuccess: true,
|
||||
},
|
||||
{
|
||||
name: "denied_titlecase_exclamation",
|
||||
commentBody: "Denied!",
|
||||
isSuccess: true,
|
||||
},
|
||||
{
|
||||
name: "deny_titlecase_question",
|
||||
commentBody: "Deny?",
|
||||
isSuccess: false,
|
||||
},
|
||||
{
|
||||
name: "sentence_with_keyword",
|
||||
commentBody: "should i deny this",
|
||||
isSuccess: false,
|
||||
},
|
||||
{
|
||||
name: "sentence_without_keyword",
|
||||
commentBody: "this is just some random comment",
|
||||
isSuccess: false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, testCase := range testCases {
|
||||
t.Run(testCase.name, func(t *testing.T) {
|
||||
actual, err := isDenied(testCase.commentBody)
|
||||
if err != nil {
|
||||
t.Fatalf("error getting approval: %v", err)
|
||||
}
|
||||
if actual != testCase.isSuccess {
|
||||
t.Fatalf("expected %v but got %v", testCase.isSuccess, actual)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,3 +11,8 @@ const (
|
|||
envVarToken string = "INPUT_SECRET"
|
||||
envVarApprovers string = "INPUT_APPROVERS"
|
||||
)
|
||||
|
||||
var (
|
||||
approvedWords = []string{"approved", "approve", "lgtm", "yes"}
|
||||
deniedWords = []string{"denied", "deny", "no"}
|
||||
)
|
||||
|
|
|
|||
6
main.go
6
main.go
|
|
@ -57,7 +57,11 @@ commentLoop:
|
|||
os.Exit(1)
|
||||
}
|
||||
|
||||
approved := approvalFromComments(comments, approvers)
|
||||
approved, err := approvalFromComments(comments, approvers)
|
||||
if err != nil {
|
||||
fmt.Printf("error getting approval from comments: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
fmt.Printf("Workflow status: %s\n", approved)
|
||||
switch approved {
|
||||
case approvalStatusApproved:
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue