diff --git a/github/copilot.go b/github/copilot.go index 914c732420b..0c8a8b7aa4e 100644 --- a/github/copilot.go +++ b/github/copilot.go @@ -610,6 +610,108 @@ func (s *CopilotService) ListOrganizationCodingAgentRepositories(ctx context.Con return result, resp, nil } +// CopilotCodingAgentPermissions represents which repositories in an organization can use the Copilot coding agent. +type CopilotCodingAgentPermissions struct { + // EnabledRepositories is one of "all", "selected" or "none". + EnabledRepositories string `json:"enabled_repositories"` + SelectedRepositoriesURL *string `json:"selected_repositories_url,omitempty"` +} + +// CopilotCodingAgentPermissionsRequest represents the request body to set the Copilot coding agent permissions for an organization. +type CopilotCodingAgentPermissionsRequest struct { + // EnabledRepositories is one of "all", "selected" or "none". + EnabledRepositories string `json:"enabled_repositories"` +} + +// GetOrganizationCodingAgentPermissions gets which repositories in an organization can use the Copilot coding agent. +// +// GitHub API docs: https://docs.github.com/rest/copilot/copilot-coding-agent-management?apiVersion=2022-11-28#get-copilot-cloud-agent-permissions-for-an-organization +// +//meta:operation GET /orgs/{org}/copilot/coding-agent/permissions +func (s *CopilotService) GetOrganizationCodingAgentPermissions(ctx context.Context, org string) (*CopilotCodingAgentPermissions, *Response, error) { + u := fmt.Sprintf("orgs/%v/copilot/coding-agent/permissions", org) + + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + var permissions *CopilotCodingAgentPermissions + resp, err := s.client.Do(req, &permissions) + if err != nil { + return nil, resp, err + } + + return permissions, resp, nil +} + +// SetOrganizationCodingAgentPermissions sets which repositories in an organization can use the Copilot coding agent. +// +// GitHub API docs: https://docs.github.com/rest/copilot/copilot-coding-agent-management?apiVersion=2022-11-28#set-copilot-cloud-agent-permissions-for-an-organization +// +//meta:operation PUT /orgs/{org}/copilot/coding-agent/permissions +func (s *CopilotService) SetOrganizationCodingAgentPermissions(ctx context.Context, org string, body CopilotCodingAgentPermissionsRequest) (*Response, error) { + u := fmt.Sprintf("orgs/%v/copilot/coding-agent/permissions", org) + + req, err := s.client.NewRequest(ctx, "PUT", u, body) + if err != nil { + return nil, err + } + + return s.client.Do(req, nil) +} + +// SetOrganizationCodingAgentRepositories replaces the list of repositories enabled for the Copilot coding agent in an organization. +// The organization's EnabledRepositories permission must be set to "selected". +// +// GitHub API docs: https://docs.github.com/rest/copilot/copilot-coding-agent-management?apiVersion=2022-11-28#set-selected-repositories-for-copilot-cloud-agent-in-an-organization +// +//meta:operation PUT /orgs/{org}/copilot/coding-agent/permissions/repositories +func (s *CopilotService) SetOrganizationCodingAgentRepositories(ctx context.Context, org string, repositoryIDs []int64) (*Response, error) { + u := fmt.Sprintf("orgs/%v/copilot/coding-agent/permissions/repositories", org) + + req, err := s.client.NewRequest(ctx, "PUT", u, struct { + IDs []int64 `json:"selected_repository_ids"` + }{IDs: repositoryIDs}) + if err != nil { + return nil, err + } + + return s.client.Do(req, nil) +} + +// EnableOrganizationCodingAgentRepository enables a repository for the Copilot coding agent in an organization. +// +// GitHub API docs: https://docs.github.com/rest/copilot/copilot-coding-agent-management?apiVersion=2022-11-28#enable-a-repository-for-copilot-cloud-agent-in-an-organization +// +//meta:operation PUT /orgs/{org}/copilot/coding-agent/permissions/repositories/{repository_id} +func (s *CopilotService) EnableOrganizationCodingAgentRepository(ctx context.Context, org string, repositoryID int64) (*Response, error) { + u := fmt.Sprintf("orgs/%v/copilot/coding-agent/permissions/repositories/%v", org, repositoryID) + + req, err := s.client.NewRequest(ctx, "PUT", u, nil) + if err != nil { + return nil, err + } + + return s.client.Do(req, nil) +} + +// DisableOrganizationCodingAgentRepository disables a repository for the Copilot coding agent in an organization. +// +// GitHub API docs: https://docs.github.com/rest/copilot/copilot-coding-agent-management?apiVersion=2022-11-28#disable-a-repository-for-copilot-cloud-agent-in-an-organization +// +//meta:operation DELETE /orgs/{org}/copilot/coding-agent/permissions/repositories/{repository_id} +func (s *CopilotService) DisableOrganizationCodingAgentRepository(ctx context.Context, org string, repositoryID int64) (*Response, error) { + u := fmt.Sprintf("orgs/%v/copilot/coding-agent/permissions/repositories/%v", org, repositoryID) + + req, err := s.client.NewRequest(ctx, "DELETE", u, nil) + if err != nil { + return nil, err + } + + return s.client.Do(req, 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. diff --git a/github/copilot_test.go b/github/copilot_test.go index 996ed014a4d..f90d01878ee 100644 --- a/github/copilot_test.go +++ b/github/copilot_test.go @@ -1068,6 +1068,166 @@ func TestCopilotService_ListOrganizationCodingAgentRepositories(t *testing.T) { }) } +func TestCopilotService_GetOrganizationCodingAgentPermissions(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/orgs/o/copilot/coding-agent/permissions", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `{ + "enabled_repositories": "selected", + "selected_repositories_url": "https://api.github.com/orgs/o/copilot/coding-agent/permissions/repositories" + }`) + }) + + ctx := t.Context() + got, _, err := client.Copilot.GetOrganizationCodingAgentPermissions(ctx, "o") + if err != nil { + t.Errorf("Copilot.GetOrganizationCodingAgentPermissions returned error: %v", err) + } + + want := &CopilotCodingAgentPermissions{ + EnabledRepositories: "selected", + SelectedRepositoriesURL: new("https://api.github.com/orgs/o/copilot/coding-agent/permissions/repositories"), + } + if !cmp.Equal(got, want) { + t.Errorf("Copilot.GetOrganizationCodingAgentPermissions returned %+v, want %+v", got, want) + } + + const methodName = "GetOrganizationCodingAgentPermissions" + + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Copilot.GetOrganizationCodingAgentPermissions(ctx, "\n") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Copilot.GetOrganizationCodingAgentPermissions(ctx, "o") + if got != nil { + t.Errorf("Copilot.GetOrganizationCodingAgentPermissions returned %+v, want nil", got) + } + return resp, err + }) +} + +func TestCopilotService_SetOrganizationCodingAgentPermissions(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + input := CopilotCodingAgentPermissionsRequest{EnabledRepositories: "all"} + + mux.HandleFunc("/orgs/o/copilot/coding-agent/permissions", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "PUT") + testJSONBody(t, r, input) + w.WriteHeader(http.StatusNoContent) + }) + + ctx := t.Context() + _, err := client.Copilot.SetOrganizationCodingAgentPermissions(ctx, "o", input) + if err != nil { + t.Errorf("Copilot.SetOrganizationCodingAgentPermissions returned error: %v", err) + } + + const methodName = "SetOrganizationCodingAgentPermissions" + + testBadOptions(t, methodName, func() (err error) { + _, err = client.Copilot.SetOrganizationCodingAgentPermissions(ctx, "\n", input) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + return client.Copilot.SetOrganizationCodingAgentPermissions(ctx, "o", input) + }) +} + +func TestCopilotService_SetOrganizationCodingAgentRepositories(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + input := []int64{123, 1234} + + mux.HandleFunc("/orgs/o/copilot/coding-agent/permissions/repositories", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "PUT") + testJSONBody(t, r, struct { + IDs []int64 `json:"selected_repository_ids"` + }{ + IDs: input, + }) + w.WriteHeader(http.StatusNoContent) + }) + + ctx := t.Context() + _, err := client.Copilot.SetOrganizationCodingAgentRepositories(ctx, "o", input) + if err != nil { + t.Errorf("Copilot.SetOrganizationCodingAgentRepositories returned error: %v", err) + } + + const methodName = "SetOrganizationCodingAgentRepositories" + + testBadOptions(t, methodName, func() (err error) { + _, err = client.Copilot.SetOrganizationCodingAgentRepositories(ctx, "\n", input) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + return client.Copilot.SetOrganizationCodingAgentRepositories(ctx, "o", input) + }) +} + +func TestCopilotService_EnableOrganizationCodingAgentRepository(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/orgs/o/copilot/coding-agent/permissions/repositories/123", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "PUT") + w.WriteHeader(http.StatusNoContent) + }) + + ctx := t.Context() + _, err := client.Copilot.EnableOrganizationCodingAgentRepository(ctx, "o", 123) + if err != nil { + t.Errorf("Copilot.EnableOrganizationCodingAgentRepository returned error: %v", err) + } + + const methodName = "EnableOrganizationCodingAgentRepository" + + testBadOptions(t, methodName, func() (err error) { + _, err = client.Copilot.EnableOrganizationCodingAgentRepository(ctx, "\n", 123) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + return client.Copilot.EnableOrganizationCodingAgentRepository(ctx, "o", 123) + }) +} + +func TestCopilotService_DisableOrganizationCodingAgentRepository(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/orgs/o/copilot/coding-agent/permissions/repositories/123", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "DELETE") + w.WriteHeader(http.StatusNoContent) + }) + + ctx := t.Context() + _, err := client.Copilot.DisableOrganizationCodingAgentRepository(ctx, "o", 123) + if err != nil { + t.Errorf("Copilot.DisableOrganizationCodingAgentRepository returned error: %v", err) + } + + const methodName = "DisableOrganizationCodingAgentRepository" + + testBadOptions(t, methodName, func() (err error) { + _, err = client.Copilot.DisableOrganizationCodingAgentRepository(ctx, "\n", 123) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + return client.Copilot.DisableOrganizationCodingAgentRepository(ctx, "o", 123) + }) +} + func TestCopilotService_GetOrganizationContentExclusionDetails(t *testing.T) { t.Parallel() client, mux, _ := setup(t) diff --git a/github/github-accessors.go b/github/github-accessors.go index ab7e10a4888..cc4000d8986 100644 --- a/github/github-accessors.go +++ b/github/github-accessors.go @@ -9462,6 +9462,30 @@ func (c *CopilotCodeReviewRuleParameters) GetReviewOnPush() bool { return c.ReviewOnPush } +// GetEnabledRepositories returns the EnabledRepositories field. +func (c *CopilotCodingAgentPermissions) GetEnabledRepositories() string { + if c == nil { + return "" + } + return c.EnabledRepositories +} + +// GetSelectedRepositoriesURL returns the SelectedRepositoriesURL field if it's non-nil, zero value otherwise. +func (c *CopilotCodingAgentPermissions) GetSelectedRepositoriesURL() string { + if c == nil || c.SelectedRepositoriesURL == nil { + return "" + } + return *c.SelectedRepositoriesURL +} + +// GetEnabledRepositories returns the EnabledRepositories field. +func (c *CopilotCodingAgentPermissionsRequest) GetEnabledRepositories() string { + if c == nil { + return "" + } + return c.EnabledRepositories +} + // 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 8cba3028927..0795e66a36a 100644 --- a/github/github-accessors_test.go +++ b/github/github-accessors_test.go @@ -12013,6 +12013,33 @@ func TestCopilotCodeReviewRuleParameters_GetReviewOnPush(tt *testing.T) { c.GetReviewOnPush() } +func TestCopilotCodingAgentPermissions_GetEnabledRepositories(tt *testing.T) { + tt.Parallel() + c := &CopilotCodingAgentPermissions{} + c.GetEnabledRepositories() + c = nil + c.GetEnabledRepositories() +} + +func TestCopilotCodingAgentPermissions_GetSelectedRepositoriesURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &CopilotCodingAgentPermissions{SelectedRepositoriesURL: &zeroValue} + c.GetSelectedRepositoriesURL() + c = &CopilotCodingAgentPermissions{} + c.GetSelectedRepositoriesURL() + c = nil + c.GetSelectedRepositoriesURL() +} + +func TestCopilotCodingAgentPermissionsRequest_GetEnabledRepositories(tt *testing.T) { + tt.Parallel() + c := &CopilotCodingAgentPermissionsRequest{} + c.GetEnabledRepositories() + c = nil + c.GetEnabledRepositories() +} + func TestCopilotDailyMetrics_GetCodeAcceptanceActivityCount(tt *testing.T) { tt.Parallel() var zeroValue int