@@ -2,7 +2,12 @@ package github
22
33import (
44 "context"
5+ "encoding/json"
6+ "fmt"
7+ "io"
8+ "net/http"
59 "reflect"
10+ "regexp"
611 "sort"
712
813 "github.com/google/go-github/v84/github"
@@ -880,6 +885,110 @@ func flattenRules(ctx context.Context, rules *github.RepositoryRulesetRules, org
880885 return []any {rulesMap }
881886}
882887
888+ // reviewerStringIDPattern matches JSON "id" fields with quoted integer values.
889+ // The GitHub API returns reviewer.id as a JSON string (e.g., "id": "12345") but
890+ // go-github's RulesetReviewer.ID expects *int64, causing unmarshal failures.
891+ // See https://github.com/integrations/terraform-provider-github/issues/3340
892+ var reviewerStringIDPattern = regexp .MustCompile (`"id"\s*:\s*"(\d+)"` )
893+
894+ // fixReviewerStringIDs converts quoted integer "id" fields in raw JSON to
895+ // unquoted integers so that go-github can unmarshal them into *int64 fields.
896+ func fixReviewerStringIDs (data []byte ) []byte {
897+ return reviewerStringIDPattern .ReplaceAll (data , []byte (`"id":$1` ))
898+ }
899+
900+ // doRulesetRequest executes an HTTP request against the GitHub API and
901+ // unmarshals the response into a RepositoryRuleset. It applies
902+ // fixReviewerStringIDs to the response body before unmarshaling to work around
903+ // the GitHub API returning reviewer.id as a string instead of a number.
904+ func doRulesetRequest (ctx context.Context , client * github.Client , req * http.Request ) (* github.RepositoryRuleset , * github.Response , error ) {
905+ resp , err := client .BareDo (ctx , req )
906+ if err != nil {
907+ return nil , resp , err
908+ }
909+ defer resp .Body .Close ()
910+
911+ body , err := io .ReadAll (resp .Body )
912+ if err != nil {
913+ return nil , resp , err
914+ }
915+
916+ body = fixReviewerStringIDs (body )
917+
918+ var rs * github.RepositoryRuleset
919+ if err := json .Unmarshal (body , & rs ); err != nil {
920+ return nil , resp , err
921+ }
922+
923+ return rs , resp , nil
924+ }
925+
926+ // createRepoRuleset creates a repository ruleset, working around the
927+ // reviewer.id string marshaling bug in go-github.
928+ func createRepoRuleset (ctx context.Context , client * github.Client , owner , repo string , ruleset github.RepositoryRuleset ) (* github.RepositoryRuleset , * github.Response , error ) {
929+ u := fmt .Sprintf ("repos/%v/%v/rulesets" , owner , repo )
930+ req , err := client .NewRequest ("POST" , u , ruleset )
931+ if err != nil {
932+ return nil , nil , err
933+ }
934+ return doRulesetRequest (ctx , client , req )
935+ }
936+
937+ // getRepoRuleset gets a repository ruleset, working around the
938+ // reviewer.id string marshaling bug in go-github.
939+ func getRepoRuleset (ctx context.Context , client * github.Client , owner , repo string , rulesetID int64 , includesParents bool ) (* github.RepositoryRuleset , * github.Response , error ) {
940+ u := fmt .Sprintf ("repos/%v/%v/rulesets/%v?includes_parents=%v" , owner , repo , rulesetID , includesParents )
941+ req , err := client .NewRequest ("GET" , u , nil )
942+ if err != nil {
943+ return nil , nil , err
944+ }
945+ return doRulesetRequest (ctx , client , req )
946+ }
947+
948+ // updateRepoRuleset updates a repository ruleset, working around the
949+ // reviewer.id string marshaling bug in go-github.
950+ func updateRepoRuleset (ctx context.Context , client * github.Client , owner , repo string , rulesetID int64 , ruleset github.RepositoryRuleset ) (* github.RepositoryRuleset , * github.Response , error ) {
951+ u := fmt .Sprintf ("repos/%v/%v/rulesets/%v" , owner , repo , rulesetID )
952+ req , err := client .NewRequest ("PUT" , u , ruleset )
953+ if err != nil {
954+ return nil , nil , err
955+ }
956+ return doRulesetRequest (ctx , client , req )
957+ }
958+
959+ // createOrgRuleset creates an organization ruleset, working around the
960+ // reviewer.id string marshaling bug in go-github.
961+ func createOrgRuleset (ctx context.Context , client * github.Client , org string , ruleset github.RepositoryRuleset ) (* github.RepositoryRuleset , * github.Response , error ) {
962+ u := fmt .Sprintf ("orgs/%v/rulesets" , org )
963+ req , err := client .NewRequest ("POST" , u , ruleset )
964+ if err != nil {
965+ return nil , nil , err
966+ }
967+ return doRulesetRequest (ctx , client , req )
968+ }
969+
970+ // getOrgRuleset gets an organization ruleset, working around the
971+ // reviewer.id string marshaling bug in go-github.
972+ func getOrgRuleset (ctx context.Context , client * github.Client , org string , rulesetID int64 ) (* github.RepositoryRuleset , * github.Response , error ) {
973+ u := fmt .Sprintf ("orgs/%v/rulesets/%v" , org , rulesetID )
974+ req , err := client .NewRequest ("GET" , u , nil )
975+ if err != nil {
976+ return nil , nil , err
977+ }
978+ return doRulesetRequest (ctx , client , req )
979+ }
980+
981+ // updateOrgRuleset updates an organization ruleset, working around the
982+ // reviewer.id string marshaling bug in go-github.
983+ func updateOrgRuleset (ctx context.Context , client * github.Client , org string , rulesetID int64 , ruleset github.RepositoryRuleset ) (* github.RepositoryRuleset , * github.Response , error ) {
984+ u := fmt .Sprintf ("orgs/%v/rulesets/%v" , org , rulesetID )
985+ req , err := client .NewRequest ("PUT" , u , ruleset )
986+ if err != nil {
987+ return nil , nil , err
988+ }
989+ return doRulesetRequest (ctx , client , req )
990+ }
991+
883992func bypassActorsDiffSuppressFunc (k , o , n string , d * schema.ResourceData ) bool {
884993 // If the length has changed, no need to suppress
885994 if k == "bypass_actors.#" {
0 commit comments