Prior to this change, the approvers could only be explicit users. With this change, you can now specify an org team and this will be expanded out with a user list for approvers. Closes #14.
193 lines
5.3 KiB
Go
193 lines
5.3 KiB
Go
package main
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"os"
|
|
"os/signal"
|
|
"strconv"
|
|
"time"
|
|
|
|
"github.com/google/go-github/v43/github"
|
|
"golang.org/x/oauth2"
|
|
)
|
|
|
|
func handleInterrupt(ctx context.Context, client *github.Client, apprv *approvalEnvironment) {
|
|
newState := "closed"
|
|
closeComment := "Workflow cancelled, closing issue."
|
|
fmt.Println(closeComment)
|
|
_, _, err := client.Issues.CreateComment(ctx, apprv.repoOwner, apprv.repo, apprv.approvalIssueNumber, &github.IssueComment{
|
|
Body: &closeComment,
|
|
})
|
|
if err != nil {
|
|
fmt.Printf("error commenting on issue: %v\n", err)
|
|
return
|
|
}
|
|
_, _, err = client.Issues.Edit(ctx, apprv.repoOwner, apprv.repo, apprv.approvalIssueNumber, &github.IssueRequest{State: &newState})
|
|
if err != nil {
|
|
fmt.Printf("error closing issue: %v\n", err)
|
|
return
|
|
}
|
|
}
|
|
|
|
func newCommentLoopChannel(ctx context.Context, apprv *approvalEnvironment, client *github.Client) chan int {
|
|
channel := make(chan int)
|
|
go func() {
|
|
for {
|
|
comments, _, err := client.Issues.ListComments(ctx, apprv.repoOwner, apprv.repo, apprv.approvalIssueNumber, &github.IssueListCommentsOptions{})
|
|
if err != nil {
|
|
fmt.Printf("error getting comments: %v\n", err)
|
|
channel <- 1
|
|
close(channel)
|
|
}
|
|
|
|
approved, err := approvalFromComments(comments, apprv.issueApprovers, apprv.minimumApprovals)
|
|
if err != nil {
|
|
fmt.Printf("error getting approval from comments: %v\n", err)
|
|
channel <- 1
|
|
close(channel)
|
|
}
|
|
fmt.Printf("Workflow status: %s\n", approved)
|
|
switch approved {
|
|
case approvalStatusApproved:
|
|
newState := "closed"
|
|
closeComment := "All approvers have approved, continuing workflow and closing this issue."
|
|
_, _, err := client.Issues.CreateComment(ctx, apprv.repoOwner, apprv.repo, apprv.approvalIssueNumber, &github.IssueComment{
|
|
Body: &closeComment,
|
|
})
|
|
if err != nil {
|
|
fmt.Printf("error commenting on issue: %v\n", err)
|
|
channel <- 1
|
|
close(channel)
|
|
}
|
|
_, _, err = client.Issues.Edit(ctx, apprv.repoOwner, apprv.repo, apprv.approvalIssueNumber, &github.IssueRequest{State: &newState})
|
|
if err != nil {
|
|
fmt.Printf("error closing issue: %v\n", err)
|
|
channel <- 1
|
|
close(channel)
|
|
}
|
|
channel <- 0
|
|
fmt.Println("Workflow manual approval completed")
|
|
close(channel)
|
|
case approvalStatusDenied:
|
|
newState := "closed"
|
|
closeComment := "Request denied. Closing issue and failing workflow."
|
|
_, _, err := client.Issues.CreateComment(ctx, apprv.repoOwner, apprv.repo, apprv.approvalIssueNumber, &github.IssueComment{
|
|
Body: &closeComment,
|
|
})
|
|
if err != nil {
|
|
fmt.Printf("error commenting on issue: %v\n", err)
|
|
channel <- 1
|
|
close(channel)
|
|
}
|
|
_, _, err = client.Issues.Edit(ctx, apprv.repoOwner, apprv.repo, apprv.approvalIssueNumber, &github.IssueRequest{State: &newState})
|
|
if err != nil {
|
|
fmt.Printf("error closing issue: %v\n", err)
|
|
channel <- 1
|
|
close(channel)
|
|
}
|
|
channel <- 1
|
|
close(channel)
|
|
}
|
|
|
|
time.Sleep(pollingInterval)
|
|
}
|
|
}()
|
|
return channel
|
|
}
|
|
|
|
func newGithubClient(ctx context.Context) *github.Client {
|
|
token := os.Getenv(envVarToken)
|
|
ts := oauth2.StaticTokenSource(
|
|
&oauth2.Token{AccessToken: token},
|
|
)
|
|
tc := oauth2.NewClient(ctx, ts)
|
|
return github.NewClient(tc)
|
|
}
|
|
|
|
func validateInput() error {
|
|
missingEnvVars := []string{}
|
|
if os.Getenv(envVarRepoFullName) == "" {
|
|
missingEnvVars = append(missingEnvVars, envVarRepoFullName)
|
|
}
|
|
|
|
if os.Getenv(envVarRunID) == "" {
|
|
missingEnvVars = append(missingEnvVars, envVarRunID)
|
|
}
|
|
|
|
if os.Getenv(envVarRepoOwner) == "" {
|
|
missingEnvVars = append(missingEnvVars, envVarRepoOwner)
|
|
}
|
|
|
|
if os.Getenv(envVarToken) == "" {
|
|
missingEnvVars = append(missingEnvVars, envVarToken)
|
|
}
|
|
|
|
if os.Getenv(envVarApprovers) == "" {
|
|
missingEnvVars = append(missingEnvVars, envVarApprovers)
|
|
}
|
|
|
|
if len(missingEnvVars) > 0 {
|
|
return fmt.Errorf("missing env vars: %v", missingEnvVars)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func main() {
|
|
if err := validateInput(); err != nil {
|
|
fmt.Printf("%v\n", err)
|
|
os.Exit(1)
|
|
}
|
|
|
|
repoFullName := os.Getenv(envVarRepoFullName)
|
|
runID, err := strconv.Atoi(os.Getenv(envVarRunID))
|
|
if err != nil {
|
|
fmt.Printf("error getting runID: %v\n", err)
|
|
os.Exit(1)
|
|
}
|
|
repoOwner := os.Getenv(envVarRepoOwner)
|
|
|
|
ctx := context.Background()
|
|
client := newGithubClient(ctx)
|
|
|
|
approvers, err := retrieveApprovers(client, repoOwner)
|
|
if err != nil {
|
|
fmt.Printf("error retrieving approvers: %v\n", err)
|
|
os.Exit(1)
|
|
}
|
|
|
|
issueTitle := os.Getenv(envVarIssueTitle)
|
|
minimumApprovalsRaw := os.Getenv(envVarMinimumApprovals)
|
|
minimumApprovals := 0
|
|
if minimumApprovalsRaw != "" {
|
|
minimumApprovals, err = strconv.Atoi(minimumApprovalsRaw)
|
|
if err != nil {
|
|
fmt.Printf("error parsing minimum approvals: %v\n", err)
|
|
os.Exit(1)
|
|
}
|
|
}
|
|
apprv, err := newApprovalEnvironment(client, repoFullName, repoOwner, runID, approvers, minimumApprovals, issueTitle)
|
|
if err != nil {
|
|
fmt.Printf("error creating approval environment: %v\n", err)
|
|
os.Exit(1)
|
|
}
|
|
|
|
err = apprv.createApprovalIssue(ctx)
|
|
if err != nil {
|
|
fmt.Printf("error creating issue: %v", err)
|
|
os.Exit(1)
|
|
}
|
|
|
|
killSignalChannel := make(chan os.Signal, 1)
|
|
signal.Notify(killSignalChannel, os.Interrupt)
|
|
|
|
commentLoopChannel := newCommentLoopChannel(ctx, apprv, client)
|
|
|
|
select {
|
|
case exitCode := <-commentLoopChannel:
|
|
os.Exit(exitCode)
|
|
case <-killSignalChannel:
|
|
handleInterrupt(ctx, client, apprv)
|
|
os.Exit(1)
|
|
}
|
|
}
|