96 lines
2.6 KiB
Go
96 lines
2.6 KiB
Go
package main
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"strings"
|
|
|
|
"github.com/google/go-github/v43/github"
|
|
)
|
|
|
|
type approvalEnvironment struct {
|
|
client *github.Client
|
|
repoFullName string
|
|
repo string
|
|
repoOwner string
|
|
runID int
|
|
approvers []string
|
|
approvalIssue *github.Issue
|
|
approvalIssueNumber int
|
|
}
|
|
|
|
func newApprovalEnvironment(client *github.Client, repoFullName, repoOwner string, runID int, approvers []string) (*approvalEnvironment, error) {
|
|
repoOwnerAndName := strings.Split(repoFullName, "/")
|
|
if len(repoOwnerAndName) != 2 {
|
|
return nil, fmt.Errorf("repo owner and name in unexpected format: %s", repoFullName)
|
|
}
|
|
repo := repoOwnerAndName[1]
|
|
|
|
return &approvalEnvironment{
|
|
client: client,
|
|
repoFullName: repoFullName,
|
|
repo: repo,
|
|
repoOwner: repoOwner,
|
|
runID: runID,
|
|
approvers: approvers,
|
|
}, nil
|
|
}
|
|
|
|
func (a approvalEnvironment) runURL() string {
|
|
return fmt.Sprintf("https://github.com/%s/actions/runs/%d", a.repoFullName, a.runID)
|
|
}
|
|
|
|
func (a *approvalEnvironment) createApprovalIssue(ctx context.Context) error {
|
|
issueTitle := fmt.Sprintf("Manual approval required for workflow run %d", a.runID)
|
|
issueBody := fmt.Sprintf(`Workflow is pending manual review.
|
|
URL: %s
|
|
|
|
Required approvers: %s
|
|
|
|
Respond '%s' to continue workflow or '%s' to cancel.
|
|
`, a.runURL(), a.approvers, approvalStatusApproved, approvalStatusDenied)
|
|
var err error
|
|
a.approvalIssue, _, err = a.client.Issues.Create(ctx, a.repoOwner, a.repo, &github.IssueRequest{
|
|
Title: &issueTitle,
|
|
Body: &issueBody,
|
|
Assignees: &a.approvers,
|
|
})
|
|
a.approvalIssueNumber = a.approvalIssue.GetNumber()
|
|
return err
|
|
}
|
|
|
|
func approvalFromComments(comments []*github.IssueComment, approvers []string) approvalStatus {
|
|
remainingApprovers := make([]string, len(approvers))
|
|
copy(remainingApprovers, approvers)
|
|
|
|
for _, comment := range comments {
|
|
commentUser := comment.User.GetLogin()
|
|
approverIdx := approversIndex(remainingApprovers, commentUser)
|
|
if approverIdx < 0 {
|
|
continue
|
|
}
|
|
|
|
commentBody := comment.GetBody()
|
|
if commentBody == string(approvalStatusApproved) {
|
|
if len(remainingApprovers) == 1 {
|
|
return approvalStatusApproved
|
|
}
|
|
remainingApprovers[approverIdx] = remainingApprovers[len(remainingApprovers)-1]
|
|
remainingApprovers = remainingApprovers[:len(remainingApprovers)-1]
|
|
continue
|
|
} else if commentBody == string(approvalStatusDenied) {
|
|
return approvalStatusDenied
|
|
}
|
|
}
|
|
|
|
return approvalStatusPending
|
|
}
|
|
|
|
func approversIndex(approvers []string, name string) int {
|
|
for idx, approver := range approvers {
|
|
if approver == name {
|
|
return idx
|
|
}
|
|
}
|
|
return -1
|
|
}
|