package main import ( "fmt" "os" "strconv" "strings" "codeberg.org/mvdkleijn/forgejo-sdk/forgejo/v3" ) func retrieveApprovers(client *forgejo.Client, repoOwner string) ([]string, error) { 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]) } for _, approverUser := range requiredApprovers { expandedUsers := expandGroupFromUser(client, repoOwner, approverUser, workflowInitiator, shouldExcludeWorkflowInitiator) if len(expandedUsers) > 0 { 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) } } approvers = deduplicateUsers(approvers) minimumApprovalsRaw := os.Getenv(envVarMinimumApprovals) minimumApprovals := len(approvers) var err error if minimumApprovalsRaw != "" { minimumApprovals, err = strconv.Atoi(minimumApprovalsRaw) if err != nil { return nil, fmt.Errorf("error parsing minimum number of approvals: %w", err) } } if minimumApprovals > len(approvers) { return nil, fmt.Errorf("error: minimum required approvals (%d) is greater than the total number of approvers (%d)", minimumApprovals, len(approvers)) } return approvers, nil } func expandGroupFromUser(client *forgejo.Client, org string, userOrTeam string, workflowInitiator string, shouldExcludeWorkflowInitiator bool) []string { fmt.Printf("Attempting to expand user %s/%s as a group (may not succeed)\n", org, userOrTeam) // Try to find a team with this name in the org teams, _, err := client.SearchOrgTeams(org, &forgejo.SearchTeamsOptions{Query: userOrTeam}) if err != nil { fmt.Printf("Error searching teams: %v\n", err) return nil } var team *forgejo.Team for _, t := range teams { if strings.EqualFold(t.Name, userOrTeam) { team = t break } } if team == nil { return nil } users, _, err := client.ListTeamMembers(team.ID, forgejo.ListTeamMembersOptions{}) if err != nil { fmt.Printf("Error listing team members: %v\n", err) return nil } userNames := make([]string, 0, len(users)) for _, user := range users { userName := user.UserName 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 } func deduplicateUsers(users []string) []string { uniqValuesByKey := make(map[string]bool) uniqUsers := []string{} for _, user := range users { if _, ok := uniqValuesByKey[user]; !ok { uniqValuesByKey[user] = true uniqUsers = append(uniqUsers, user) } } return uniqUsers }