From aba06e32f19b0890e99cfd2a9af7868a671cba16 Mon Sep 17 00:00:00 2001 From: Liz MacLean <18120837+lizziemac@users.noreply.github.com> Date: Fri, 14 Feb 2025 20:23:48 -0500 Subject: [PATCH 01/17] 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 --- Makefile | 3 ++- action.yaml | 9 ++++++++- approval.go | 4 +++- constants.go | 1 + main.go | 48 ++++++++++++++++++++++++++++++++++++++++++++++-- 5 files changed, 60 insertions(+), 5 deletions(-) diff --git a/Makefile b/Makefile index 3ec553a..8c3fc9b 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,5 @@ IMAGE_REPO=ghcr.io/trstringer/manual-approval +TARGET_PLATFORM=linux/amd64 .PHONY: build build: @@ -6,7 +7,7 @@ build: echo "VERSION is required"; \ exit 1; \ fi - docker build -t $(IMAGE_REPO):$$VERSION . + docker build --platform $(TARGET_PLATFORM) -t $(IMAGE_REPO):$$VERSION . .PHONY: push push: diff --git a/action.yaml b/action.yaml index 7866c0b..bef33d7 100644 --- a/action.yaml +++ b/action.yaml @@ -24,13 +24,20 @@ inputs: required: false exclude-workflow-initiator-as-approver: description: Whether or not to filter out the user who initiated the workflow as an approver if they are in the approvers list - default: false + required: false + default: 'false' additional-approved-words: description: Comma separated list of words that can be used to approve beyond the defaults. + required: false default: '' additional-denied-words: description: Comma separated list of words that can be used to deny beyond the defaults. + required: false default: '' + fail-on-denial: + description: Whether or not to fail the workflow if the approval is denied + required: false + default: 'true' runs: using: docker image: docker://ghcr.io/trstringer/manual-approval:1.9.1 diff --git a/approval.go b/approval.go index e2bb13f..d69df54 100644 --- a/approval.go +++ b/approval.go @@ -21,9 +21,10 @@ type approvalEnvironment struct { issueBody string issueApprovers []string minimumApprovals int + failOnDenial bool } -func newApprovalEnvironment(client *github.Client, repoFullName, repoOwner string, runID int, approvers []string, minimumApprovals int, issueTitle, issueBody string) (*approvalEnvironment, error) { +func newApprovalEnvironment(client *github.Client, repoFullName, repoOwner string, runID int, approvers []string, minimumApprovals int, issueTitle, issueBody 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) @@ -40,6 +41,7 @@ func newApprovalEnvironment(client *github.Client, repoFullName, repoOwner strin minimumApprovals: minimumApprovals, issueTitle: issueTitle, issueBody: issueBody, + failOnDenial: failOnDenial, }, nil } diff --git a/constants.go b/constants.go index 03e4b4e..0e08102 100644 --- a/constants.go +++ b/constants.go @@ -21,6 +21,7 @@ const ( envVarExcludeWorkflowInitiatorAsApprover string = "INPUT_EXCLUDE-WORKFLOW-INITIATOR-AS-APPROVER" envVarAdditionalApprovedWords string = "INPUT_ADDITIONAL-APPROVED-WORDS" envVarAdditionalDeniedWords string = "INPUT_ADDITIONAL-DENIED-WORDS" + envVarFailOnDenial string = "INPUT_FAIL-ON-DENIAL" ) var ( diff --git a/main.go b/main.go index 5aad15e..5349a87 100644 --- a/main.go +++ b/main.go @@ -12,6 +12,19 @@ import ( "golang.org/x/oauth2" ) +func setActionOutput(name, value string) error { + f, err := os.OpenFile(os.Getenv("GITHUB_OUTPUT"), os.O_APPEND|os.O_WRONLY, 0600) + if err != nil { + return err + } + defer f.Close() + + if _, err = f.WriteString(fmt.Sprintf("%s=%s\n", name, value)); err != nil { + return err + } + return nil +} + func handleInterrupt(ctx context.Context, client *github.Client, apprv *approvalEnvironment) { newState := "closed" closeComment := "Workflow cancelled, closing issue." @@ -71,7 +84,14 @@ func newCommentLoopChannel(ctx context.Context, apprv *approvalEnvironment, clie close(channel) case approvalStatusDenied: newState := "closed" - closeComment := "Request denied. Closing issue and failing workflow." + closeComment := "Request denied. Closing issue " + if !apprv.failOnDenial { + closeComment += "but continuing" + } else { + closeComment += "and failing" + } + closeComment += " workflow." + _, _, err := client.Issues.CreateComment(ctx, apprv.repoOwner, apprv.repo, apprv.approvalIssueNumber, &github.IssueComment{ Body: &closeComment, }) @@ -170,6 +190,16 @@ func main() { os.Exit(1) } + failOnDenial := true + failOnDenialRaw := os.Getenv(envVarFailOnDenial) + if failOnDenialRaw != "" { + failOnDenial, err = strconv.ParseBool(failOnDenialRaw) + if err != nil { + fmt.Printf("error parsing fail on denial: %v\n", err) + os.Exit(1) + } + } + issueTitle := os.Getenv(envVarIssueTitle) issueBody := os.Getenv(envVarIssueBody) minimumApprovalsRaw := os.Getenv(envVarMinimumApprovals) @@ -181,7 +211,7 @@ func main() { os.Exit(1) } } - apprv, err := newApprovalEnvironment(client, repoFullName, repoOwner, runID, approvers, minimumApprovals, issueTitle, issueBody) + apprv, err := newApprovalEnvironment(client, repoFullName, repoOwner, runID, approvers, minimumApprovals, issueTitle, issueBody, failOnDenial) if err != nil { fmt.Printf("error creating approval environment: %v\n", err) os.Exit(1) @@ -200,6 +230,20 @@ func main() { select { case exitCode := <-commentLoopChannel: + approvalStatus := "" + + if (!failOnDenial && exitCode == 1) { + approvalStatus = "denied" + exitCode = 0 + } else if (exitCode == 1) { + approvalStatus = "denied" + } else { + approvalStatus = "approved" + } + if err := setActionOutput("approval_status", approvalStatus); err != nil { + fmt.Printf("error setting action output: %v\n", err) + exitCode = 1 + } os.Exit(exitCode) case <-killSignalChannel: handleInterrupt(ctx, client, apprv) From 17c84dd68836c83380433bf434b73e094ddbfc04 Mon Sep 17 00:00:00 2001 From: Liz MacLean <18120837+lizziemac@users.noreply.github.com> Date: Mon, 17 Feb 2025 15:12:35 -0500 Subject: [PATCH 02/17] Remove default title if custom title is provided --- approval.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/approval.go b/approval.go index d69df54..c6e454e 100644 --- a/approval.go +++ b/approval.go @@ -57,7 +57,7 @@ func (a *approvalEnvironment) createApprovalIssue(ctx context.Context) error { issueTitle := fmt.Sprintf("Manual approval required for workflow run %d", a.runID) if a.issueTitle != "" { - issueTitle = fmt.Sprintf("%s: %s", issueTitle, a.issueTitle) + issueTitle = a.issueTitle } issueBody := fmt.Sprintf(`Workflow is pending manual review. From c17f1c63ff8c81f8b35f98be67488f5b2e3dd241 Mon Sep 17 00:00:00 2001 From: Sanskar Arora <55059942+sunny-1651@users.noreply.github.com> Date: Sun, 23 Feb 2025 00:20:00 +0530 Subject: [PATCH 03/17] 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> --- .github/workflows/ci.yaml | 1 + README.md | 19 +++++++++++++++++++ action.yaml | 6 ++++++ approval.go | 12 ++++++++---- constants.go | 2 ++ main.go | 27 +++++++++++++++++++-------- 6 files changed, 55 insertions(+), 12 deletions(-) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index a6ab720..5fa65b1 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -20,6 +20,7 @@ jobs: steps: - name: Checkout uses: actions/checkout@v2 + - name: Build run: make build env: diff --git a/README.md b/README.md index 25218e9..68336f9 100644 --- a/README.md +++ b/README.md @@ -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 ✅. diff --git a/action.yaml b/action.yaml index bef33d7..c515b96 100644 --- a/action.yaml +++ b/action.yaml @@ -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 diff --git a/approval.go b/approval.go index d69df54..540cd71 100644 --- a/approval.go +++ b/approval.go @@ -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, diff --git a/constants.go b/constants.go index 0e08102..027aafa 100644 --- a/constants.go +++ b/constants.go @@ -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 ( diff --git a/main.go b/main.go index 5349a87..e9944bb 100644 --- a/main.go +++ b/main.go @@ -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) From 846a0ce970c14b5c4e23061e50e841d622a82913 Mon Sep 17 00:00:00 2001 From: Bailey Everts Date: Sat, 22 Feb 2025 11:53:00 -0700 Subject: [PATCH 04/17] feat: use blocks for issue-body (#137) --- approval.go | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/approval.go b/approval.go index 540cd71..c7b673d 100644 --- a/approval.go +++ b/approval.go @@ -64,12 +64,14 @@ func (a *approvalEnvironment) createApprovalIssue(ctx context.Context) error { issueTitle = fmt.Sprintf("%s: %s", issueTitle, a.issueTitle) } - issueBody := fmt.Sprintf(`Workflow is pending manual review. -URL: %s + issueBody := fmt.Sprintf(`> Workflow is pending manual review. +> URL: %s -Required approvers: %s +> [!IMPORTANT] +> Required approvers: %s -Respond %s to continue workflow or %s to cancel.`, +> [!TIP] +> Respond %s to continue workflow or %s to cancel.`, a.runURL(), a.issueApprovers, formatAcceptedWords(approvedWords), @@ -77,8 +79,9 @@ Respond %s to continue workflow or %s to cancel.`, ) if a.issueBody != "" { - issueBody = fmt.Sprintf("%s\n\n%s", a.issueBody, issueBody) + issueBody = fmt.Sprintf(">%s\n>\n%s", a.issueBody, issueBody) } + issueBody = fmt.Sprintf(">[!NOTE]\n%s", issueBody) var err error fmt.Printf( From 77d7e0184d4cb81961299ec47ce783cb96bde67d Mon Sep 17 00:00:00 2001 From: Bailey Everts Date: Sun, 23 Feb 2025 09:36:20 -0700 Subject: [PATCH 05/17] feat: dispaly users as a list (#138) --- approval.go | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/approval.go b/approval.go index c7b673d..f168ab6 100644 --- a/approval.go +++ b/approval.go @@ -64,16 +64,22 @@ func (a *approvalEnvironment) createApprovalIssue(ctx context.Context) error { issueTitle = fmt.Sprintf("%s: %s", issueTitle, a.issueTitle) } + approversBody := "" + for _, approver := range a.issueApprovers { + approversBody = fmt.Sprintf("%s> * @%s\n", approversBody, approver) + } + issueBody := fmt.Sprintf(`> Workflow is pending manual review. > URL: %s > [!IMPORTANT] -> Required approvers: %s +> Required approvers: +%s > [!TIP] > Respond %s to continue workflow or %s to cancel.`, a.runURL(), - a.issueApprovers, + approversBody, formatAcceptedWords(approvedWords), formatAcceptedWords(deniedWords), ) From 8b4df3f1bc12bd87945015fe497dce8f59c576b7 Mon Sep 17 00:00:00 2001 From: Sanskar Arora <55059942+sunny-1651@users.noreply.github.com> Date: Tue, 4 Mar 2025 05:56:53 +0530 Subject: [PATCH 06/17] Dependabot codeql patch (#152) * addding codecheck yamls * updating versions * code ql to run only on main repo not forks --- .github/dependabot.yml | 7 +++++++ .github/workflows/codeql.yml | 37 ++++++++++++++++++++++++++++++++++++ 2 files changed, 44 insertions(+) create mode 100644 .github/dependabot.yml create mode 100644 .github/workflows/codeql.yml diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..21c0165 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,7 @@ +version: 2 +updates: + - package-ecosystem: "gomod" + directory: "/" + schedule: + interval: "weekly" + open-pull-requests-limit: 10 # default: 5 diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml new file mode 100644 index 0000000..6d68125 --- /dev/null +++ b/.github/workflows/codeql.yml @@ -0,0 +1,37 @@ +name: "Code Analysis" + +on: + push: + pull_request: + +permissions: + contents: read + +jobs: + CodeQL-Build: + if: github.repository == 'trstringer/manual-approval' + permissions: + actions: read + contents: read + security-events: write + runs-on: ubuntu-latest + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Setup Golang + uses: actions/setup-go@v5 + with: + go-version-file: go.mod + + - name: Initialize CodeQL + uses: github/codeql-action/init@v3 + with: + languages: go + + - name: Autobuild + uses: github/codeql-action/autobuild@v3 + + - name: Perform CodeQL Analysis + uses: github/codeql-action/analyze@v3 From 2177c2e2376645b3bad2dabd24d77b3af170f11f Mon Sep 17 00:00:00 2001 From: Sanskar Arora <55059942+sunny-1651@users.noreply.github.com> Date: Tue, 4 Mar 2025 21:54:44 +0530 Subject: [PATCH 07/17] Update codeql.yml --- .github/workflows/codeql.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index 6d68125..f5442a1 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -2,6 +2,8 @@ name: "Code Analysis" on: push: + branches: + - "main" pull_request: permissions: From 824801926666d7718ae522c838704d64611ab80a Mon Sep 17 00:00:00 2001 From: Liz MacLean <18120837+lizziemac@users.noreply.github.com> Date: Thu, 6 Mar 2025 15:50:38 -0500 Subject: [PATCH 08/17] Cleanup README and add some more informative messaging (#149) --- README.md | 51 ++++++++++++++++++++++++++++++--------------------- action.yaml | 3 --- main.go | 3 ++- 3 files changed, 32 insertions(+), 25 deletions(-) diff --git a/README.md b/README.md index 68336f9..37317e0 100644 --- a/README.md +++ b/README.md @@ -1,12 +1,13 @@ + # Manual Workflow Approval [![ci](https://github.com/trstringer/manual-approval/actions/workflows/ci.yaml/badge.svg)](https://github.com/trstringer/manual-approval/actions/workflows/ci.yaml) Pause a GitHub Actions workflow and require manual approval from one or more approvers before continuing. -This is a very common feature for a deployment or release pipeline, and while [this functionality is available from GitHub](https://docs.github.com/en/actions/managing-workflow-runs/reviewing-deployments), it requires the use of environments and if you want to use this for private repositories then you need GitHub Enterprise. This action provides manual approval without the use of environments, and is freely available to use on private repositories. +This is a very common feature for a deployment or release pipeline, and while [this functionality is available from GitHub](https://docs.github.com/en/actions/managing-workflow-runs/reviewing-deployments), it requires the use of environments and if you want to use this for private repositories, then you need GitHub Enterprise. This action provides manual approval without the use of environments, and is freely available to use on private repositories. -*Note: This approval duration is subject to the broader 72 hours timeout for a workflow. So keep that in mind when figuring out how quickly an approver must respond.* +*Note: This approval duration is subject to the broader 35 day timeout for a workflow, as well as usage costs. So keep that in mind when figuring out how quickly an approver must respond. See [limitations](#limitations) for more information.* The way this action works is the following: @@ -34,17 +35,23 @@ steps: 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 + fail-on-denial: true additional-approved-words: '' additional-denied-words: '' ``` -- `approvers` is a comma-delimited list of all required approvers. An approver can either be a user or an org team. (*Note: Required approvers must have the ability to be set as approvers in the repository. If you add an approver that doesn't have this permission then you would receive an HTTP/402 Validation Failed error when running this action*) -- `minimum-approvals` is an integer that sets the minimum number of approvals required to progress the workflow. Defaults to ALL approvers. -- `issue-title` is a string that will be appended to the title of the issue. -- `issue-body` is a string that will be prepended to the body of the issue. -- `exclude-workflow-initiator-as-approver` is a boolean that indicates if the workflow initiator (determined by the `GITHUB_ACTOR` environment variable) should be filtered from the final list of approvers. This is optional and defaults to `false`. Set this to `true` to prevent users in the `approvers` list from being able to self-approve workflows. -- `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. +* `approvers` is a comma-delimited list of all required approvers. An approver can either be a user or an org team. (*Note: Required approvers must have the ability to be set as approvers in the repository. If you add an approver that doesn't have this permission then you would receive an HTTP/402 Validation Failed error when running this action*) +* `minimum-approvals` is an integer that sets the minimum number of approvals required to progress the workflow. Defaults to ALL approvers. +* `issue-title` is a string that will be appended to the title of the issue. +* `issue-body` is a string that will be prepended to the body of the issue. +* `exclude-workflow-initiator-as-approver` is a boolean that indicates if the workflow initiator (determined by the `GITHUB_ACTOR` environment variable) should be filtered from the final list of approvers. This is optional and defaults to `false`. Set this to `true` to prevent users in the `approvers` list from being able to self-approve workflows. +* `fail-on-denial` is a boolean that indicates if the workflow should fail if any approver denies the approval. This is optional and defaults to `true`. Set this to `false` to allow the workflow to continue if any approver denies the approval. +* `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. + +### Outputs + +* `approval_status` is a string that indicates the final status of the approval. This will be either `approved` or `denied`. ### Creating Issues in a different repository @@ -124,8 +131,10 @@ For more information on permissions, please look at the [GitHub documentation](h ## Limitations * While the workflow is paused, it will still continue to consume a concurrent job allocation out of the [max concurrent jobs](https://docs.github.com/en/actions/learn-github-actions/usage-limits-billing-and-administration#usage-limits). -* A job (including a paused job) will be failed [after 6 hours](https://docs.github.com/en/actions/learn-github-actions/usage-limits-billing-and-administration#usage-limits). * A paused job is still running compute/instance/virtual machine and will continue to incur costs. +* Expirations (also mentioned elsewhere in this document): + * A job (including a paused job) will be failed [after 6 hours, and a workflow will be failed after 35 days](https://docs.github.com/en/actions/learn-github-actions/usage-limits-billing-and-administration#usage-limits). + * GitHub App tokens expire after 1 hour which implies duration for the approval cannot exceed 60 minutes or the job will fail due to bad credentials. See [docs](https://docs.github.com/en/rest/apps/apps#create-an-installation-access-token-for-an-app) ## Development @@ -139,16 +148,16 @@ To test out your code in an action, you need to build the image and push it to a Build the image: -``` -$ VERSION=1.7.1-rc.1 make IMAGE_REPO=ghcr.io/trstringer/manual-approval-test build +```shell +VERSION=1.7.1-rc.1 make IMAGE_REPO=ghcr.io/trstringer/manual-approval-test build ``` *Note: The image version can be whatever you want, as this image wouldn't be pushed to production. It is only for testing.* Push the image to your container registry: -``` -$ VERSION=1.7.1-rc.1 make IMAGE_REPO=ghcr.io/trstringer/manual-approval-test push +```shell +VERSION=1.7.1-rc.1 make IMAGE_REPO=ghcr.io/trstringer/manual-approval-test push ``` To test out the image you will need to modify `action.yaml` so that it points to your new image that you're testing: @@ -174,10 +183,10 @@ For `uses`, this should point to your repo and dev branch. ### Create a release 1. Build the new version's image: `$ VERSION=1.7.0 make build` -1. Push the new image: `$ VERSION=1.7.0 make push` -1. Create a release branch and modify `action.yaml` to point to the new image -1. Open and merge a PR to add these changes to the default branch -1. Make sure to fetch the new changes into your local repo: `$ git checkout main && git fetch origin && git merge origin main` -1. Delete the `v1` tag locally and remotely: `$ git tag -d v1 && git push --delete origin v1` -1. Create and push new tags: `$ git tag v1.7.0 && git tag v1 && git push origin --tags` -1. Create the GitHub project release +2. Push the new image: `$ VERSION=1.7.0 make push` +3. Create a release branch and modify `action.yaml` to point to the new image +4. Open and merge a PR to add these changes to the default branch +5. Make sure to fetch the new changes into your local repo: `$ git checkout main && git fetch origin && git merge origin main` +6. Delete the `v1` tag locally and remotely: `$ git tag -d v1 && git push --delete origin v1` +7. Create and push new tags: `$ git tag v1.7.0 && git tag v1 && git push origin --tags` +8. Create the GitHub project release diff --git a/action.yaml b/action.yaml index c515b96..9998363 100644 --- a/action.yaml +++ b/action.yaml @@ -13,9 +13,6 @@ inputs: minimum-approvals: description: Minimum number of approvals to progress workflow required: false - timeout-minutes: - description: Force timeout of your workflow pause - required: false issue-title: description: The custom subtitle for the issue required: false diff --git a/main.go b/main.go index e9944bb..b771f92 100644 --- a/main.go +++ b/main.go @@ -29,6 +29,7 @@ func setActionOutput(name, value string) error { 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.targetRepoOwner, apprv.targetRepoName, apprv.approvalIssueNumber, &github.IssueComment{ Body: &closeComment, @@ -65,7 +66,7 @@ func newCommentLoopChannel(ctx context.Context, apprv *approvalEnvironment, clie switch approved { case approvalStatusApproved: newState := "closed" - closeComment := "All approvers have approved, continuing workflow and closing this issue." + closeComment := fmt.Sprintf("The required number of approvals (%d) has been met; continuing workflow and closing this issue.", apprv.minimumApprovals) _, _, err := client.Issues.CreateComment(ctx, apprv.targetRepoOwner, apprv.targetRepoName, apprv.approvalIssueNumber, &github.IssueComment{ Body: &closeComment, }) From c10ec77612398b015cd669cfc6f931fbde744c8a Mon Sep 17 00:00:00 2001 From: Mateus Caruccio Date: Fri, 7 Mar 2025 06:46:54 +0000 Subject: [PATCH 09/17] Feature: Save output (#132) --- action.yaml | 7 ++++++ approval.go | 39 ++++++++++++++++++++++++++++++++ approval_test.go | 59 ++++++++++++++++++++++++++++++++++++++++++++++++ main.go | 28 +++++++++++------------ 4 files changed, 119 insertions(+), 14 deletions(-) diff --git a/action.yaml b/action.yaml index 9998363..a61c3c9 100644 --- a/action.yaml +++ b/action.yaml @@ -41,6 +41,13 @@ inputs: description: Whether or not to fail the workflow if the approval is denied required: false default: 'true' +outputs: + issue-number: + description: The number of the issue created + issue-url: + description: The URL of the issue created + approval-status: + description: The status of the approval ("approved" or "denied") runs: using: docker image: docker://ghcr.io/trstringer/manual-approval:1.9.1 diff --git a/approval.go b/approval.go index 1ed12d9..9516b64 100644 --- a/approval.go +++ b/approval.go @@ -3,6 +3,7 @@ package main import ( "context" "fmt" + "os" "regexp" "strings" @@ -112,6 +113,44 @@ func (a *approvalEnvironment) createApprovalIssue(ctx context.Context) error { return nil } +func (a *approvalEnvironment) SetActionOutputs(outputs map[string]string) (bool, error) { + outputFile := os.Getenv("GITHUB_OUTPUT") + if outputFile == "" { + return false, nil + } + + f, err := os.OpenFile(outputFile, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644) + + if err != nil { + return false, err + } + defer f.Close() + + var pairs []string + + for key, value := range outputs { + pairs = append(pairs, fmt.Sprintf("%s=%s", key, value)) + } + + // Add a newline before writing the new outputs if the file is not empty. This prevents + // two outputs from being written on the same line. + fileInfo, err := f.Stat() + if err != nil { + return false, err + } + if fileInfo.Size() > 0 { + if _, err := f.WriteString("\n"); err != nil { + return false, err + } + } + + if _, err := f.WriteString(strings.Join(pairs, "\n")); err != nil { + return false, err + } + + return true, nil +} + func approvalFromComments(comments []*github.IssueComment, approvers []string, minimumApprovals int) (approvalStatus, error) { remainingApprovers := make([]string, len(approvers)) copy(remainingApprovers, approvers) diff --git a/approval_test.go b/approval_test.go index afc215f..cefe1b0 100644 --- a/approval_test.go +++ b/approval_test.go @@ -1,6 +1,8 @@ package main import ( + "errors" + "os" "testing" "github.com/google/go-github/v43/github" @@ -427,3 +429,60 @@ func TestDeniedCommentBody(t *testing.T) { }) } } + +func TestSaveOutput(t *testing.T) { + testCases := []struct { + name string + approvalIssueNumber int + env_github_output string + isSuccess bool + }{ + { + name: "save_output_with_env", + approvalIssueNumber: 123, + env_github_output: "./output.txt", + isSuccess: true, + }, + { + name: "fail_save_output_without_env", + approvalIssueNumber: 123, + env_github_output: "", + isSuccess: false, + }, + } + + for _, testCase := range testCases { + t.Run(testCase.name, func(t *testing.T) { + os.Setenv("GITHUB_OUTPUT", testCase.env_github_output) + a := approvalEnvironment{ + client: nil, + repoFullName: "", + repo: "", + repoOwner: "", + runID: -1, + approvalIssueNumber: testCase.approvalIssueNumber, + issueTitle: "", + issueBody: "", + issueApprovers: nil, + minimumApprovals: 0, + } + + os.Remove(testCase.env_github_output) + actual, err := a.SetActionOutputs(nil) + + if err != nil { + t.Fatalf("error creating output file: %v: %v", testCase.env_github_output, err) + } + + if actual != testCase.isSuccess { + t.Fatalf("expected %v but got %v", testCase.isSuccess, actual) + } + + if actual == true { + if _, err := os.Stat(testCase.env_github_output); errors.Is(err, os.ErrNotExist) { + t.Fatalf("expected create output file %v but it was not", testCase.env_github_output) + } + } + }) + } +} diff --git a/main.go b/main.go index b771f92..e439961 100644 --- a/main.go +++ b/main.go @@ -13,19 +13,6 @@ import ( "golang.org/x/oauth2" ) -func setActionOutput(name, value string) error { - f, err := os.OpenFile(os.Getenv("GITHUB_OUTPUT"), os.O_APPEND|os.O_WRONLY, 0600) - if err != nil { - return err - } - defer f.Close() - - if _, err = f.WriteString(fmt.Sprintf("%s=%s\n", name, value)); err != nil { - return err - } - return nil -} - func handleInterrupt(ctx context.Context, client *github.Client, apprv *approvalEnvironment) { newState := "closed" closeComment := "Workflow cancelled, closing issue." @@ -235,6 +222,16 @@ func main() { os.Exit(1) } + outputs := map[string]string { + "issue-number": fmt.Sprintf("%d", apprv.approvalIssueNumber), + "issue-url": apprv.approvalIssue.GetHTMLURL(), + } + _, err = apprv.SetActionOutputs(outputs) + if err != nil { + fmt.Printf("error saving output: %v", err) + os.Exit(1) + } + killSignalChannel := make(chan os.Signal, 1) signal.Notify(killSignalChannel, os.Interrupt) @@ -252,7 +249,10 @@ func main() { } else { approvalStatus = "approved" } - if err := setActionOutput("approval_status", approvalStatus); err != nil { + outputs := map[string]string { + "approval-status": approvalStatus, + } + if _, err := apprv.SetActionOutputs(outputs); err != nil { fmt.Printf("error setting action output: %v\n", err) exitCode = 1 } From e6592a6b3284d0b354d3fa087600e76af66bb238 Mon Sep 17 00:00:00 2001 From: sunny-1651 Date: Fri, 7 Mar 2025 04:38:42 +0530 Subject: [PATCH 10/17] create go sum at build-time with fresh hashes --- .github/workflows/ci.yaml | 2 + .gitignore | 1 + Dockerfile | 1 + Makefile | 4 + go.sum | 383 -------------------------------------- 5 files changed, 8 insertions(+), 383 deletions(-) delete mode 100644 go.sum diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 5fa65b1..dbcfa1a 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -21,6 +21,8 @@ jobs: - name: Checkout uses: actions/checkout@v2 + - name: Refresh module hashsums + run: make tidy - name: Build run: make build env: diff --git a/.gitignore b/.gitignore index a9ad188..03ad3d9 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ .env .idea/ +go.sum diff --git a/Dockerfile b/Dockerfile index 6ec73aa..da0f22e 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,6 +1,7 @@ FROM golang:1.17 AS builder COPY . /var/app WORKDIR /var/app +RUN go mod tidy RUN CGO_ENABLED=0 go build -o app . FROM alpine:3.14 diff --git a/Makefile b/Makefile index 8c3fc9b..56ba8bc 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,10 @@ IMAGE_REPO=ghcr.io/trstringer/manual-approval TARGET_PLATFORM=linux/amd64 +.PHONY: tidy +tidy: + go mod tidy + .PHONY: build build: @if [ -z "$$VERSION" ]; then \ diff --git a/go.sum b/go.sum deleted file mode 100644 index fdb1ce0..0000000 --- a/go.sum +++ /dev/null @@ -1,383 +0,0 @@ -cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= -cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= -cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= -cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= -cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0= -cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To= -cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4= -cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M= -cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc= -cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk= -cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs= -cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc= -cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY= -cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= -cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= -cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= -cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg= -cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc= -cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ= -cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= -cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= -cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= -cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= -cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA= -cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU= -cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= -cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos= -cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk= -cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= -cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= -dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= -github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= -github.com/bradleyfalzon/ghinstallation/v2 v2.0.4/go.mod h1:B40qPqJxWE0jDZgOR1JmaMy+4AY1eBP+IByOvqyAKp0= -github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= -github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= -github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= -github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= -github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= -github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= -github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= -github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= -github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= -github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= -github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= -github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= -github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= -github.com/golang-jwt/jwt/v4 v4.0.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg= -github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= -github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= -github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= -github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= -github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= -github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= -github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= -github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= -github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= -github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= -github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk= -github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= -github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= -github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= -github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= -github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= -github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= -github.com/golang/protobuf v1.4.2 h1:+Z5KGCizgyZCbGh1KZqA0fcLLkwbsjIzS4aV2v7wJX0= -github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= -github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= -github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= -github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= -github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= -github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= -github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.7 h1:81/ik6ipDQS2aGcBfIN5dHDB36BwrStyeAQquSYCV4o= -github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE= -github.com/google/go-github/v41 v41.0.0/go.mod h1:XgmCA5H323A9rtgExdTcnDkcqp6S30AVACCBDOonIxg= -github.com/google/go-github/v43 v43.0.0 h1:y+GL7LIsAIF2NZlJ46ZoC/D1W1ivZasT0lnWHMYPZ+U= -github.com/google/go-github/v43 v43.0.0/go.mod h1:ZkTvvmCXBvsfPpTHXnH/d2hP9Y0cTbvN9kr5xqyXOIc= -github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8= -github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU= -github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= -github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= -github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= -github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= -github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= -github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= -github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= -github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= -github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= -github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= -github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= -github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= -github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= -github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= -github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= -github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= -github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= -github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= -github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= -go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= -go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= -go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= -go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= -golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20210817164053-32db794688a5 h1:HWj/xjIHfjYU5nVXpTM0s39J9CbLn7Cc5a7IC5rwsMQ= -golang.org/x/crypto v0.0.0-20210817164053-32db794688a5/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= -golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek= -golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY= -golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= -golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= -golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= -golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= -golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= -golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= -golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= -golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= -golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= -golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= -golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs= -golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= -golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= -golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= -golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= -golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= -golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= -golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= -golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= -golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= -golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= -golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= -golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= -golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd h1:O7DYs+zxREGLKzKoMQrtrEacpb0ZVXA5rIwylE2Xchk= -golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= -golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= -golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20220309155454-6242fa91716a h1:qfl7ob3DIEs3Ml9oLuPwY2N04gymzAW04WsUQHIClgM= -golang.org/x/oauth2 v0.0.0-20220309155454-6242fa91716a/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= -golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= -golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= -golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= -golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= -golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= -golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= -golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= -golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= -golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= -golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= -golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= -google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= -google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= -google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= -google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= -google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= -google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= -google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= -google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= -google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM= -google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc= -google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= -google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= -google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= -google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= -google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= -google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= -google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c= -google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= -google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= -google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= -google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= -google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= -google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA= -google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U= -google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= -google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA= -google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= -google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= -google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= -google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= -google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= -google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= -google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= -google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= -google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60= -google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= -google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= -google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= -google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= -google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= -google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= -google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= -google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= -google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= -google.golang.org/protobuf v1.25.0 h1:Ejskq+SyPohKW+1uil0JJMtmHCgJPJ/qWTxr8qp+R4c= -google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= -gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= -honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= -honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= -rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= -rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= -rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= From 330ce9b42bd3ee087d843f64b239f2b8e6ffaaff Mon Sep 17 00:00:00 2001 From: Liz MacLean <18120837+lizziemac@users.noreply.github.com> Date: Mon, 17 Mar 2025 10:58:39 -0400 Subject: [PATCH 11/17] Create CONTRIBUTING.md (#160) --- CONTRIBUTING.md | 44 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) create mode 100644 CONTRIBUTING.md diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..bf270b3 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,44 @@ +# Contribution Guide + +Thank you for your interest in contributing to this project! We appreciate your efforts to make this project better. Please follow the guidelines below to ensure a smooth contribution process. + +## Guidelines + +### Code Contributions +- Follow the existing code style and conventions. +- Write meaningful PR titles. +- Ensure your changes do not break existing functionality. +- Run tests before submitting a pull request (PR). +- Build and run your changes in your own environment to ensure they work as expected. +- Add or update documentation if necessary. + +### Submitting a Pull Request +1. Ensure your branch is up to date with the main branch: + + ```sh + git fetch upstream + git checkout main + git merge upstream/main + ``` + +2. Push your changes to your fork: + + ```sh + git push origin feature-branch-name + ``` + +3. Open a PR on GitHub, describing your changes clearly. +4. If your PR is related to an issue, use the [GitHub key words](https://docs.github.com/en/get-started/writing-on-github/working-with-advanced-formatting/using-keywords-in-issues-and-pull-requests#linking-a-pull-request-to-an-issue). +This will ensure that the related issue will automatically close when your change is merged. +5. Address any feedback and update your PR as necessary. + +### Issue Reporting +- **Check for existing issues before opening a new one.** +- Provide clear, concise descriptions with steps to reproduce. +- Include logs, screenshots, or example code if relevant. +- Suggest possible solutions if applicable. + +## License +By contributing, you agree that your contributions will be licensed under the project's [LICENSE](LICENSE). + +Thank you for contributing! From ef4dddd64bf9eca035ffe51d23f12d20e3a83613 Mon Sep 17 00:00:00 2001 From: Liz MacLean <18120837+lizziemac@users.noreply.github.com> Date: Mon, 17 Mar 2025 10:58:51 -0400 Subject: [PATCH 12/17] Issue templates (#161) --- .github/ISSUE_TEMPLATE/bug_report.md | 29 +++++++++++++++++++++++ .github/ISSUE_TEMPLATE/feature_request.md | 22 +++++++++++++++++ 2 files changed, 51 insertions(+) create mode 100644 .github/ISSUE_TEMPLATE/bug_report.md create mode 100644 .github/ISSUE_TEMPLATE/feature_request.md diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 0000000..d0c25c5 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,29 @@ +--- +name: Bug report +about: Create a report to help us improve +title: '' +labels: '' +assignees: '' + +--- + +- [ ] I have searched through the current issues and did not find any that were related. + +**Describe the bug** +A clear and concise description of what the bug is. + +**To Reproduce** +Steps to reproduce the behavior: +1. Go to '...' +2. Click on '....' +3. Scroll down to '....' +4. See error + +**Expected behavior** +A clear and concise description of what you expected to happen. + +**Screenshots** +If applicable, add screenshots to help explain your problem. + +**Additional context** +Add any other context about the problem here. diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md new file mode 100644 index 0000000..6089845 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -0,0 +1,22 @@ +--- +name: Feature request +about: Suggest an idea for this project +title: '' +labels: '' +assignees: '' + +--- + +- [ ] I have searched through the current feature requests and did not find any that were related. + +**Is your feature request related to a problem? Please describe.** +A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] + +**Describe the solution you'd like** +A clear and concise description of what you want to happen. + +**Describe alternatives you've considered** +A clear and concise description of any alternative solutions or features you've considered. + +**Additional context** +Add any other context or screenshots about the feature request here. From c1ebf589e26f42869b0ee9f5fde3cf15e6fef997 Mon Sep 17 00:00:00 2001 From: Simeon Widdis Date: Sun, 20 Apr 2025 08:36:57 -0700 Subject: [PATCH 13/17] Make name comparison case-insensitive (#172) Signed-off-by: Simeon Widdis --- approval.go | 10 +++++----- approval_test.go | 14 ++++++++++++++ 2 files changed, 19 insertions(+), 5 deletions(-) diff --git a/approval.go b/approval.go index 9516b64..8487e26 100644 --- a/approval.go +++ b/approval.go @@ -136,12 +136,12 @@ func (a *approvalEnvironment) SetActionOutputs(outputs map[string]string) (bool, // two outputs from being written on the same line. fileInfo, err := f.Stat() if err != nil { - return false, err + return false, err } if fileInfo.Size() > 0 { - if _, err := f.WriteString("\n"); err != nil { - return false, err - } + if _, err := f.WriteString("\n"); err != nil { + return false, err + } } if _, err := f.WriteString(strings.Join(pairs, "\n")); err != nil { @@ -194,7 +194,7 @@ func approvalFromComments(comments []*github.IssueComment, approvers []string, m func approversIndex(approvers []string, name string) int { for idx, approver := range approvers { - if approver == name { + if strings.EqualFold(approver, name) { return idx } } diff --git a/approval_test.go b/approval_test.go index cefe1b0..efddf7f 100644 --- a/approval_test.go +++ b/approval_test.go @@ -3,6 +3,7 @@ package main import ( "errors" "os" + "strings" "testing" "github.com/google/go-github/v43/github" @@ -16,6 +17,8 @@ func TestApprovalFromComments(t *testing.T) { bodyDenied := "Denied" bodyPending := "not approval or denial" + login1u := strings.ToUpper(login1) + testCases := []struct { name string comments []*github.IssueComment @@ -160,6 +163,17 @@ func TestApprovalFromComments(t *testing.T) { expectedStatus: approvalStatusPending, minimumApprovals: 2, }, + { + name: "single_approver_single_comment_approved_case_insensitive", + comments: []*github.IssueComment{ + { + User: &github.User{Login: &login1u}, + Body: &bodyApproved, + }, + }, + approvers: []string{login1}, + expectedStatus: approvalStatusApproved, + }, } for _, testCase := range testCases { From bfb0ba38d02a528a2c101381809dc6c3062732c1 Mon Sep 17 00:00:00 2001 From: Sunny <55059942+snskArora@users.noreply.github.com> Date: Sat, 26 Apr 2025 12:02:02 +0530 Subject: [PATCH 14/17] Adding support for issue-body with more than 65536 words, issue body to be created as an issue comment (#167) * push a built package to ghcr * adding err catch * auth check * auth update * update uname * update uname * testing with splitted issues * minor fix * revert testing changes * Update approval.go --- .github/workflows/ci.yaml | 1 + Makefile | 6 +-- approval.go | 86 +++++++++++++++++++++++++++++++++++++-- 3 files changed, 87 insertions(+), 6 deletions(-) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index dbcfa1a..c0d4e03 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -27,6 +27,7 @@ jobs: run: make build env: VERSION: latest + - name: Test run: make test - name: Lint diff --git a/Makefile b/Makefile index 56ba8bc..54f20b9 100644 --- a/Makefile +++ b/Makefile @@ -7,7 +7,7 @@ tidy: .PHONY: build build: - @if [ -z "$$VERSION" ]; then \ + @if [ -z "$(VERSION)" ]; then \ echo "VERSION is required"; \ exit 1; \ fi @@ -15,11 +15,11 @@ build: .PHONY: push push: - @if [ -z "$$VERSION" ]; then \ + @if [ -z "$(VERSION)" ]; then \ echo "VERSION is required"; \ exit 1; \ fi - docker push $(IMAGE_REPO):$$VERSION + docker push $(IMAGE_REPO):$(VERSION) .PHONY: test test: diff --git a/approval.go b/approval.go index 8487e26..2b7baa1 100644 --- a/approval.go +++ b/approval.go @@ -85,9 +85,6 @@ func (a *approvalEnvironment) createApprovalIssue(ctx context.Context) error { formatAcceptedWords(deniedWords), ) - if a.issueBody != "" { - issueBody = fmt.Sprintf(">%s\n>\n%s", a.issueBody, issueBody) - } issueBody = fmt.Sprintf(">[!NOTE]\n%s", issueBody) var err error @@ -109,6 +106,16 @@ func (a *approvalEnvironment) createApprovalIssue(ctx context.Context) error { } a.approvalIssueNumber = a.approvalIssue.GetNumber() + bodyChunks := splitLongString(a.issueBody) + for _, chunk := range bodyChunks { + _, _, err = a.client.Issues.CreateComment(ctx, a.targetRepoOwner, a.targetRepoName, *a.approvalIssue.Number, &github.IssueComment{ + Body: &chunk, + }) + if err != nil { + return fmt.Errorf("failed to add comment chunk to issue: %w", err) + } + } + fmt.Printf("Issue created: %s\n", a.approvalIssue.GetHTMLURL()) return nil } @@ -244,3 +251,76 @@ func formatAcceptedWords(words []string) string { return strings.Join(quotedWords, ", ") } + +func splitLongLine(line string, maxL int) ([]string, bool) { + if len(line) <= maxL { + return []string{line}, false + } + + words := strings.Fields(line) + var result []string + var currentLine string + + for _, word := range words { + if len(currentLine)+len(word)+1 > maxL { + result = append(result, currentLine) + currentLine = word + } else { + if currentLine != "" { + currentLine += " " + } + currentLine += word + } + } + if currentLine != "" { + result = append(result, currentLine) + } + return result, true +} + +func splitLongString(input string) []string { + maxLength := 65536 + var result []string + + lines := strings.Split(input, "\n") + currentChunk := strings.Builder{} + currentLength := 0 + + for i, line := range lines { + lineLength := len(line) + if i < len(lines)-1 { + lineLength++ + } + + if currentLength+lineLength > maxLength { + if currentChunk.Len() > 0 { + result = append(result, currentChunk.String()) + currentChunk.Reset() + currentLength = 0 + } + } + + lineSplit, isLongLine := splitLongLine(line, maxLength) + if isLongLine { + if currentChunk.Len() > 0 { + result = append(result, currentChunk.String()) + currentChunk.Reset() + } + result = append(result, lineSplit[:len(lineSplit)-1]...) + currentChunk.WriteString(lineSplit[len(lineSplit)-1]) + currentLength = len(lineSplit[len(lineSplit)-1]) + } else { + currentChunk.WriteString(line) + currentLength += lineLength + } + + if i < len(lines)-1 { + currentChunk.WriteString("\n") + } + } + if currentChunk.Len() > 0 { + result = append(result, currentChunk.String()) + } + return result +} + From 9ef3307c2579bc7ba1c3f292c7942b9ce57a4186 Mon Sep 17 00:00:00 2001 From: Liz MacLean <18120837+lizziemac@users.noreply.github.com> Date: Sat, 10 May 2025 11:10:29 -0400 Subject: [PATCH 15/17] Update README.md to have a more explicit callout about timeout-minutes (#180) --- README.md | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 37317e0..4c87544 100644 --- a/README.md +++ b/README.md @@ -108,14 +108,30 @@ jobs: If you'd like to force a timeout of your workflow pause, you can specify `timeout-minutes` at either the [step](https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepstimeout-minutes) level or the [job](https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idtimeout-minutes) level. +> **Note:** The `timeout-minutes` option has been removed from the `manual-approval` inputs, as it did nothing and incorrectly assured users that they were in fact +> getting timeout behavior. Please use one of the below two approaches instead. +> +> If you are currently using `timeout-minutes` as a `manual-approval` input, you may see a warning, but this will not break your action. + For instance, if you want your manual approval step to timeout after an hour you could do the following: ```yaml -steps: - - uses: trstringer/manual-approval@v1 - timeout-minutes: 60 +jobs: + approval: + steps: + - uses: trstringer/manual-approval@v1 + timeout-minutes: 60 ... ``` +or +```yaml +jobs: + approval: + timeout-minutes: 10 + steps: + - uses: trstringer/manual-approval@v1 + +``` ## Permissions From fb0bfeda5ff2d043dcad82c10be2db6e46086662 Mon Sep 17 00:00:00 2001 From: Liz MacLean <18120837+lizziemac@users.noreply.github.com> Date: Tue, 20 May 2025 18:17:01 -0400 Subject: [PATCH 16/17] Fix typo in README related to the action output of approval status (#181) --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 4c87544..86a2034 100644 --- a/README.md +++ b/README.md @@ -51,7 +51,7 @@ steps: ### Outputs -* `approval_status` is a string that indicates the final status of the approval. This will be either `approved` or `denied`. +* `approval-status` is a string that indicates the final status of the approval. This will be either `approved` or `denied`. ### Creating Issues in a different repository From a1f96b91e22565d2730d88ae055c8dc333e471c1 Mon Sep 17 00:00:00 2001 From: Sunny <55059942+snskArora@users.noreply.github.com> Date: Tue, 27 May 2025 01:44:38 +0530 Subject: [PATCH 17/17] Updates for release 1.10.0 (#183) --- Dockerfile | 2 +- action.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Dockerfile b/Dockerfile index da0f22e..d757e2e 100644 --- a/Dockerfile +++ b/Dockerfile @@ -5,7 +5,7 @@ RUN go mod tidy RUN CGO_ENABLED=0 go build -o app . FROM alpine:3.14 -LABEL org.opencontainers.image.source https://github.com/trstringer/manual-approval +LABEL org.opencontainers.image.source=https://github.com/trstringer/manual-approval RUN apk update && apk add ca-certificates COPY --from=builder /var/app/app /var/app/app CMD ["/var/app/app"] diff --git a/action.yaml b/action.yaml index a61c3c9..0126c0a 100644 --- a/action.yaml +++ b/action.yaml @@ -50,4 +50,4 @@ outputs: description: The status of the approval ("approved" or "denied") runs: using: docker - image: docker://ghcr.io/trstringer/manual-approval:1.9.1 + image: docker://ghcr.io/trstringer/manual-approval:1.10.0