Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ Code Ownership & Review Assignment Tool - GitHub CODEOWNERS but better

[![Go Report Card](https://goreportcard.com/badge/github.com/multimediallc/codeowners-plus)](https://goreportcard.com/report/github.com/multimediallc/codeowners-plus?kill_cache=1)
[![Tests](https://github.com/multimediallc/codeowners-plus/actions/workflows/go.yml/badge.svg)](https://github.com/multimediallc/codeowners-plus/actions/workflows/go.yml)
![Coverage](https://img.shields.io/badge/Coverage-81.8%25-brightgreen)
![Coverage](https://img.shields.io/badge/Coverage-82.8%25-brightgreen)
[![License](https://img.shields.io/badge/License-BSD%203--Clause-blue.svg)](https://opensource.org/licenses/BSD-3-Clause)
[![Contributor Covenant](https://img.shields.io/badge/Contributor%20Covenant-2.1-4baaaa.svg)](CODE_OF_CONDUCT.md)

Expand Down
51 changes: 27 additions & 24 deletions internal/app/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,10 @@ func NewOutputData(co codeowners.CodeOwners) *OutputData {
fileOwners := make(map[string][]string)
fileOptional := make(map[string][]string)
for file, reviewers := range co.FileRequired() {
fileOwners[file] = reviewers.Flatten()
fileOwners[file] = codeowners.OriginalStrings(reviewers.Flatten())
}
for file, reviewers := range co.FileOptional() {
fileOptional[file] = reviewers.Flatten()
fileOptional[file] = codeowners.OriginalStrings(reviewers.Flatten())
}
return &OutputData{
FileOwners: fileOwners,
Expand Down Expand Up @@ -173,17 +173,15 @@ func (a *App) processApprovalsAndReviewers() (bool, string, []string, error) {
// Get all required owners before filtering
allRequiredOwners := a.codeowners.AllRequired()
allRequiredOwnerNames := allRequiredOwners.Flatten()
a.printDebug("All Required Owners: %s\n", allRequiredOwnerNames)
a.printDebug("All Required Owners: %s\n", codeowners.OriginalStrings(allRequiredOwnerNames))

// Get optional reviewers
allOptionalReviewerNames := a.codeowners.AllOptional().Flatten()
allOptionalReviewerNames = f.Filtered(allOptionalReviewerNames, func(name string) bool {
return !slices.Contains(allRequiredOwnerNames, name)
})
a.printDebug("All Optional Reviewers: %s\n", allOptionalReviewerNames)
allOptionalReviewerNames = codeowners.FilterOutNames(allOptionalReviewerNames, allRequiredOwnerNames)
a.printDebug("All Optional Reviewers: %s\n", codeowners.OriginalStrings(allOptionalReviewerNames))

// Initialize user reviewer map
if err := a.client.InitUserReviewerMap(allRequiredOwnerNames); err != nil {
if err := a.client.InitUserReviewerMap(codeowners.OriginalStrings(allRequiredOwnerNames)); err != nil {
return false, message, nil, fmt.Errorf("InitUserReviewerMap Error: %v", err)
}

Expand Down Expand Up @@ -229,7 +227,8 @@ func (a *App) processApprovalsAndReviewers() (bool, string, []string, error) {
unapprovedOwners := a.codeowners.AllRequired()
maxReviewsMet := false
if a.Conf.MaxReviews != nil && *a.Conf.MaxReviews > 0 {
if validApprovalCount >= *a.Conf.MaxReviews && len(f.Intersection(unapprovedOwners.Flatten(), a.Conf.UnskippableReviewers)) == 0 {
unskippableReviewerSlugs := codeowners.NewSlugs(a.Conf.UnskippableReviewers)
if validApprovalCount >= *a.Conf.MaxReviews && !unapprovedOwners.ContainsAny(unskippableReviewerSlugs) {
maxReviewsMet = true
}
}
Expand All @@ -239,7 +238,7 @@ func (a *App) processApprovalsAndReviewers() (bool, string, []string, error) {
if err != nil {
return false, message, nil, fmt.Errorf("failed to add review status comment: %w", err)
}
err = a.addOptionalCcComment(allOptionalReviewerNames)
err = a.addOptionalCcComment(codeowners.OriginalStrings(allOptionalReviewerNames))
if err != nil {
return false, message, nil, fmt.Errorf("failed to add optional CC comment: %w", err)
}
Expand All @@ -260,7 +259,8 @@ func (a *App) processApprovalsAndReviewers() (bool, string, []string, error) {
}

// Collect still required data
stillRequired := unapprovedOwners.Flatten()
stillRequiredSlugs := unapprovedOwners.Flatten()
stillRequired := codeowners.OriginalStrings(stillRequiredSlugs)

// Exit if there are any unapproved codeowner teams
if len(unapprovedOwners) > 0 && !maxReviewsMet {
Expand Down Expand Up @@ -289,15 +289,15 @@ func (a *App) processApprovalsAndReviewers() (bool, string, []string, error) {
} else {
currentlyRequestedSet := make(map[string]struct{}, len(currentlyRequestedOwners))
for _, owner := range currentlyRequestedOwners {
currentlyRequestedSet[owner] = struct{}{}
currentlyRequestedSet[owner.Normalized()] = struct{}{}
}
ownersToReRequest := f.Filtered(allRequiredOwnerNames, func(owner string) bool {
_, exists := currentlyRequestedSet[owner]
ownersToReRequest := f.Filtered(allRequiredOwnerNames, func(owner codeowners.Slug) bool {
_, exists := currentlyRequestedSet[owner.Normalized()]
return !exists
})
if len(ownersToReRequest) > 0 {
a.printDebug("Re-requesting Reviews from satisfied team(s) to meet min_reviews: %s\n", ownersToReRequest)
if err := a.client.RequestReviewers(ownersToReRequest); err != nil {
a.printDebug("Re-requesting Reviews from satisfied team(s) to meet min_reviews: %s\n", codeowners.OriginalStrings(ownersToReRequest))
if err := a.client.RequestReviewers(codeowners.OriginalStrings(ownersToReRequest)); err != nil {
a.printWarn("WARNING: Error re-requesting reviewers: %v\n", err)
}
}
Expand Down Expand Up @@ -431,9 +431,12 @@ func (a *App) processTokenOwnerApproval() (*gh.CurrentApproval, error) {
}

func (a *App) processApprovals(ghApprovals []*gh.CurrentApproval) (int, error) {
fileReviewers := f.MapMap(a.codeowners.FileRequired(), func(reviewers codeowners.ReviewerGroups) []string { return reviewers.Flatten() })
// Create file reviewer map with normalized names for case-insensitive comparison
fileReviewers := f.MapMap(a.codeowners.FileRequired(), func(reviewers codeowners.ReviewerGroups) []string {
return codeowners.NormalizedStrings(reviewers.Flatten())
})

var approvers []string
var approvers []codeowners.Slug
var approvalsToDismiss []*gh.CurrentApproval

if a.Conf.DisableSmartDismissal {
Expand Down Expand Up @@ -467,27 +470,27 @@ func (a *App) requestReviews() error {

unapprovedOwners := a.codeowners.AllRequired()
unapprovedOwnerNames := unapprovedOwners.Flatten()
a.printDebug("Remaining Required Owners: %s\n", unapprovedOwnerNames)
a.printDebug("Remaining Required Owners: %s\n", codeowners.OriginalStrings(unapprovedOwnerNames))

currentlyRequestedOwners, err := a.client.GetCurrentlyRequested()
if err != nil {
return fmt.Errorf("GetCurrentlyRequested Error: %v", err)
}
a.printDebug("Currently Requested Owners: %s\n", currentlyRequestedOwners)
a.printDebug("Currently Requested Owners: %s\n", codeowners.OriginalStrings(currentlyRequestedOwners))

previousReviewers, err := a.client.GetAlreadyReviewed()
if err != nil {
return fmt.Errorf("GetAlreadyReviewed Error: %v", err)
}
a.printDebug("Already Reviewed Owners: %s\n", previousReviewers)
a.printDebug("Already Reviewed Owners: %s\n", codeowners.OriginalStrings(previousReviewers))

filteredOwners := unapprovedOwners.FilterOut(currentlyRequestedOwners...)
filteredOwners = filteredOwners.FilterOut(previousReviewers...)
filteredOwnerNames := filteredOwners.Flatten()

if len(filteredOwners) > 0 {
a.printDebug("Requesting Reviews from: %s\n", filteredOwnerNames)
if err := a.client.RequestReviewers(filteredOwnerNames); err != nil {
a.printDebug("Requesting Reviews from: %s\n", codeowners.OriginalStrings(filteredOwnerNames))
if err := a.client.RequestReviewers(codeowners.OriginalStrings(filteredOwnerNames)); err != nil {
return fmt.Errorf("RequestReviewers Error: %v", err)
}
}
Expand All @@ -514,7 +517,7 @@ func (a *App) getFileOwnersMapToString(fileReviewers map[string]codeowners.Revie
for _, file := range files {
reviewers := fileReviewers[file]
// builder.WriteString error return is always nil
_, _ = fmt.Fprintf(&builder, "- %s: %+v\n", file, reviewers.Flatten())
_, _ = fmt.Fprintf(&builder, "- %s: %+v\n", file, codeowners.OriginalStrings(reviewers.Flatten()))
}
return builder.String()
}
Loading