From 29128ca9de3854e169e37cf63a4420325ad83a73 Mon Sep 17 00:00:00 2001 From: Tens1de Date: Mon, 7 Sep 2026 11:35:26 +0300 Subject: [PATCH 1/2] feat: Add Copilot content exclusion set and enterprise endpoints Fixes #4526 --- github/copilot.go | 88 ++++++++++++++++++++-- github/copilot_test.go | 128 +++++++++++++++++++++++++++++++- github/github-accessors.go | 8 ++ github/github-accessors_test.go | 11 +++ 4 files changed, 228 insertions(+), 7 deletions(-) diff --git a/github/copilot.go b/github/copilot.go index 301a837b333..932622ad790 100644 --- a/github/copilot.go +++ b/github/copilot.go @@ -390,17 +390,27 @@ func (s *CopilotService) ListOrganizationCodingAgentRepositories(ctx context.Con return result, resp, nil } -// CopilotOrganizationContentExclusionDetails lists all Copilot content exclusion -// rules for an organization, keyed by repository full name. Each value is the -// list of file paths excluded from Copilot for that repository. -type CopilotOrganizationContentExclusionDetails map[string][]string +// CopilotContentExclusionDetails lists Copilot content exclusion path rules, +// keyed by repository identifier. Each value is the list of file paths excluded +// from Copilot for that repository. +type CopilotContentExclusionDetails map[string][]string + +// CopilotOrganizationContentExclusionDetails is an alias for organization-scoped +// content exclusion rules. Prefer CopilotContentExclusionDetails for new code. +type CopilotOrganizationContentExclusionDetails = CopilotContentExclusionDetails + +// CopilotContentExclusionUpdateResponse represents the response from setting +// Copilot content exclusion rules. +type CopilotContentExclusionUpdateResponse struct { + Message *string `json:"message,omitempty"` +} // GetOrganizationContentExclusionDetails gets the Copilot content exclusion rules for an organization. // // GitHub API docs: https://docs.github.com/rest/copilot/copilot-content-exclusion-management?apiVersion=2022-11-28#get-copilot-content-exclusion-rules-for-an-organization // //meta:operation GET /orgs/{org}/copilot/content_exclusion -func (s *CopilotService) GetOrganizationContentExclusionDetails(ctx context.Context, org string) (CopilotOrganizationContentExclusionDetails, *Response, error) { +func (s *CopilotService) GetOrganizationContentExclusionDetails(ctx context.Context, org string) (CopilotContentExclusionDetails, *Response, error) { u := fmt.Sprintf("orgs/%v/copilot/content_exclusion", org) req, err := s.client.NewRequest(ctx, "GET", u, nil) @@ -408,7 +418,51 @@ func (s *CopilotService) GetOrganizationContentExclusionDetails(ctx context.Cont return nil, nil, err } - details := CopilotOrganizationContentExclusionDetails{} + details := CopilotContentExclusionDetails{} + resp, err := s.client.Do(req, &details) + if err != nil { + return nil, resp, err + } + + return details, resp, nil +} + +// SetOrganizationContentExclusionDetails sets Copilot content exclusion path rules for an organization. +// +// GitHub API docs: https://docs.github.com/rest/copilot/copilot-content-exclusion-management?apiVersion=2022-11-28#set-copilot-content-exclusion-rules-for-an-organization +// +//meta:operation PUT /orgs/{org}/copilot/content_exclusion +func (s *CopilotService) SetOrganizationContentExclusionDetails(ctx context.Context, org string, body CopilotContentExclusionDetails) (*CopilotContentExclusionUpdateResponse, *Response, error) { + u := fmt.Sprintf("orgs/%v/copilot/content_exclusion", org) + + req, err := s.client.NewRequest(ctx, "PUT", u, body) + if err != nil { + return nil, nil, err + } + + var result *CopilotContentExclusionUpdateResponse + resp, err := s.client.Do(req, &result) + if err != nil { + return nil, resp, err + } + + return result, resp, nil +} + +// GetEnterpriseContentExclusionDetails gets the Copilot content exclusion rules for an enterprise. +// +// GitHub API docs: https://docs.github.com/enterprise-cloud@latest/rest/copilot/copilot-content-exclusion-management?apiVersion=2022-11-28#get-copilot-content-exclusion-rules-for-an-enterprise +// +//meta:operation GET /enterprises/{enterprise}/copilot/content_exclusion +func (s *CopilotService) GetEnterpriseContentExclusionDetails(ctx context.Context, enterprise string) (CopilotContentExclusionDetails, *Response, error) { + u := fmt.Sprintf("enterprises/%v/copilot/content_exclusion", enterprise) + + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + details := CopilotContentExclusionDetails{} resp, err := s.client.Do(req, &details) if err != nil { return nil, resp, err @@ -417,6 +471,28 @@ func (s *CopilotService) GetOrganizationContentExclusionDetails(ctx context.Cont return details, resp, nil } +// SetEnterpriseContentExclusionDetails sets Copilot content exclusion path rules for an enterprise. +// +// GitHub API docs: https://docs.github.com/enterprise-cloud@latest/rest/copilot/copilot-content-exclusion-management?apiVersion=2022-11-28#set-copilot-content-exclusion-rules-for-an-enterprise +// +//meta:operation PUT /enterprises/{enterprise}/copilot/content_exclusion +func (s *CopilotService) SetEnterpriseContentExclusionDetails(ctx context.Context, enterprise string, body CopilotContentExclusionDetails) (*CopilotContentExclusionUpdateResponse, *Response, error) { + u := fmt.Sprintf("enterprises/%v/copilot/content_exclusion", enterprise) + + req, err := s.client.NewRequest(ctx, "PUT", u, body) + if err != nil { + return nil, nil, err + } + + var result *CopilotContentExclusionUpdateResponse + resp, err := s.client.Do(req, &result) + if err != nil { + return nil, resp, err + } + + return result, resp, nil +} + // AddCopilotTeams adds teams to the Copilot for Business subscription for an organization. // // GitHub API docs: https://docs.github.com/rest/copilot/copilot-user-management?apiVersion=2022-11-28#add-teams-to-the-copilot-subscription-for-an-organization diff --git a/github/copilot_test.go b/github/copilot_test.go index b04b406d24d..e0fa9c5b6c3 100644 --- a/github/copilot_test.go +++ b/github/copilot_test.go @@ -845,7 +845,7 @@ func TestCopilotService_GetOrganizationContentExclusionDetails(t *testing.T) { t.Errorf("Copilot.GetOrganizationContentExclusionDetails returned error: %v", err) } - want := CopilotOrganizationContentExclusionDetails{ + want := CopilotContentExclusionDetails{ "octo-repo": {"/src/some-dir/kernel.rs"}, "octo-repo-2": {"/docs/secret.md", "**/*.env"}, } @@ -869,6 +869,132 @@ func TestCopilotService_GetOrganizationContentExclusionDetails(t *testing.T) { }) } +func TestCopilotService_SetOrganizationContentExclusionDetails(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + input := CopilotContentExclusionDetails{ + "octo-repo": {"/src/some-dir/kernel.rs"}, + } + + mux.HandleFunc("/orgs/o/copilot/content_exclusion", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "PUT") + testJSONBody(t, r, input) + fmt.Fprint(w, `{"message": "Content exclusion rules updated"}`) + }) + + ctx := t.Context() + got, _, err := client.Copilot.SetOrganizationContentExclusionDetails(ctx, "o", input) + if err != nil { + t.Errorf("Copilot.SetOrganizationContentExclusionDetails returned error: %v", err) + } + + want := &CopilotContentExclusionUpdateResponse{ + Message: new("Content exclusion rules updated"), + } + if !cmp.Equal(got, want) { + t.Errorf("Copilot.SetOrganizationContentExclusionDetails returned %+v, want %+v", got, want) + } + + const methodName = "SetOrganizationContentExclusionDetails" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Copilot.SetOrganizationContentExclusionDetails(ctx, "\n", input) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Copilot.SetOrganizationContentExclusionDetails(ctx, "o", input) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestCopilotService_GetEnterpriseContentExclusionDetails(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/enterprises/e/copilot/content_exclusion", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `{ + "git@github.com:*/copilot": ["/__tests__/**"], + "octo-org/octo-repo": ["/src/some-dir/kernel.rs"] + }`) + }) + + ctx := t.Context() + got, _, err := client.Copilot.GetEnterpriseContentExclusionDetails(ctx, "e") + if err != nil { + t.Errorf("Copilot.GetEnterpriseContentExclusionDetails returned error: %v", err) + } + + want := CopilotContentExclusionDetails{ + "git@github.com:*/copilot": {"/__tests__/**"}, + "octo-org/octo-repo": {"/src/some-dir/kernel.rs"}, + } + if !cmp.Equal(got, want) { + t.Errorf("Copilot.GetEnterpriseContentExclusionDetails returned %+v, want %+v", got, want) + } + + const methodName = "GetEnterpriseContentExclusionDetails" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Copilot.GetEnterpriseContentExclusionDetails(ctx, "\n") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Copilot.GetEnterpriseContentExclusionDetails(ctx, "e") + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestCopilotService_SetEnterpriseContentExclusionDetails(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + input := CopilotContentExclusionDetails{ + "git@github.com:*/copilot": {"/__tests__/**"}, + "octo-org/octo-repo": {"/src/some-dir/kernel.rs"}, + } + + mux.HandleFunc("/enterprises/e/copilot/content_exclusion", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "PUT") + testJSONBody(t, r, input) + fmt.Fprint(w, `{"message": "Content exclusion rules updated"}`) + }) + + ctx := t.Context() + got, _, err := client.Copilot.SetEnterpriseContentExclusionDetails(ctx, "e", input) + if err != nil { + t.Errorf("Copilot.SetEnterpriseContentExclusionDetails returned error: %v", err) + } + + want := &CopilotContentExclusionUpdateResponse{ + Message: new("Content exclusion rules updated"), + } + if !cmp.Equal(got, want) { + t.Errorf("Copilot.SetEnterpriseContentExclusionDetails returned %+v, want %+v", got, want) + } + + const methodName = "SetEnterpriseContentExclusionDetails" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Copilot.SetEnterpriseContentExclusionDetails(ctx, "\n", input) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Copilot.SetEnterpriseContentExclusionDetails(ctx, "e", input) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + func TestCopilotService_AddCopilotTeams(t *testing.T) { t.Parallel() client, mux, _ := setup(t) diff --git a/github/github-accessors.go b/github/github-accessors.go index b9ce167b413..eae98b0326b 100644 --- a/github/github-accessors.go +++ b/github/github-accessors.go @@ -9094,6 +9094,14 @@ func (c *CopilotCodeReviewRuleParameters) GetReviewOnPush() bool { return c.ReviewOnPush } +// GetMessage returns the Message field if it's non-nil, zero value otherwise. +func (c *CopilotContentExclusionUpdateResponse) GetMessage() string { + if c == nil || c.Message == nil { + return "" + } + return *c.Message +} + // GetCodeAcceptanceActivityCount returns the CodeAcceptanceActivityCount field if it's non-nil, zero value otherwise. func (c *CopilotDailyMetrics) GetCodeAcceptanceActivityCount() int { if c == nil || c.CodeAcceptanceActivityCount == nil { diff --git a/github/github-accessors_test.go b/github/github-accessors_test.go index bc822744add..3ed26528614 100644 --- a/github/github-accessors_test.go +++ b/github/github-accessors_test.go @@ -11600,6 +11600,17 @@ func TestCopilotCodeReviewRuleParameters_GetReviewOnPush(tt *testing.T) { c.GetReviewOnPush() } +func TestCopilotContentExclusionUpdateResponse_GetMessage(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &CopilotContentExclusionUpdateResponse{Message: &zeroValue} + c.GetMessage() + c = &CopilotContentExclusionUpdateResponse{} + c.GetMessage() + c = nil + c.GetMessage() +} + func TestCopilotDailyMetrics_GetCodeAcceptanceActivityCount(tt *testing.T) { tt.Parallel() var zeroValue int From 2da94fdb923edb536f72e08167daa1c8c1e7838c Mon Sep 17 00:00:00 2001 From: Tens1de Date: Tue, 8 Sep 2026 00:12:28 +0300 Subject: [PATCH 2/2] feat!: drop content exclusion type alias Remove CopilotOrganizationContentExclusionDetails alias and keep a single CopilotContentExclusionDetails type, as requested in review. --- github/copilot.go | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/github/copilot.go b/github/copilot.go index 932622ad790..44e2a3e20b7 100644 --- a/github/copilot.go +++ b/github/copilot.go @@ -393,12 +393,10 @@ func (s *CopilotService) ListOrganizationCodingAgentRepositories(ctx context.Con // CopilotContentExclusionDetails lists Copilot content exclusion path rules, // keyed by repository identifier. Each value is the list of file paths excluded // from Copilot for that repository. +// +// NOTE Breaking API change: renamed from CopilotOrganizationContentExclusionDetails. type CopilotContentExclusionDetails map[string][]string -// CopilotOrganizationContentExclusionDetails is an alias for organization-scoped -// content exclusion rules. Prefer CopilotContentExclusionDetails for new code. -type CopilotOrganizationContentExclusionDetails = CopilotContentExclusionDetails - // CopilotContentExclusionUpdateResponse represents the response from setting // Copilot content exclusion rules. type CopilotContentExclusionUpdateResponse struct {