diff --git a/github/util_v4_branch_protection.go b/github/util_v4_branch_protection.go index eae3467cda..ca16953709 100644 --- a/github/util_v4_branch_protection.go +++ b/github/util_v4_branch_protection.go @@ -4,6 +4,7 @@ import ( "context" "fmt" "log" + "sort" "strings" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" @@ -258,6 +259,7 @@ func branchProtectionResourceData(d *schema.ResourceData, meta interface{}) (Bra pushActorIDs = append(pushActorIDs, v.(string)) } if len(pushActorIDs) > 0 { + sort.Strings(pushActorIDs) data.PushActorIDs = pushActorIDs } } @@ -460,33 +462,53 @@ func setBypassPullRequestActorIDs(actors []BypassPullRequestActorTypes, data Bra func setPushActorIDs(actors []PushActorTypes, data BranchProtectionResourceData, meta interface{}) []string { pushActors := make([]string, 0, len(actors)) - orgName := meta.(*Owner).name + // Create a map to track seen IDs to prevent duplicates + seenIDs := make(map[string]struct{}) + for _, a := range actors { - IsID := false - for _, v := range data.PushActorIDs { - if (a.Actor.Team.ID != nil && a.Actor.Team.ID.(string) == v) || (a.Actor.User.ID != nil && a.Actor.User.ID.(string) == v) || (a.Actor.App.ID != nil && a.Actor.App.ID.(string) == v) { - pushActors = append(pushActors, v) - IsID = true - break - } + var id string + if a.Actor.Team.ID != nil { + id = a.Actor.Team.ID.(string) + } else if a.Actor.User.ID != nil { + id = a.Actor.User.ID.(string) + } else if a.Actor.App.ID != nil { + id = a.Actor.App.ID.(string) + } else if a.Actor.Team.Slug != "" { + formattedID = orgName + "/" + string(a.Actor.Team.Slug) + } else if a.Actor.User.Login != "" { + formattedID = "/" + string(a.Actor.User.Login) } - if !IsID { - if a.Actor.Team.Slug != "" { - pushActors = append(pushActors, orgName+"/"+string(a.Actor.Team.Slug)) - continue - } - if a.Actor.User.Login != "" { - pushActors = append(pushActors, "/"+string(a.Actor.User.Login)) - continue - } - if a.Actor.App != (Actor{}) { - pushActors = append(pushActors, a.Actor.App.ID.(string)) + + if id != "" { + if _, exists := seenIDs[id]; !exists { + pushActors = append(pushActors, id) + seenIDs[id] = struct{}{} } } } - return pushActors + + // Sort for consistent ordering + sort.Strings(pushActors) + + // Validate against provided IDs + idMap := make(map[string]bool) + for _, v := range data.PushActorIDs { + idMap[v] = true + } + + // Only keep IDs that were in the original PushActorIDs + validPushActors := make([]string, 0, len(pushActors)) + for _, actor := range pushActors { + if idMap[actor] { + validPushActors = append(validPushActors, actor) + } + } + + sort.Strings(validPushActors) + log.Printf("[DEBUG] Final sorted and validated pushActors: %v", validPushActors) + return validPushActors } func setApprovingReviews(protection BranchProtectionRule, data BranchProtectionResourceData, meta interface{}) interface{} { @@ -538,13 +560,19 @@ func setPushes(protection BranchProtectionRule, data BranchProtectionResourceDat pushAllowances := protection.PushAllowances.Nodes pushActors := setPushActorIDs(pushAllowances, data, meta) + // If we have no push actors but restrictions are enabled, return an empty list + // rather than nil to prevent drift + if len(pushActors) == 0 && protection.RestrictsPushes { + log.Printf("[DEBUG] No push actors found, returning empty list") + pushActors = make([]string, 0) + } + restrictsPushes := []interface{}{ map[string]interface{}{ PROTECTION_BLOCKS_CREATIONS: protection.BlocksCreations, PROTECTION_PUSH_ALLOWANCES: pushActors, }, } - return restrictsPushes } @@ -611,15 +639,34 @@ func getBranchProtectionID(repoID githubv4.ID, pattern string, meta interface{}) func getActorIds(data []string, meta interface{}) ([]string, error) { var actors []string + log.Printf("[DEBUG] getActorIds input data: %v", data) + + // Create a map to track processed IDs and prevent duplicates + seen := make(map[string]bool) + for _, v := range data { + if v == "" { + continue + } + id, err := getNodeIDv4(v, meta) if err != nil { + log.Printf("[DEBUG] Error getting node ID for %s: %v", v, err) return []string{}, err } - log.Printf("[DEBUG] Retrieved node ID for user/team : %s - node ID : %s", v, id) - actors = append(actors, id) + + log.Printf("[DEBUG] Retrieved node ID for user/team: %s - node ID: %s", v, id) + + if !seen[id] { + actors = append(actors, id) + seen[id] = true + } else { + log.Printf("[DEBUG] Skipping duplicate ID: %s", id) + } } + sort.Strings(actors) + log.Printf("[DEBUG] Final sorted actor IDs: %v", actors) return actors, nil }