Merge c4560df605 into 3811d88913
This commit is contained in:
commit
a2f1b231f1
5 changed files with 113 additions and 42 deletions
|
|
@ -48,6 +48,12 @@ inputs:
|
|||
description: Number of seconds to wait between polling GitHub API for approval status
|
||||
required: false
|
||||
default: '10'
|
||||
close-issue-means-denial:
|
||||
description: >
|
||||
If true, closing the approval issue without an explicit approval
|
||||
comment will be treated as a denial. Disabled by default.
|
||||
required: false
|
||||
default: "false"
|
||||
outputs:
|
||||
issue-number:
|
||||
description: The number of the issue created
|
||||
|
|
|
|||
67
approval.go
67
approval.go
|
|
@ -11,23 +11,24 @@ import (
|
|||
)
|
||||
|
||||
type approvalEnvironment struct {
|
||||
client *github.Client
|
||||
repoFullName string
|
||||
repo string
|
||||
repoOwner string
|
||||
runID int
|
||||
approvalIssue *github.Issue
|
||||
approvalIssueNumber int
|
||||
issueTitle string
|
||||
issueBody string
|
||||
issueApprovers []string
|
||||
minimumApprovals int
|
||||
targetRepoOwner string
|
||||
targetRepoName string
|
||||
failOnDenial bool
|
||||
client *github.Client
|
||||
repoFullName string
|
||||
repo string
|
||||
repoOwner string
|
||||
runID int
|
||||
approvalIssue *github.Issue
|
||||
approvalIssueNumber int
|
||||
issueTitle string
|
||||
issueBody string
|
||||
issueApprovers []string
|
||||
minimumApprovals int
|
||||
targetRepoOwner string
|
||||
targetRepoName string
|
||||
failOnDenial bool
|
||||
closeIssueMeansDenial bool
|
||||
}
|
||||
|
||||
func newApprovalEnvironment(client *github.Client, repoFullName, repoOwner string, runID int, approvers []string, minimumApprovals int, issueTitle, issueBody string, targetRepoOwner string, targetRepoName string, failOnDenial bool) (*approvalEnvironment, error) {
|
||||
func newApprovalEnvironment(client *github.Client, repoFullName, repoOwner string, runID int, approvers []string, minimumApprovals int, issueTitle, issueBody string, targetRepoOwner string, targetRepoName string, failOnDenial bool, closeIssueMeansDenial bool) (*approvalEnvironment, error) {
|
||||
repoOwnerAndName := strings.Split(repoFullName, "/")
|
||||
if len(repoOwnerAndName) != 2 {
|
||||
return nil, fmt.Errorf("repo owner and name in unexpected format: %s", repoFullName)
|
||||
|
|
@ -35,18 +36,19 @@ func newApprovalEnvironment(client *github.Client, repoFullName, repoOwner strin
|
|||
repo := repoOwnerAndName[1]
|
||||
|
||||
return &approvalEnvironment{
|
||||
client: client,
|
||||
repoFullName: repoFullName,
|
||||
repo: repo,
|
||||
repoOwner: repoOwner,
|
||||
runID: runID,
|
||||
issueApprovers: approvers,
|
||||
minimumApprovals: minimumApprovals,
|
||||
issueTitle: issueTitle,
|
||||
issueBody: issueBody,
|
||||
targetRepoOwner: targetRepoOwner,
|
||||
targetRepoName: targetRepoName,
|
||||
failOnDenial: failOnDenial,
|
||||
client: client,
|
||||
repoFullName: repoFullName,
|
||||
repo: repo,
|
||||
repoOwner: repoOwner,
|
||||
runID: runID,
|
||||
issueApprovers: approvers,
|
||||
minimumApprovals: minimumApprovals,
|
||||
issueTitle: issueTitle,
|
||||
issueBody: issueBody,
|
||||
targetRepoOwner: targetRepoOwner,
|
||||
targetRepoName: targetRepoName,
|
||||
failOnDenial: failOnDenial,
|
||||
closeIssueMeansDenial: closeIssueMeansDenial,
|
||||
}, nil
|
||||
}
|
||||
|
||||
|
|
@ -150,9 +152,9 @@ func (a *approvalEnvironment) SetActionOutputs(outputs map[string]string) (bool,
|
|||
return false, err
|
||||
}
|
||||
|
||||
defer func() {
|
||||
_ = f.Close() // Error explicitly ignored as there is nothing to handle if file close fails.
|
||||
}()
|
||||
defer func() {
|
||||
_ = f.Close() // Error explicitly ignored as there is nothing to handle if file close fails.
|
||||
}()
|
||||
|
||||
var pairs []string
|
||||
|
||||
|
|
@ -308,10 +310,10 @@ func splitLongString(input string) []string {
|
|||
currentLength := 0
|
||||
|
||||
for i, line := range lines {
|
||||
lineLength := len(line)
|
||||
lineLength := len(line)
|
||||
if i < len(lines)-1 {
|
||||
lineLength++
|
||||
}
|
||||
}
|
||||
|
||||
if currentLength+lineLength > maxLength {
|
||||
if currentChunk.Len() > 0 {
|
||||
|
|
@ -344,4 +346,3 @@ func splitLongString(input string) []string {
|
|||
}
|
||||
return result
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -481,9 +481,9 @@ func TestSaveOutput(t *testing.T) {
|
|||
minimumApprovals: 0,
|
||||
}
|
||||
|
||||
if err := os.Remove(testCase.env_github_output); err != nil && !os.IsNotExist(err) {
|
||||
t.Fatalf("failed to remove file: %v", err)
|
||||
}
|
||||
if err := os.Remove(testCase.env_github_output); err != nil && !os.IsNotExist(err) {
|
||||
t.Fatalf("failed to remove file: %v", err)
|
||||
}
|
||||
|
||||
actual, err := a.SetActionOutputs(nil)
|
||||
|
||||
|
|
|
|||
|
|
@ -26,6 +26,7 @@ const (
|
|||
envVarTargetRepoOwner string = "INPUT_TARGET-REPOSITORY-OWNER"
|
||||
envVarTargetRepo string = "INPUT_TARGET-REPOSITORY"
|
||||
envVarPollingIntervalSeconds string = "INPUT_POLLING-INTERVAL-SECONDS"
|
||||
envVarCloseIssueMeansDenial string = "INPUT_CLOSE-ISSUE-MEANS-DENIAL"
|
||||
)
|
||||
|
||||
var (
|
||||
|
|
|
|||
75
main.go
75
main.go
|
|
@ -51,6 +51,7 @@ func handleInterrupt(ctx context.Context, client *github.Client, apprv *approval
|
|||
func newCommentLoopChannel(ctx context.Context, apprv *approvalEnvironment, client *github.Client, pollingInterval time.Duration) chan int {
|
||||
channel := make(chan int)
|
||||
go func() {
|
||||
loop_ctr := 0
|
||||
for {
|
||||
comments, _, err := client.Issues.ListComments(ctx, apprv.targetRepoOwner, apprv.targetRepoName, apprv.approvalIssueNumber, &github.IssueListCommentsOptions{})
|
||||
if err != nil {
|
||||
|
|
@ -119,6 +120,58 @@ func newCommentLoopChannel(ctx context.Context, apprv *approvalEnvironment, clie
|
|||
channel <- 1
|
||||
close(channel)
|
||||
return
|
||||
case approvalStatusPending:
|
||||
if apprv.closeIssueMeansDenial {
|
||||
// Loop counter to make an API call only once per 10 interation, intention: avoid github rate limiting and reduce api cost and stress.
|
||||
if loop_ctr < 10 {
|
||||
loop_ctr += 1
|
||||
continue
|
||||
}
|
||||
loop_ctr = 0
|
||||
|
||||
issue, _, err := client.Issues.Get(
|
||||
|
||||
ctx,
|
||||
apprv.targetRepoOwner,
|
||||
apprv.targetRepoName,
|
||||
apprv.approvalIssueNumber,
|
||||
)
|
||||
if err != nil {
|
||||
fmt.Printf("error fetching issue state: %v\n", err)
|
||||
channel <- 1
|
||||
close(channel)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
if issue.GetState() == "closed" {
|
||||
|
||||
// Issue was closed externally without any approval/denial comment.
|
||||
// Treat as denial per user configuration.
|
||||
denyComment := "Issue was closed without approval. Treating closure as denial"
|
||||
|
||||
if !apprv.failOnDenial {
|
||||
denyComment += " but continuing workflow."
|
||||
} else {
|
||||
denyComment += " and failing workflow."
|
||||
}
|
||||
fmt.Println(denyComment)
|
||||
// Issue is already closed — add comment only, skip re-closing
|
||||
_, _, err := client.Issues.CreateComment(
|
||||
ctx,
|
||||
apprv.targetRepoOwner,
|
||||
apprv.targetRepoName,
|
||||
apprv.approvalIssueNumber,
|
||||
&github.IssueComment{Body: &denyComment},
|
||||
)
|
||||
if err != nil {
|
||||
fmt.Printf("error commenting on closed issue: %v\n", err)
|
||||
}
|
||||
channel <- 1
|
||||
close(channel)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
time.Sleep(pollingInterval)
|
||||
|
|
@ -237,6 +290,16 @@ func main() {
|
|||
}
|
||||
}
|
||||
|
||||
closeIssueMeansDenial := false
|
||||
closeIssueMeansDenialRaw := os.Getenv(envVarCloseIssueMeansDenial)
|
||||
if closeIssueMeansDenialRaw != "" {
|
||||
closeIssueMeansDenial, err = strconv.ParseBool(closeIssueMeansDenialRaw)
|
||||
if err != nil {
|
||||
fmt.Printf("error parsing close-issue-means-denial: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
pollingInterval := defaultPollingInterval
|
||||
pollingIntervalSecondsRaw := os.Getenv(envVarPollingIntervalSeconds)
|
||||
if pollingIntervalSecondsRaw != "" {
|
||||
|
|
@ -274,7 +337,7 @@ func main() {
|
|||
}
|
||||
}
|
||||
|
||||
apprv, err := newApprovalEnvironment(client, repoFullName, repoOwner, runID, approvers, minimumApprovals, issueTitle, issueBody, targetRepoOwner, targetRepoName, failOnDenial)
|
||||
apprv, err := newApprovalEnvironment(client, repoFullName, repoOwner, runID, approvers, minimumApprovals, issueTitle, issueBody, targetRepoOwner, targetRepoName, failOnDenial, closeIssueMeansDenial)
|
||||
if err != nil {
|
||||
fmt.Printf("error creating approval environment: %v\n", err)
|
||||
os.Exit(1)
|
||||
|
|
@ -286,9 +349,9 @@ func main() {
|
|||
os.Exit(1)
|
||||
}
|
||||
|
||||
outputs := map[string]string {
|
||||
outputs := map[string]string{
|
||||
"issue-number": fmt.Sprintf("%d", apprv.approvalIssueNumber),
|
||||
"issue-url": apprv.approvalIssue.GetHTMLURL(),
|
||||
"issue-url": apprv.approvalIssue.GetHTMLURL(),
|
||||
}
|
||||
_, err = apprv.SetActionOutputs(outputs)
|
||||
if err != nil {
|
||||
|
|
@ -305,15 +368,15 @@ func main() {
|
|||
case exitCode := <-commentLoopChannel:
|
||||
approvalStatus := ""
|
||||
|
||||
if (!failOnDenial && exitCode == 1) {
|
||||
if !failOnDenial && exitCode == 1 {
|
||||
approvalStatus = "denied"
|
||||
exitCode = 0
|
||||
} else if (exitCode == 1) {
|
||||
} else if exitCode == 1 {
|
||||
approvalStatus = "denied"
|
||||
} else {
|
||||
approvalStatus = "approved"
|
||||
}
|
||||
outputs := map[string]string {
|
||||
outputs := map[string]string{
|
||||
"approval-status": approvalStatus,
|
||||
}
|
||||
if _, err := apprv.SetActionOutputs(outputs); err != nil {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue