Adding the capability to create the approval issue in the another repository (#142)

* Adding the capability to create the approval issue in another repository

* reverting testing changes

* reverting test changes contd.

* Updating readme and removing a debug line

* Introduce a 'fail-on-denial' boolean (#147)

* Introduce a 'fail-on-approval' boolean

* Use test docker image

* oops

* change default and fix logic

* Update action.yaml

* Fix linting error

* Adding the capability to create the approval issue in another repository

* reverting testing changes

* reverting test changes contd.

* moving targetRepo inputs from env vars to action-args

* Update constants.go

* Update constants.go to resolve nuances created by inconsistent tab length settings

---------

Co-authored-by: Liz MacLean <18120837+lizziemac@users.noreply.github.com>
This commit is contained in:
Sanskar Arora 2025-02-23 00:20:00 +05:30 committed by GitHub
parent aba06e32f1
commit c17f1c63ff
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 55 additions and 12 deletions

View file

@ -20,6 +20,7 @@ jobs:
steps:
- name: Checkout
uses: actions/checkout@v2
- name: Build
run: make build
env:

View file

@ -46,6 +46,25 @@ steps:
- `additional-approved-words` is a comma separated list of strings to expand the dictionary of words that indicate approval. This is optional and defaults to an empty string.
- `additional-denied-words` is a comma separated list of strings to expand the dictionary of words that indicate denial. This is optional and defaults to an empty string.
### Creating Issues in a different repository
```yaml
steps:
- uses: trstringer/manual-approval@v1
with:
secret: ${{ github.TOKEN }}
approvers: user1,user2,org-team1
minimum-approvals: 1
issue-title: "Deploying v1.3.5 to prod from staging"
issue-body: "Please approve or deny the deployment of version v1.3.5."
exclude-workflow-initiator-as-approver: false
additional-approved-words: ''
additional-denied-words: ''
target-repository: repository-name
target-repository-owner: owner-id
```
- if either of `target-repository` or `target-repository-owner` is missing or is an empty string then the issue will be created in the same repository where this step is used.
### Using Custom Words
GitHub has a rich library of emojis, and these all work in additional approved words or denied words. Some values GitHub will store in their text version - i.e. `:shipit:`. Other emojis, GitHub will store in their unicode emoji form, like ✅.

View file

@ -34,6 +34,12 @@ inputs:
description: Comma separated list of words that can be used to deny beyond the defaults.
required: false
default: ''
target-repository-owner:
description: Owner of the repository in which the issue will be created.
default: ''
target-repository:
description: Name of the repository in which the issue will be created.
default: ''
fail-on-denial:
description: Whether or not to fail the workflow if the approval is denied
required: false

View file

@ -21,10 +21,12 @@ type approvalEnvironment struct {
issueBody string
issueApprovers []string
minimumApprovals int
targetRepoOwner string
targetRepoName string
failOnDenial bool
}
func newApprovalEnvironment(client *github.Client, repoFullName, repoOwner string, runID int, approvers []string, minimumApprovals int, issueTitle, issueBody 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) (*approvalEnvironment, error) {
repoOwnerAndName := strings.Split(repoFullName, "/")
if len(repoOwnerAndName) != 2 {
return nil, fmt.Errorf("repo owner and name in unexpected format: %s", repoFullName)
@ -41,6 +43,8 @@ func newApprovalEnvironment(client *github.Client, repoFullName, repoOwner strin
minimumApprovals: minimumApprovals,
issueTitle: issueTitle,
issueBody: issueBody,
targetRepoOwner: targetRepoOwner,
targetRepoName: targetRepoName,
failOnDenial: failOnDenial,
}, nil
}
@ -79,13 +83,13 @@ Respond %s to continue workflow or %s to cancel.`,
var err error
fmt.Printf(
"Creating issue in repo %s/%s with the following content:\nTitle: %s\nApprovers: %s\nBody:\n%s\n",
a.repoOwner,
a.repo,
a.targetRepoOwner,
a.targetRepoName,
issueTitle,
a.issueApprovers,
issueBody,
)
a.approvalIssue, _, err = a.client.Issues.Create(ctx, a.repoOwner, a.repo, &github.IssueRequest{
a.approvalIssue, _, err = a.client.Issues.Create(ctx, a.targetRepoOwner, a.targetRepoName, &github.IssueRequest{
Title: &issueTitle,
Body: &issueBody,
Assignees: &a.issueApprovers,

View file

@ -22,6 +22,8 @@ const (
envVarAdditionalApprovedWords string = "INPUT_ADDITIONAL-APPROVED-WORDS"
envVarAdditionalDeniedWords string = "INPUT_ADDITIONAL-DENIED-WORDS"
envVarFailOnDenial string = "INPUT_FAIL-ON-DENIAL"
envVarTargetRepoOwner string = "INPUT_TARGET-REPOSITORY-OWNER"
envVarTargetRepo string = "INPUT_TARGET-REPOSITORY"
)
var (

27
main.go
View file

@ -6,6 +6,7 @@ import (
"os"
"os/signal"
"strconv"
"strings"
"time"
"github.com/google/go-github/v43/github"
@ -29,14 +30,14 @@ func handleInterrupt(ctx context.Context, client *github.Client, apprv *approval
newState := "closed"
closeComment := "Workflow cancelled, closing issue."
fmt.Println(closeComment)
_, _, err := client.Issues.CreateComment(ctx, apprv.repoOwner, apprv.repo, apprv.approvalIssueNumber, &github.IssueComment{
_, _, err := client.Issues.CreateComment(ctx, apprv.targetRepoOwner, apprv.targetRepoName, 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})
_, _, err = client.Issues.Edit(ctx, apprv.targetRepoOwner, apprv.targetRepoName, apprv.approvalIssueNumber, &github.IssueRequest{State: &newState})
if err != nil {
fmt.Printf("error closing issue: %v\n", err)
return
@ -47,7 +48,7 @@ func newCommentLoopChannel(ctx context.Context, apprv *approvalEnvironment, clie
channel := make(chan int)
go func() {
for {
comments, _, err := client.Issues.ListComments(ctx, apprv.repoOwner, apprv.repo, apprv.approvalIssueNumber, &github.IssueListCommentsOptions{})
comments, _, err := client.Issues.ListComments(ctx, apprv.targetRepoOwner, apprv.targetRepoName, apprv.approvalIssueNumber, &github.IssueListCommentsOptions{})
if err != nil {
fmt.Printf("error getting comments: %v\n", err)
channel <- 1
@ -65,7 +66,7 @@ func newCommentLoopChannel(ctx context.Context, apprv *approvalEnvironment, clie
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{
_, _, err := client.Issues.CreateComment(ctx, apprv.targetRepoOwner, apprv.targetRepoName, apprv.approvalIssueNumber, &github.IssueComment{
Body: &closeComment,
})
if err != nil {
@ -73,7 +74,7 @@ func newCommentLoopChannel(ctx context.Context, apprv *approvalEnvironment, clie
channel <- 1
close(channel)
}
_, _, err = client.Issues.Edit(ctx, apprv.repoOwner, apprv.repo, apprv.approvalIssueNumber, &github.IssueRequest{State: &newState})
_, _, err = client.Issues.Edit(ctx, apprv.targetRepoOwner, apprv.targetRepoName, apprv.approvalIssueNumber, &github.IssueRequest{State: &newState})
if err != nil {
fmt.Printf("error closing issue: %v\n", err)
channel <- 1
@ -92,7 +93,7 @@ func newCommentLoopChannel(ctx context.Context, apprv *approvalEnvironment, clie
}
closeComment += " workflow."
_, _, err := client.Issues.CreateComment(ctx, apprv.repoOwner, apprv.repo, apprv.approvalIssueNumber, &github.IssueComment{
_, _, err := client.Issues.CreateComment(ctx, apprv.targetRepoOwner, apprv.targetRepoName, apprv.approvalIssueNumber, &github.IssueComment{
Body: &closeComment,
})
if err != nil {
@ -100,7 +101,7 @@ func newCommentLoopChannel(ctx context.Context, apprv *approvalEnvironment, clie
channel <- 1
close(channel)
}
_, _, err = client.Issues.Edit(ctx, apprv.repoOwner, apprv.repo, apprv.approvalIssueNumber, &github.IssueRequest{State: &newState})
_, _, err = client.Issues.Edit(ctx, apprv.targetRepoOwner, apprv.targetRepoName, apprv.approvalIssueNumber, &github.IssueRequest{State: &newState})
if err != nil {
fmt.Printf("error closing issue: %v\n", err)
channel <- 1
@ -169,6 +170,9 @@ func main() {
os.Exit(1)
}
targetRepoName := os.Getenv(envVarTargetRepo)
targetRepoOwner := os.Getenv(envVarTargetRepoOwner)
repoFullName := os.Getenv(envVarRepoFullName)
runID, err := strconv.Atoi(os.Getenv(envVarRunID))
if err != nil {
@ -177,6 +181,12 @@ func main() {
}
repoOwner := os.Getenv(envVarRepoOwner)
if targetRepoName == "" || targetRepoOwner == "" {
parts := strings.SplitN(repoFullName, "/", 2)
targetRepoOwner = parts[0]
targetRepoName = parts[1]
}
ctx := context.Background()
client, err := newGithubClient(ctx)
if err != nil {
@ -211,7 +221,8 @@ func main() {
os.Exit(1)
}
}
apprv, err := newApprovalEnvironment(client, repoFullName, repoOwner, runID, approvers, minimumApprovals, issueTitle, issueBody, failOnDenial)
apprv, err := newApprovalEnvironment(client, repoFullName, repoOwner, runID, approvers, minimumApprovals, issueTitle, issueBody, targetRepoOwner, targetRepoName, failOnDenial)
if err != nil {
fmt.Printf("error creating approval environment: %v\n", err)
os.Exit(1)