From 753e13e9357f41a29eba2d9f2f076ca0bf3c1b2f Mon Sep 17 00:00:00 2001 From: Timothy Ng <5664347+timorthi@users.noreply.github.com> Date: Thu, 10 Nov 2022 09:25:31 -0800 Subject: [PATCH] Option to exclude workflow initiator (`GITHUB_ACTOR`) as an approver (#59) * Add constant for GITHUB_ACTOR env var * autoformat * Ignore workflow initiator * Fix incorrect if-else * refactor: camelcase userName * fix typo * Add allow-workflow-initiator-as-approver input * Add shouldIncludeWorkflowInitiator * Add usage & description for allow-workflow-initiator-as-approver * Clearer input description * refactor: rename inputs/vars to 'exclude' * update error msg with correct input name * docs: move note on optional/default to description --- README.md | 4 +++- action.yaml | 3 +++ approvers.go | 25 +++++++++++++++++++------ constants.go | 16 +++++++++------- main.go | 2 +- 5 files changed, 35 insertions(+), 15 deletions(-) diff --git a/README.md b/README.md index 085854b..18a78c2 100644 --- a/README.md +++ b/README.md @@ -32,11 +32,13 @@ steps: approvers: user1,user2,org-team1 minimum-approvals: 1 issue-title: "Deploying v1.3.5 to prod from staging" + exclude-workflow-initiator-as-approver: false ``` - `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 appened to the title of the issue. +- `issue-title` is a string that will be appended to the title 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. ## Org team approver diff --git a/action.yaml b/action.yaml index 0572f18..9fd7fdb 100644 --- a/action.yaml +++ b/action.yaml @@ -16,6 +16,9 @@ inputs: issue-title: description: The custom subtitle for the issue 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 runs: using: docker image: docker://ghcr.io/trstringer/manual-approval:1.7.0 diff --git a/approvers.go b/approvers.go index 19a8f01..d7aae15 100644 --- a/approvers.go +++ b/approvers.go @@ -11,19 +11,27 @@ import ( ) func retrieveApprovers(client *github.Client, repoOwner string) ([]string, error) { - approvers := []string{} + workflowInitiator := os.Getenv(envVarWorkflowInitiator) + shouldExcludeWorkflowInitiatorRaw := os.Getenv(envVarExcludeWorkflowInitiatorAsApprover) + shouldExcludeWorkflowInitiator, parseBoolErr := strconv.ParseBool(shouldExcludeWorkflowInitiatorRaw) + if parseBoolErr != nil { + return nil, fmt.Errorf("error parsing exclude-workflow-initiator-as-approver flag: %w", parseBoolErr) + } + approvers := []string{} requiredApproversRaw := os.Getenv(envVarApprovers) requiredApprovers := strings.Split(requiredApproversRaw, ",") for i := range requiredApprovers { - requiredApprovers[i] = strings.TrimSpace(requiredApprovers[i]) + requiredApprovers[i] = strings.TrimSpace(requiredApprovers[i]) } - + for _, approverUser := range requiredApprovers { - expandedUsers := expandGroupFromUser(client, repoOwner, approverUser) + expandedUsers := expandGroupFromUser(client, repoOwner, approverUser, workflowInitiator, shouldExcludeWorkflowInitiator) if expandedUsers != nil { approvers = append(approvers, expandedUsers...) + } else if strings.EqualFold(workflowInitiator, approverUser) && shouldExcludeWorkflowInitiator { + fmt.Printf("Not adding user '%s' as an approver as they are the workflow initiator\n", approverUser) } else { approvers = append(approvers, approverUser) } @@ -48,7 +56,7 @@ func retrieveApprovers(client *github.Client, repoOwner string) ([]string, error return approvers, nil } -func expandGroupFromUser(client *github.Client, org, userOrTeam string) []string { +func expandGroupFromUser(client *github.Client, org, userOrTeam string, workflowInitiator string, shouldExcludeWorkflowInitiator bool) []string { fmt.Printf("Attempting to expand user %s/%s as a group (may not succeed)\n", org, userOrTeam) users, _, err := client.Teams.ListTeamMembersBySlug(context.Background(), org, userOrTeam, &github.TeamListTeamMembersOptions{}) if err != nil { @@ -58,7 +66,12 @@ func expandGroupFromUser(client *github.Client, org, userOrTeam string) []string userNames := make([]string, 0, len(users)) for _, user := range users { - userNames = append(userNames, user.GetLogin()) + userName := user.GetLogin() + if strings.EqualFold(userName, workflowInitiator) && shouldExcludeWorkflowInitiator { + fmt.Printf("Not adding user '%s' from group '%s' as an approver as they are the workflow initiator\n", userName, userOrTeam) + } else { + userNames = append(userNames, userName) + } } return userNames diff --git a/constants.go b/constants.go index 2d1b532..3124ec2 100644 --- a/constants.go +++ b/constants.go @@ -5,13 +5,15 @@ import "time" const ( pollingInterval time.Duration = 10 * time.Second - envVarRepoFullName string = "GITHUB_REPOSITORY" - envVarRunID string = "GITHUB_RUN_ID" - envVarRepoOwner string = "GITHUB_REPOSITORY_OWNER" - envVarToken string = "INPUT_SECRET" - envVarApprovers string = "INPUT_APPROVERS" - envVarMinimumApprovals string = "INPUT_MINIMUM-APPROVALS" - envVarIssueTitle string = "INPUT_ISSUE-TITLE" + envVarRepoFullName string = "GITHUB_REPOSITORY" + envVarRunID string = "GITHUB_RUN_ID" + envVarRepoOwner string = "GITHUB_REPOSITORY_OWNER" + envVarWorkflowInitiator string = "GITHUB_ACTOR" + envVarToken string = "INPUT_SECRET" + envVarApprovers string = "INPUT_APPROVERS" + envVarMinimumApprovals string = "INPUT_MINIMUM-APPROVALS" + envVarIssueTitle string = "INPUT_ISSUE-TITLE" + envVarExcludeWorkflowInitiatorAsApprover string = "INPUT_EXCLUDE-WORKFLOW-INITIATOR-AS-APPROVER" ) var ( diff --git a/main.go b/main.go index dfc69fe..efa4ba9 100644 --- a/main.go +++ b/main.go @@ -107,7 +107,7 @@ func newGithubClient(ctx context.Context) (*github.Client, error) { apiUrl, apiUrlPresent := os.LookupEnv("GITHUB_API_URL") if serverUrlPresent { - if ! apiUrlPresent { + if !apiUrlPresent { apiUrl = serverUrl } return github.NewEnterpriseClient(apiUrl, serverUrl, tc)